2009-07-14 29 views
6

Mã bên dưới cực kỳ chậm đối với các bảng có kích thước đáng kể. (100, 1000, v.v.) Thủ phạm đang khởi tạo các đối tượng của tôi với new T(). Lưu ý rằng đây không phải là mã hoàn thiện của tôi, tôi vừa chia nhỏ các phần của nó ra để dễ dàng hơn trong hồ sơ. Việc khởi tạo và khởi tạo sẽ xảy ra cùng nhau khi tôi tái cấu trúc lại mã.Làm thế nào tôi có thể tăng tốc độ khởi tạo một tập hợp các đối tượng lớn?

Có cách nào để tăng tốc độ này không? Tôi có thể quên một cái gì đó thật sự đơn giản, hoặc có lẽ tôi đang boned. Hy vọng rằng, trước đây.

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

chỉnh sửa để biết thêm:

Các constructor của bất kỳ trao ModelType sẽ trông như thế này:

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

Các constructor của bất kỳ ModelTypeInfo được chỉ đơn giản là sẽ thiết lập một số chuỗi và string [] giá trị và công việc duy nhất của lớp đó là cung cấp các giá trị được đặt.

chỉnh sửa để biết nhiều hơn:

Kể từ khi nó có vẻ là một chủ đề nóng, đây là những gì phương pháp của tôi trông giống như đối với số thực trước khi phá vỡ xây dựng đối tượng và khởi tạo:

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

tắt chủ đề: Tại sao bạn không chỉ cần tạo một 'Danh sách ' ở nơi đầu tiên thay vì một 'T [] 'và sau đó biến chúng thành một danh sách? Ngoài ra, tại sao 'for' thay vì' foreach'? – Svish

+0

Trong thực tế, đó là * là những gì tôi làm. Tôi vừa phá mã xuống dưới dạng này cho mục đích minh họa. Bạn biết đấy, để cho biết mã đang ở đâu chậm, sử dụng ít ma thuật nhất có thể. –

Trả lời

13

Dưới sự bao trùm, new T() tạo ra một cuộc gọi đến System.Activator.CreateInstance<T>(), đó là (phản tư) chậm:

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 

Thay vào đó, bạn có thể cân nhắc việc chuyển sang một đại biểu xây dựng.

+1

+1. Không biết điều đó, và có thể trả lời câu hỏi. –

+0

Hoặc, có thể xác định đúng hàm tạo chỉ một lần, thay vì n lần không? Tôi nghĩ điều đó sẽ đủ tốt mà không làm xấu đi mọi cuộc gọi đến phương pháp này. –

+0

CreateDelegate yêu cầu một MethodInfo không khớp với một ConstructorInfo. Tốt nhất, bạn phải Emit một phương thức wrapper cho constructor và bọc nó trong một delegate để tránh sự phản chiếu xảy ra bên trong vòng lặp. –

3

Các tiêu đề của câu hỏi của bạn cho thấy rằng điều này đã làm với thực tế là phương pháp này là chung chung. Phân bổ cùng một số đối tượng không có generics nhanh hơn? Nếu không, nó phải được làm với bất kỳ công việc đang xảy ra trong constructor của bạn. Bạn có thể đăng các mã constructor?

EDITED Dưới đây là một cái gì đó tôi đã viết một lúc trước để nhà xây dựng bộ nhớ cache trong một DynamicMethod, mà là rất nhanh:

Trong lớp học của bạn:

delegate T ConstructorDelegate(); 

Thân phương pháp:

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)); 
+0

Cố định tiêu đề ít cụ thể hơn. –

+0

Có thể sử dụng Func thay vì khai báo một ConstructorDelegate có tên? –

+0

Điều đó sẽ tốt hơn. Đoạn trích này hơi cũ - được viết trước khi tôi thực sự sử dụng Func và Action với C# 3. –

0

Bạn đang thử nghiệm bản phát hành bản phát hành?
Bảng.loop.count là một thuộc tính đơn giản và bạn có thể so sánh để kéo nó ra khỏi vòng lặp không?
Chi phí khởi tạo T là gì?
Tạo T có phân bổ rất nhiều đối tượng nhỏ để bạn chạy vào một vài bộ sưu tập rác?

