많은 사람들이 리플렉션을 사용하면 성능 문제가 발생한다고 말합니다. 직접 호출하는 것보다 얼마나 느려질까요? 아래에서 테스트해 보겠습니다.
직접 호출과 반사 호출의 성능 차이를 확인하기 위해 데모를 작성해 보겠습니다. 코드는 다음과 같습니다.
1 namespace ConsoleApplication7 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 //比较直接调用和反射调用的性能差异 8 //7ms vs 365ms 9 int times = 1000000;10 var program = new Program();11 CodeTimerHelper.Initialize();12 13 CodeTimerHelper.Time("直接调用", times, () =>14 {15 program.Call();16 });17 18 var t = typeof(Program);19 var obj = Activator.CreateInstance(t);20 CodeTimerHelper.Time("反射调用", times, () =>21 {22 t.InvokeMember("Call", BindingFlags.InvokeMethod, null, obj, null);23 });24 25 Console.ReadKey();26 }27 28 /// <summary>29 /// 测试方法30 /// </summary>31 public void Call()32 {33 }34 35 }36 }
테스트 결과:
100만 호출에서 결과는 많은 사람들이 말했듯이 둘 사이의 성능에 큰 차이가 있음을 보여줍니다.
반영에 성능 손실이 있는 이유는 무엇인가요?
1. 리플렉션은 어셈블리 및 메타데이터를 기반으로 합니다. 메타데이터는 문자열을 기반으로 하며 미리 컴파일할 수 없으므로 이러한 일련의 작업은 성능에 영향을 미칩니다.
2. 대량의 복싱과 언박싱도 성능에 영향을 미칩니다. 우리는 대상 유형을 모르고 메소드에 전달된 매개변수가 일반적으로 객체 유형이므로 박싱과 언박싱이 많이 발생합니다.
반사 성능 최적화 솔루션
여기에서는 Lao Zhao가 공개한 System.Linq.Expressions.Expression
1 //3,基于表达式树2 var methodInfo = t.GetMethod("Call");3 var executor = new DynamicMethodExecutor(methodInfo);4 CodeTimerHelper.Time("Dynamic executor", times, () =>5 {6 executor.Execute(obj, null);7 });
테스트 결과:
와우, 동일한 100만 호출에도 DynamicMethodExecutor를 사용한 호출 성능은 직접 호출 성능과 거의 같습니다.
첨부된 것은 DynamicMethodExecutor의 캡슐화 코드입니다:
1 /// <summary> 2 /// 3 /// </summary> 4 public class DynamicMethodExecutor 5 { 6 private Func<object, object[], object> m_execute; 7 8 public DynamicMethodExecutor(MethodInfo methodInfo) 9 {10 this.m_execute = this.GetExecuteDelegate(methodInfo);11 }12 13 public object Execute(object instance, object[] parameters)14 {15 return this.m_execute(instance, parameters);16 }17 18 private Func<object, object[], object> GetExecuteDelegate(MethodInfo methodInfo)19 {20 // parameters to execute21 ParameterExpression instanceParameter = Expression.Parameter(typeof(object), "instance");22 ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters");23 24 // build parameter list25 List<Expression> parameterExpressions = new List<Expression>();26 ParameterInfo[] paramInfos = methodInfo.GetParameters();27 for (int i = 0; i < paramInfos.Length; i++)28 {29 // (Ti)parameters[i]30 BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));31 UnaryExpression valueCast = Expression.Convert(valueObj, paramInfos[i].ParameterType);32 parameterExpressions.Add(valueCast);33 }34 35 // non-instance for static method, or ((TInstance)instance)36 Expression instanceCast = methodInfo.IsStatic ? null : Expression.Convert(instanceParameter, methodInfo.ReflectedType);37 38 // static invoke or ((TInstance)instance).Method39 MethodCallExpression methodCall = Expression.Call(instanceCast, methodInfo, parameterExpressions);40 41 // ((TInstance)instance).Method((T0)parameters[0], (T1)parameters[1], ...)42 if (methodCall.Type == typeof(void))43 {44 Expression<Action<object, object[]>> lambda = Expression.Lambda<Action<object, object[]>>(methodCall, instanceParameter, parametersParameter);45 Action<object, object[]> execute = lambda.Compile();46 return (instance, parameters) =>47 {48 execute(instance, parameters);49 return null;50 };51 }52 else53 {54 UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));55 Expression<Func<object, object[], object>> lambda = Expression.Lambda<Func<object, object[], object>>(castMethodCall, instanceParameter, parametersParameter);56 return lambda.Compile();57 }58 }
linq의 표현식 트리를 사용하여 Delegate를 생성하는 방법 외에도 CodeDom과 같은 방법으로 코드를 생성하고 동적으로 컴파일하거나 Emit를 사용하여 Reflection 성능을 향상시키기 위해 IL을 직접 작성하지만 상대적으로 말하면 위의 방법이 가장 간단합니다.
이제 전체 리플렉션 요약이 완성되었습니다!
참고자료
C# 기본 지식 복습 시리즈 15: 리플렉션
2. 반성이란 무엇인가, 반성은 무엇을 할 수 있는가
위 내용은 직접 통화 및 반사 통화 예시 튜토리얼의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!