【问题标题】:Activator.CreateInstance Performance AlternativeActivator.CreateInstance 性能替代方案
【发布时间】:2011-05-24 20:09:00
【问题描述】:

我正在使用 RedGate 进行一些性能评估。我注意到使用Activator.CreateInstance(带有两个构造函数参数)动态创建一个实例需要相当长的时间......是否有更好的替代方案仍然使用反射方法(不是显式实例化)?

【问题讨论】:

  • 我为默认实例提供了答案here。

标签: c# .net performance reflection


【解决方案1】:

【讨论】:

  • +1。如果这仍然不够快,那么您可以尝试反射发射,但这不太可能对已编译的 lambda 产生太大改进,而且代码要复杂得多。
  • Compile() 操作是慢了还是快了?我假设性能是在不包括编译时间的情况下计算的?在需要多个不同类型的实例以及在运行时确定对象类型的情况下,每次编译操作会比Activator.CreateInstance慢很多吗?
  • @47d_ 我相信你是对的。我刚刚做了一些测试,虽然我可能犯了可怕的错误,但现在看起来编译会占用大部分资源。甚至超过 Activator。
  • @user1561358 lambda 的初始编译时间将比很多 Activator.CreateInstance() 调用慢得多,但您需要在整个过程中保持并重用相同的 lambda 以查看任何有用的性能提升并且仅当您调用该 lambda 数百次时。与往常一样,在需要的地方进行优化。不频繁的呼叫不会从中受益。如果有人构建了一个可以根据调用量自动优化的库,那就太好了,但这可能会减慢分析速度。赢不了。 8-)
【解决方案2】:

别忘了DynamicMethod

这里是如何通过默认构造函数创建新实例的示例

public static ObjectActivator CreateCtor(Type type)
{
    if (type == null)
    {
        throw new NullReferenceException("type");
    }
    ConstructorInfo emptyConstructor = type.GetConstructor(Type.EmptyTypes);
    var dynamicMethod = new DynamicMethod("CreateInstance", type, Type.EmptyTypes, true);
    ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
    ilGenerator.Emit(OpCodes.Nop);
    ilGenerator.Emit(OpCodes.Newobj, emptyConstructor);
    ilGenerator.Emit(OpCodes.Ret);
    return (ObjectActivator)dynamicMethod.CreateDelegate(typeof(ObjectActivator));
}

public delegate object ObjectActivator();

这里有更多关于performance comparison的信息

正在测量 InvokeMember...在 1.5643784 秒内进行 1000000 次迭代。

正在测量 MethodInfo.Invoke...在 0.8150111 秒内进行 1000000 次迭代。

测量 DynamicMethod... 1000000 次迭代在 0.0330202 秒内。

正在测量直接调用...在 0.0136752 秒内进行了 1000000 次迭代。