2

Bạn có thực sự cần một danh sách hoặc liệu IEnumerable có đủ tốt không? Nếu vậy, bạn có thể làm lười biếng/tạo thu nhập hoãn lại của các đối tượng của bạn:

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

Đáng tiếc là điều này vẫn còn có khả năng là chậm, vì hầu hết thời gian có khả năng dành xây dựng các đối tượng, nhưng nó có thể cho phép bạn trì hoãn này tải đủ lâu để làm cho ứng dụng xuất hiện nhanh hơn hoặc cho đến khi bạn có thể lọc hoàn toàn một số đối tượng.

Ngoài ra, bạn có thể nghĩ về thực hiện điều này bằng cách sử dụng máy -like mẫu:

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

Mã ban đầu giống như nhà máy hơn và có phương pháp tạo T dựa trên DataRow. Từ khóa lợi nhuận mà tôi không biết! Cảm ơn bạn đã cho tôi thấy điều này. Đáng tiếc là tôi cần một người theo đạo luật. –

+0

Bạn chỉ có thể gọi .ToList() trên các kết quả. –

+0

Phải, tôi phải quên. Tôi ngớ ngẩn quá. ;) Tôi đã kết thúc thực hiện phương pháp IEnumerable của bạn anyway, và làm sạch lên ToList() để piggyback trên ToEnumerable(). Có lẽ tôi sẽ cần nó. –

0

Để hiển thị bằng ví dụ, phương pháp này trong C#:

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

được biên dịch sang mã này MSIL (từ 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 
} 

Để không đi vào internals quá nhiều, có một số bước ở đây, một số điều kiện được kiểm tra, sự cần thiết phải khởi tạo các trường dữ liệu, vv, và cuối cùng là một cuộc gọi đến Activator có thể được yêu cầu. Tất cả điều này để khởi tạo một đối tượng kiểu generic. Và có, điều này được sử dụng thay vì một cuộc gọi trực tiếp đến constructor của loại luôn.

0

Thậm chí nếu bạn cần sử dụng danh sách, tại sao lại tạo mảng trước?

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

Đây là mã tạm thời để minh họa nơi diễn ra sự chậm trễ. Đọc câu hỏi chặt chẽ hơn xin vui lòng. –

3

Vấn đề là biểu thức new T() thực sự sử dụng phản chiếu đằng sau hậu trường. (Nó gọi Activator.CreateInstance) Vì vậy, mỗi cuộc gọi đến nó sẽ mất thời gian.


Một giải pháp sẽ là hạn chế T thực hiện ICloneable. Sau đó, bạn có thể viết new T() một lần và sao chép nó trong vòng lặp. Rõ ràng, bạn chỉ có thể làm điều đó nếu bạn có toàn quyền kiểm soát mô hình.


Một lựa chọn khác sẽ được thực hiện phương pháp này có một đại biểu tác giả, như thế này:

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(); 

    //... 
} 

Sau đó, bạn sẽ gọi nó là như thế này:

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

Bởi vì nó được sử dụng trong một tham số, bạn sẽ không cần phải chỉ định kiểu generic một cách rõ ràng khi gọi phương thức.


Phương pháp xâm nhập ít nhất là sử dụng biểu thức LINQ để tạo phương thức của người sáng tạo của riêng bạn.

EDIT: Như thế này:

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

Tôi vừa đề xuất điều này. Tôi sử dụng một phương pháp tương tự để xây dựng treenodes. – leppie

+0

Tôi đặt cược điều này được anh ta phải xuống đến nút cổ chai tiếp theo (tôi có thể đặt cược bởi vì tôi đã được chỉ là về khá nhiều cùng một mã vào câu trả lời của tôi). –

0

Đối với bất cứ ai chạy vào vấn đề này sau này, bài viết trên blog này là cực kỳ hữu ích cho tôi: http://blogs.msdn.com/haibo_luo/archive/2005/11/17/494009.aspx

Dưới đây là những thay đổi tôi đã kết thúc thực hiện để phương pháp "nhà máy" của tôi.(Không thực sự là một nhà máy thích hợp, nhưng phục vụ mục đích này)

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; 
    } 
} 
Các vấn đề liên quan