【问题标题】:optimizing object creation with reflection使用反射优化对象创建
【发布时间】:2014-04-07 08:30:19
【问题描述】:

我正在尝试优化我们遗留代码中使用反射来创建各种视图的类的性能。我宁愿我们根本不使用反射,但在短期内移除它不是一种选择。代码来自 MVC# 框架。这里是:

public class CreateHelper
{
    #region Documentation
    /// <summary>
    /// Creates an object of specified type.
    /// </summary>
    #endregion
    public static object Create(Type t)
    {
        return t.GetConstructor(new Type[] { }).Invoke(new object[] { });
    }

    #region Documentation
    /// <summary>
    /// Creates an object of specified type with parameters passed to the constructor.
    /// </summary>
    #endregion
    public static object Create(Type t, params object[] parameters)
    {
        Type[] paramTypes = new Type[parameters.Length];
        for (int i = 0; i < parameters.Length; i++)
            paramTypes[i] = parameters[i].GetType();
        return t.GetConstructor(paramTypes).Invoke(parameters);
    }
}

我希望尽快实现这两种方法。我在对象创建优化上阅读了此great article by Ayende 并尝试为我的目的修改他的示例,但是我对 IL 的了解不存在。

我得到一个VerificationException 操作可能会破坏运行时的稳定性。Create 方法中。有谁知道是什么问题?我可以使用这种方法的更快实现吗?这是我的尝试:

public class Created
{
    public int Num;
    public string Name;

    public Created()
    {
    }

    public Created(int num, string name)
    {
        this.Num = num;
        this.Name = name;
    }
}

public class CreateHelper
{
    private delegate object CreateCtor();
    private static CreateCtor createdCtorDelegate;

    #region Documentation
    /// <summary>
    /// Creates an object of specified type.
    /// </summary>
    #endregion
    public static object Create(Type t)
    {
        var ctor = t.GetConstructor(new Type[] { });

        var method = new DynamicMethod("CreateIntance", t, new Type[] { typeof(object[]) });
        var gen = method.GetILGenerator();
        gen.Emit(OpCodes.Ldarg_0);//arr
        gen.Emit(OpCodes.Call, ctor);// new Created
        gen.Emit(OpCodes.Ret);
        createdCtorDelegate = (CreateCtor)method.CreateDelegate(typeof(CreateCtor));
        return createdCtorDelegate(); // <=== VerificationException Operation could destabilize the runtime.
    }

    #region Documentation
    /// <summary>
    /// Creates an object of specified type with parameters passed to the constructor.
    /// </summary>
    #endregion
    public static object Create(Type t, params object[] parameters)
    {
        Type[] paramTypes = new Type[parameters.Length];
        for (int i = 0; i < parameters.Length; i++)
            paramTypes[i] = parameters[i].GetType();
        return t.GetConstructor(paramTypes).Invoke(parameters);
    }
}

然后我像这样使用这个类:

class Program
{
    private static Created CreateInstance()
    {
        return (Created)CreateHelper.Create(typeof(Created));
        //return new Created();
    }

    static void Main(string[] args)
    {
        int iterations = 1000000;
        Stopwatch watch = Stopwatch.StartNew();
        for (int i = 0; i < iterations; i++)
        {
            CreateInstance();
        }
        Console.WriteLine(watch.Elapsed);

        Console.ReadLine();
    }
}

更新 1

我做了一些计时:

  • new Created() : 00:00:00.0225015
  • Activator.CreateInstance&lt;Created&gt;() : 00:00:00.1232143
  • (Created)CreateHelper.Create(typeof(Created)) : 00:00:00.3946555

  • new Created(i, i.ToString()) : 00:00:00.1476882

  • (Created)Activator.CreateInstance(typeof(Created), new object[]{ i, i.ToString() }) : 00:00:01.6342624
  • (Created)CreateHelper.Create(typeof(Created), new object[] {i, i.ToString()}) : 00:00:01.1591511

更新 2

对于默认构造函数的情况,@Brannon 建议的解决方案有效,但是获得的时间是 00:00:00.1165000,这并不是很大的改进。这里是:

public class CreateHelper
{
    private delegate object DefaultConstructor();

    private static readonly ConcurrentDictionary<Type, DefaultConstructor> DefaultConstructors = new ConcurrentDictionary<Type, DefaultConstructor>();

    #region Documentation
    /// <summary>
    /// Creates an object of specified type.
    /// </summary>
    #endregion
    public static object Create(Type t)
    {
        DefaultConstructor defaultConstructorDelegate;

