如何加快实例化大量对象?

时间:2009-07-14 21:12:46

标签: c# performance

对于任何大小的表格,下面的代码都非常慢。 (100,1000等...)罪魁祸首是使用new T()实例化我的对象。请注意,这不是我最终确定的代码,我只是将其中的部分内容删除,以便更容易地进行分析。一旦我将代码重构成形状,实例化和初始化就会一起发生。

有没有办法加快速度?我可能会忘记一些非常简单的事情,或者我可能会被剔除。希望是前者。

public static IList<T> ToList<T>(this DataTable table) where T : Model, new()
{
    T[] entities = new T[table.Rows.Count];

    // THIS LOOP IS VERY VERY SLOW
    for (int i = 0; i < table.Rows.Count; i++)
        entities[i] = new T();

    // THIS LOOP IS FAST
    for (int i = 0; i < table.Rows.Count; i++)
        entities[i].Init(table, table.Rows[i]);

    return new List<T>(entities);
}

编辑以获取更多信息:

任何给定ModelType的构造函数将如下所示:

public ModelType()
{
    _modelInfo = new ModelTypeInfo();
}

任何给定ModelTypeInfo的构造函数将只设置一些字符串和字符串[]值,而该类的唯一作用是提供设置的值。

修改以获取更多信息:

因为它似乎是一个热门话题,所以在打破对象构造和初始化之前,这是我的方法对于实数的看法:

public static IList<T> ToList<T>(this DataTable table, ModelInfo modelInfo) where T : Model, new()
{
    var tempRepository = new Repository<T>(modelInfo);

    var list = new List<T>();
    foreach (DataRow row in table.Rows)
        list.Add(tempRepository.FromData(table, row));

    return list;
}

8 个答案:

答案 0 :(得分:13)

在幕后,new T()会生成对System.Activator.CreateInstance<T>()的调用,这是(反射性地)缓慢:

L_0012: ldc.i4.0 
L_0013: stloc.1 
L_0014: br.s L_0026
L_0016: ldloc.0 
L_0017: ldloc.1 
L_0018: call !!0 [mscorlib]System.Activator::CreateInstance<!!T>()
L_001d: stelem.any !!T
L_0022: ldloc.1 
L_0023: ldc.i4.1 
L_0024: add 
L_0025: stloc.1 

您可能希望考虑传递建筑代表。

答案 1 :(得分:3)

您的问题的标题表明这与该方法是通用的事实有关。是否更快地分配相同数量的对象而没有泛型?如果没有,它必须与构造函数中正在进行的任何工作有关。你可以发布构造函数代码吗?

EDITED 这是我前一段时间写的一个用于在DynamicMethod中缓存构造函数的东西,这非常快:

在你班上:

delegate T ConstructorDelegate();

方法体:

DynamicMethod method = new DynamicMethod(string.Empty, typeof(T), null,
    MethodBase.GetCurrentMethod().DeclaringType.Module);
ILGenerator il = method.GetILGenerator();
il.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Ret);
var constructor = (ConstructorDelegate)method.CreateDelegate(typeof(ConstructorDelegate));

答案 2 :(得分:3)

问题是表达式new T()实际上在幕后使用了反射。 (它调用Activator.CreateInstance)因此,每次调用都需要时间。


一种解决方案是约束T来实现ICloneable。然后,您可以编写new T()一次,并在循环中克隆它。显然,如果您完全控制模型,则只能这样做。


另一个选择是让方法采用创建者委托,如下所示:

public static IList<T> ToList<T>(this DataTable table, Func<T> creator) where T : Model {
    T[] entities = new T[table.Rows.Count];
    for (int i = 0; i < table.Rows.Count; i++)
        entities[i] = creator();

    //...
}

然后你会这样称呼它:

table.ToList(() => new MyModelType());

因为它在参数中使用,所以在调用方法时不需要显式指定泛型类型。


最少侵入性的方法是使用LINQ表达式来创建自己的创建者方法。

编辑:像这样:

