代码之家  ›  专栏  ›  技术社区  ›  Samantha Branham

如何加快实例化大型对象集合的速度?

  •  6
  • Samantha Branham  · 技术社区  · 17 年前

    对于任何较大的表,下面的代码都非常慢。(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 只需设置一些string和string[]值,该类的唯一任务就是提供设置的值。

    编辑以获取更多信息:

    因为这似乎是一个热门话题,下面是我的方法在开始对象构造和初始化之前对reals的外观:

    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 回复  |  直到 17 年前
        1
  •  13
  •   Jeffrey Hantin    17 年前

    在封面下,, 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 
    

    你可能希望考虑通过一个建筑代表。

        2
  •  3
  •   Ben M    17 年前

    您问题的标题表明,这与该方法是通用的这一事实有关。在没有泛型的情况下分配相同数量的对象是否更快?如果不是,那一定是与构造函数中正在进行的任何工作有关。你能发布构造函数代码吗?

    编辑 这是我不久前写的一篇文章,用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));
    
        3
  •  3
  •   Joel Coehoorn    17 年前

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


    新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;
    }
    
        4
  •  2
  •   Joel Coehoorn    17 年前

    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;
        }
    }
    

    不幸的是,这仍然可能很慢,因为大部分时间可能都花在构建对象上,但它可能允许您将此加载延迟足够长的时间,以便生成应用程序 更快,或者直到您能够完全过滤掉一些对象。

    此外,您可能会考虑使用类似工厂的模式来实现这一点:

    public static IEnumerable<T> ToEnumerable<T>(this DataTable table, Func<DataRow, T> TFactory) 
    {
        foreach (DataRow row in table.Rows)
        {
            yield return TFactory(row);
        }
    }
    
        5
  •  0
  •   peterchen    17 年前

    您正在测试发布版本吗?


    创建时不会分配很多小对象,因此会遇到一些垃圾收集吗?

        6
  •  0
  •   Fabrício Matté    17 年前

    通过示例说明,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 也许 这是必需的。所有这些都是为了实例化泛型类型对象。是的,它总是用来代替直接调用类型的构造函数。

        7
  •  0
  •   John Saunders    17 年前

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

    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;
    }
    
        8
  •  0
  •   Samantha Branham    17 年前

    对于后来遇到这个问题的人来说,这篇博文对我非常有帮助: 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;
        }
    }