        if (!DefaultConstructors.TryGetValue(t, out defaultConstructorDelegate))
        {
            var ctor = t.GetConstructor(Type.EmptyTypes);

            var method = new DynamicMethod("CreateIntance", t, Type.EmptyTypes);
            var gen = method.GetILGenerator();
            gen.Emit(OpCodes.Nop);
            gen.Emit(OpCodes.Newobj, ctor);
            gen.Emit(OpCodes.Ret);
            defaultConstructorDelegate = (DefaultConstructor)method.CreateDelegate(typeof(DefaultConstructor));
            DefaultConstructors[t] = defaultConstructorDelegate;
        }

        return defaultConstructorDelegate.Invoke();
    }
}

更新 3

使用 Expression.New 的编译表达式也产生了非常好的结果 (00:00:00.1166022)。代码如下:

public class CreateHelper
{        
    private static readonly ConcurrentDictionary<Type, Func<object>> DefaultConstructors = new ConcurrentDictionary<Type, Func<object>>();

    #region Documentation
    /// <summary>
    /// Creates an object of specified type.
    /// </summary>
    #endregion
    public static object Create(Type t)
    {
        Func<object> defaultConstructor;

        if (!DefaultConstructors.TryGetValue(t, out defaultConstructor))
        {
            var ctor = t.GetConstructor(Type.EmptyTypes);

            if (ctor == null)
            {
                throw new ArgumentException("Unsupported constructor for type " + t);
            }

            var constructorExpression = Expression.New(ctor);
            var lambda = Expression.Lambda<Func<Created>>(constructorExpression);
            defaultConstructor = lambda.Compile();
            DefaultConstructors[t] = defaultConstructor;
        }

        return defaultConstructor.Invoke();
    }

    #region Documentation
    /// <summary>
    /// Creates an object of specified type with parameters passed to the constructor.
    /// </summary>
    #endregion
    public static object Create(Type t, params object[] parameters)
    {
        return null;
    }
}

总结

对于默认构造函数的情况,总结如下:

  • (Created)CreateHelper.Create(typeof(Created)) : 00:00:00.3946555
  • new Created() : 00:00:00.0225015
  • Activator.CreateInstance&lt;Created&gt;() : 00:00:00.1232143
  • DynamicMethod : 00:00:00.1165000
  • Expression.New : 00:00:00.1131143

【问题讨论】:

标签: c# optimization reflection


【解决方案1】:

首先,您可能应该为该特定测试禁用垃圾收集器。它可能会妨碍您的结果。或者您可以将所有创建的实例放入一个数组中。我不确定调用 ToString() 作为其中的一部分是否有帮助。

您对花哨的构造函数代码的计划不一定是正确的计划。构造函数查找本身非常慢。您应该使用 Type 键将委托缓存在字典中。大多数 IoC 容器会自动为您执行此操作(缓存和构造)。我认为在您的情况下使用其中一种会很有价值。事实上,较新的 JSON 框架还缓存构造函数信息以快速创建对象。也许像 Json.Net 或 ServiceStack.Text 这样的东西会有所帮助。

您正在构建多少种不同的类型? (我知道你的例子只显示了一个。)

我不确定您在 DynamicMethod 上的参数是否正确。这段代码(如下)对我来说是稳定的。不要在值类型或数组上调用它。

DynamicMethod dm = new DynamicMethod("MyCtor", type, Type.EmptyTypes, typeof(ClassFactory).Module, true);
ILGenerator ilgen = dm.GetILGenerator();
ilgen.Emit(OpCodes.Nop);
ilgen.Emit(OpCodes.Newobj, ci);
ilgen.Emit(OpCodes.Ret);
CtorDelegate del = ((CtorDelegate) dm.CreateDelegate(typeof(CtorDelegate)));
return del.Invoke(); // could cache del in a dictionary

【讨论】:

  • +1 感谢您的回答,我将尝试您的代码并将构造函数委托存储在字典中。我们可能正在创建大约 50 种不同的类型。
  • 您发布的代码运行良好,谢谢!您是否知道如何为非默认构造函数生成 IL?
  • 我不知道其他构造函数的 IL。对于他们,您可以尝试缓存已编译的表达式。见这里:rogeralsing.com/2008/02/28/linq-expressions-creating-objects
猜你喜欢
  • 1970-01-01
  • 2012-05-15
  • 2021-10-14
  • 2022-11-01
  • 2010-12-23
  • 2015-07-30
  • 2023-03-19
  • 1970-01-01
相关资源
最近更新 更多