【讨论】:

    【解决方案3】:

    我创建了一个解决方案,可以用作Activator.CreateInstance 的替代品。你可以在我的blog找到它。

    示例:

    var myInstance = InstanceFactory.CreateInstance(typeof(MyClass));
    var myArray1 = InstanceFactory.CreateInstance(typeof(int[]), 1024);
    var myArray2 = InstanceFactory.CreateInstance(typeof(int[]), new object[] { 1024 });
    

    代码:

    public static class InstanceFactory
    {
      private delegate object CreateDelegate(Type type, object arg1, object arg2, object arg3);
    
      private static ConcurrentDictionary<Tuple<Type, Type, Type, Type>, CreateDelegate> cachedFuncs = new ConcurrentDictionary<Tuple<Type, Type, Type, Type>, CreateDelegate>();
    
      public static object CreateInstance(Type type)
      {
        return InstanceFactoryGeneric<TypeToIgnore, TypeToIgnore, TypeToIgnore>.CreateInstance(type, null, null, null);
      }
    
      public static object CreateInstance<TArg1>(Type type, TArg1 arg1)
      {
        return InstanceFactoryGeneric<TArg1, TypeToIgnore, TypeToIgnore>.CreateInstance(type, arg1, null, null);
      }
    
      public static object CreateInstance<TArg1, TArg2>(Type type, TArg1 arg1, TArg2 arg2)
      {
        return InstanceFactoryGeneric<TArg1, TArg2, TypeToIgnore>.CreateInstance(type, arg1, arg2, null);
      }
    
      public static object CreateInstance<TArg1, TArg2, TArg3>(Type type, TArg1 arg1, TArg2 arg2, TArg3 arg3)
      {
        return InstanceFactoryGeneric<TArg1, TArg2, TArg3>.CreateInstance(type, arg1, arg2, arg3);
      }
    
      public static object CreateInstance(Type type, params object[] args)
      {
        if (args == null)
          return CreateInstance(type);
    
        if (args.Length > 3 || 
          (args.Length > 0 && args[0] == null) ||
          (args.Length > 1 && args[1] == null) ||
          (args.Length > 2 && args[2] == null))
        {
            return Activator.CreateInstance(type, args);   
        }
    
        var arg0 = args.Length > 0 ? args[0] : null;
        var arg1 = args.Length > 1 ? args[1] : null;
        var arg2 = args.Length > 2 ? args[2] : null;
    
        var key = Tuple.Create(
          type,
          arg0?.GetType() ?? typeof(TypeToIgnore),
          arg1?.GetType() ?? typeof(TypeToIgnore),
          arg2?.GetType() ?? typeof(TypeToIgnore));
    
        if (cachedFuncs.TryGetValue(key, out CreateDelegate func))
          return func(type, arg0, arg1, arg2);
        else
          return CacheFunc(key)(type, arg0, arg1, arg2);
      }
    
      private static CreateDelegate CacheFunc(Tuple<Type, Type, Type, Type> key)
      {
        var types = new Type[] { key.Item1, key.Item2, key.Item3, key.Item4 };
        var method = typeof(InstanceFactory).GetMethods()
                                            .Where(m => m.Name == "CreateInstance")
                                            .Where(m => m.GetParameters().Count() == 4).Single();
        var generic = method.MakeGenericMethod(new Type[] { key.Item2, key.Item3, key.Item4 });
    
        var paramExpr = new List<ParameterExpression>();
        paramExpr.Add(Expression.Parameter(typeof(Type)));
        for (int i = 0; i < 3; i++)
          paramExpr.Add(Expression.Parameter(typeof(object)));
    
        var callParamExpr = new List<Expression>();
        callParamExpr.Add(paramExpr[0]);
        for (int i = 1; i < 4; i++)
          callParamExpr.Add(Expression.Convert(paramExpr[i], types[i]));
    
        var callExpr = Expression.Call(generic, callParamExpr);
        var lambdaExpr = Expression.Lambda<CreateDelegate>(callExpr, paramExpr);
        var func = lambdaExpr.Compile();
        cachedFuncs.TryAdd(key, func);
        return func;
      }
    }
    
    public static class InstanceFactoryGeneric<TArg1, TArg2, TArg3>
    {
      private static ConcurrentDictionary<Type, Func<TArg1, TArg2, TArg3, object>> cachedFuncs = new ConcurrentDictionary<Type, Func<TArg1, TArg2, TArg3, object>>();
    
      public static object CreateInstance(Type type, TArg1 arg1, TArg2 arg2, TArg3 arg3)
      {
        if (cachedFuncs.TryGetValue(type, out Func<TArg1, TArg2, TArg3, object> func))
          return func(arg1, arg2, arg3);
        else
          return CacheFunc(type, arg1, arg2, arg3)(arg1, arg2, arg3);
      }
    
      private static Func<TArg1, TArg2, TArg3, object> CacheFunc(Type type, TArg1 arg1, TArg2 arg2, TArg3 arg3)
      {
        var constructorTypes = new List<Type>();
        if (typeof(TArg1) != typeof(TypeToIgnore))
          constructorTypes.Add(typeof(TArg1));
        if (typeof(TArg2) != typeof(TypeToIgnore))
          constructorTypes.Add(typeof(TArg2));
        if (typeof(TArg3) != typeof(TypeToIgnore))
          constructorTypes.Add(typeof(TArg3));
    
        var parameters = new List<ParameterExpression>()
        {
          Expression.Parameter(typeof(TArg1)),
          Expression.Parameter(typeof(TArg2)),
          Expression.Parameter(typeof(TArg3)),
        };
    
        var constructor = type.GetConstructor(constructorTypes.ToArray());
        var constructorParameters = parameters.Take(constructorTypes.Count).ToList();
        var newExpr = Expression.New(constructor, constructorParameters);
        var lambdaExpr = Expression.Lambda<Func<TArg1, TArg2, TArg3, object>>(newExpr, parameters);
        var func = lambdaExpr.Compile();
        cachedFuncs.TryAdd(type, func);
        return func;
      }
    }
    
    public class TypeToIgnore
    {
    }
    

    【讨论】:

    • 谢谢!我不得不稍微改变一下以使用字符串而不是字典键的类型。但效果很好。
    • 谢谢,非常好,清晰的代码 - 使用这个缓存的好主意。
    猜你喜欢
    • 1970-01-01
    • 2011-12-07
    • 1970-01-01
    • 1970-01-01
    • 2019-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多