用反射优化对象创建

时间:2014-04-02 03:45:04

标签: c# optimization reflection

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

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<Created>():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<Created>():00:00:00.1232143
  • DynamicMethod:00:00:00.1165000
  • Expression.New:00:00:00.1131143

1 个答案:

答案 0 :(得分: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
相关问题