static class CreatorFactory<T> where T : new() {
    public static readonly Func<T> Method = 
        Expression.Lambda<Func<T>>(Expression.New(typeof(T)).Compile();
}

public static IList<T> ToList<T>(this DataTable table) where T : Model {
    var entities = table.Rows.Select(r => CreatorFactory<T>.Method()).ToList();

    for (int i = 0; i < table.Rows.Count; i++)
        entities[i].Init(table, table.Rows[i]);

    return entities;
}

答案 3 :(得分:2)

你真的需要一个列表,或者IEnumerable是否足够好?如果是这样,您可以对对象进行延迟/延迟创建:

public static IEnumerable<T> ToEnumerable<T>(this DataTable table) where T : Model, new()
{
    foreach (DataRow row in table.Rows)
    {
        T entity = new T();
        entity.Init(table, row);

        yield return entity;
    }
}

不幸的是,这仍然可能很慢,因为大部分时间都可能用于构建对象,但它可能允许您将此负载延迟足够长的时间以使应用更快地显示,或者直到你能够完全过滤掉一些物体之后。

另外,您可能会考虑使用类似Factory的模式来实现它:

public static IEnumerable<T> ToEnumerable<T>(this DataTable table, Func<DataRow, T> TFactory) 
{
    foreach (DataRow row in table.Rows)
    {
        yield return TFactory(row);
    }
}

答案 4 :(得分:0)

您是否正在测试发布版本? tables.loop.count是一个简单的属性,你可以比较它从循环中提升它吗? 实例化T的成本是多少? 创建T是否会分配大量小对象,以便您遇到一些垃圾收集?

答案 5 :(得分:0)

以示例方式显示C#中的此方法:

public T Method<T>() where T : new()
{
    return new T();
}

编译为此MSIL代码(来自Reflector):

.method public hidebysig instance !!T Method<.ctor T>() cil managed
{
.maxstack 2
.locals init (
    [0] !!T CS$1$0000,
    [1] !!T CS$0$0001)
L_0000: nop 
L_0001: ldloca.s CS$0$0001
L_0003: initobj !!T
L_0009: ldloc.1 
L_000a: box !!T
L_000f: brfalse.s L_001c
L_0011: ldloca.s CS$0$0001
L_0013: initobj !!T
L_0019: ldloc.1 
L_001a: br.s L_0021
L_001c: call !!0 [mscorlib]System.Activator::CreateInstance<!!T>()
L_0021: stloc.0 
L_0022: br.s L_0024
L_0024: ldloc.0 
L_0025: ret 
}

为了不进入太多的内部,这里有几个步骤,检查几个条件,初始化数据字段的需要等,最后需要调用Activator 。所有这些都是为了实例化泛型类型对象。是的,这是用来代替直接调用类型的构造函数。

答案 6 :(得分:0)

即使您需要使用列表,为什么还要先创建数组?

public static IList<T> ToList<T>(this DataTable table) where T : Model, new()
{
    var list = new List<T>();
    foreach (DataRow dr in table.Rows) {
        T entity = new T();
        entity.Init(table, dr);
        list.Add(entity);
    }
    return list;
}

答案 7 :(得分:0)

对于稍后遇到此问题的任何人,此博文对我非常有帮助:http://blogs.msdn.com/haibo_luo/archive/2005/11/17/494009.aspx

以下是我最终对“工厂”方法所做的更改。 (不是真正合适的工厂,但服务于此目的)

public class Repository<T> : IRepository<T> where T : Model, new()
{
    // ...

    private delegate T CtorDelegate();
    private CtorDelegate _constructor = null;
    private CtorDelegate Constructor
    {
        get
        {
            if (_constructor == null)
            {
                Type type = typeof(T);
                DynamicMethod dm = new DynamicMethod(type.Name + "Constructor", type, new Type[] { }, typeof(Repository<T>).Module);
                ILGenerator ilgen = dm.GetILGenerator();
                ilgen.Emit(OpCodes.Nop);
                ilgen.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
                ilgen.Emit(OpCodes.Ret);
                _constructor = (CtorDelegate)dm.CreateDelegate(typeof(CtorDelegate));
            }
            return _constructor;
        }
    }

    public T FromData(DataTable table, DataRow row)
    {
        T model = Constructor(); // was previously = new T();
        model.Init(table, row);
        return model;
    }
}