2011-09-28 25 views
8

Tôi đã được ấn tượng rằng trong NET đúc (không chuyển đổi) là rất rẻ và nhanh chóng. Tuy nhiên, điều này dường như không phải là trường hợp cho mảng. Tôi đang cố gắng để làm một diễn viên rất đơn giản ở đây, lấy một T1 [] và cast như T2 []. nơi T1: T2.Tại sao các mảng đúc (vectơ) lại chậm?

Có 3 cách để làm điều này và tôi gọi họ như sau ::

DropCasting: T2[] array2 = array; 
CastClass: (T2[])array; 
IsInst: array as T2[]; 

Và tôi tạo ra phương pháp để làm điều này, không may, C# dường như để tạo ra một số mã khá lạ tuỳ theo nếu điều này là chung chung hay không. (Nếu DropCasting chung của nó sử dụng toán tử castclass. Và trong cả hai trường hợp đều từ chối phát ra toán tử 'as' khi T1: T2.

Dù sao, tôi đã viết một số phương thức động và thử nghiệm nó với một số kết quả đáng ngạc nhiên (string [] => object []):.?

DropCast : 223ms 
IsInst : 3648ms 
CastClass: 3732ms 

Dropcasting là nhanh hơn so với một trong các nhà khai thác dàn diễn viên ~ 18 lần Tại sao đúc quá chậm đối với mảng đối với đối tượng bình thường như chuỗi => đối tượng, sự khác biệt là ít hơn nhiều nghiêm trọng.

DropCast : 386ms 
IsInst : 611ms 
CastClass: 519ms 

Mã điểm chuẩn bel ow:

class Program 
{ 
    static readonly String[] strings = Enumerable.Range(0, 10).Select(x => x.ToString()).ToArray(); 

    static Func<string[], object[]> Dropcast = new Func<Func<string[], object[]>>(
     () => 
     { 
      var method = new DynamicMethod("DropCast", typeof(object[]), new[] { typeof(object), typeof(string[]) },true); 
      var ilgen = method.GetILGenerator(); 
      ilgen.Emit(OpCodes.Ldarg_1); 
      ilgen.Emit(OpCodes.Ret); 
      return method.CreateDelegate(typeof(Func<string[], object[]>)) as Func<string[], object[]>; 
     })(); 
    static Func<string[], object[]> CastClass = new Func<Func<string[], object[]>>(
     () => 
     { 
      var method = new DynamicMethod("CastClass", typeof(object[]), new[] { typeof(object), typeof(string[]) },true); 
      var ilgen = method.GetILGenerator(); 
      ilgen.Emit(OpCodes.Ldarg_1); 
      ilgen.Emit(OpCodes.Castclass, typeof(object[])); 
      ilgen.Emit(OpCodes.Ret); 
      return method.CreateDelegate(typeof(Func<string[], object[]>)) as Func<string[], object[]>; 
     })(); 

    static Func<string[], object[]> IsInst = new Func<Func<string[], object[]>>(
     () => 
     { 
      var method = new DynamicMethod("IsInst", typeof(object[]), new[] { typeof(object), typeof(string[]) },true); 
      var ilgen = method.GetILGenerator(); 
      ilgen.Emit(OpCodes.Ldarg_1); 
      ilgen.Emit(OpCodes.Isinst, typeof(object[])); 
      ilgen.Emit(OpCodes.Ret); 
      return method.CreateDelegate(typeof(Func<string[], object[]>)) as Func<string[], object[]>; 
     })(); 

    static Func<string[], object[]>[] Tests = new Func<string[], object[]>[]{ 
     Dropcast, 
     IsInst, 
     CastClass 
    }; 
    static void Main(string[] args) 
    { 
     int maxMethodLength = Tests.Select(x => GetMethodName(x.Method).Length).Max(); 
     RunTests(1, false, maxMethodLength); 
     RunTests(100000000, true, maxMethodLength); 
    } 

    static string GetMethodName(MethodInfo method) 
    { 
     return method.IsGenericMethod ? 
     string.Format(@"{0}<{1}>", method.Name, string.Join<Type>(",", method.GetGenericArguments())) : method.Name; 
    } 

    static void RunTests(int count, bool displayResults, int maxLength) 
    { 
     foreach (var action in Tests) 
     { 
      Stopwatch sw = Stopwatch.StartNew(); 
      for (int i = 0; i < count; i++) 
      { 
       action(strings); 
      } 
      sw.Stop(); 
      if (displayResults) 
      { 
       Console.WriteLine("{0}: {1}ms", GetMethodName(action.Method).PadRight(maxLength), 
       ((int)sw.ElapsedMilliseconds).ToString().PadLeft(6)); 
      } 
      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 
      GC.Collect(); 
     } 
    } 
} 

Chỉnh sửa trước khi bất kỳ ai yêu cầu lưu giữ đúng đối với những thứ như int [] -> uint [] mà thông số kỹ thuật clr sẽ được truyền mà không cần chuyển đổi.

+0

Vấn đề là xoa bóp quyền IL.Trong một phương thức cực kỳ nhỏ như '() => strings as object [];' trình biên dịch sẽ thả phương thức 'as'. Việc tạo phương thức động chỉ chạy một lần tại '.cctor' của chương trình. Sau đó mỗi phương pháp chỉ là đốm màu IL. Ngoài ra tôi đã thêm một "thể hiện" cho mỗi phương thức động (tham số đối tượng), chỉ để tránh sự lộn xộn khi sử dụng một ủy nhiệm trên một phương thức tĩnh. –

+0

vâng, tôi đã bỏ lỡ lớp thứ hai của func khi đọc lần đầu tiên. Vì vậy, tôi đã xóa nhận xét của mình. ;) –

+1

Đã bao gồm nhiều lần rồi, chỉ có thể tìm thấy trang chủ: http://blogs.msdn.com/b/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two -array-covariance.aspx –

Trả lời

0

Vì bạn đang truyền mảng.

Sự khác biệt giữa 3 đoạn mã IL là hai sau này thêm một IsInst và một hoạt động CastClass. Rất ít thông tin về các loại, vì vậy CLR phải kiểm tra xem nó có phải là một hoạt động hợp lệ hay không. Điều này cần có thời gian.

Sự khác biệt nhỏ giữa CastClass và IsInst có thể được giải thích bằng thực tế là CastClass thực hiện kiểm tra null trước và thành công ngay lập tức nếu đối số là rỗng.

Tôi nghi ngờ rằng sự chậm lại là do bạn đang truyền giữa các mảng. Công việc nhiều hơn có thể cần phải được thực hiện để đảm bảo rằng một mảng đúc là hợp lệ. Nó có thể là cần thiết để xem xét từng yếu tố để xem liệu nó có thể được đúc thành loại phần tử mục tiêu hay không. Vì vậy, tôi sẽ đoán rằng, thay vì làm tất cả điều này trong mã máy 'nội tuyến', JIT phát ra một cuộc gọi đến một hàm xác nhận.

Thực tế, nếu bạn chạy phân tích hiệu suất, bạn có thể thấy đó thực sự là những gì đang xảy ra. Gần 90% thời gian được dùng trong một hàm gọi là "JIT_ChkCastArray".

+0

Điều này có ý nghĩa. Nó chỉ có vẻ kỳ lạ với tôi như thể T1: lớp, T2 sau đó Diễn viên phải luôn luôn là hợp pháp, vậy tại sao bận tâm làm kiểm tra? –

+0

Tôi đoán là, bởi vì mảng đúc là tương đối hiếm, các nhà phát triển JIT không bận tâm để tối ưu hóa nó. Nó cũng là một tối ưu hóa mà trình biên dịch có thể làm một cách dễ dàng bằng cách không phát ra các CastClass hoặc IsInst hướng dẫn ở nơi đầu tiên. Tài nguyên JIT bị giới hạn, vì vậy, bất kỳ sự tối ưu hóa nào cũng tương đối tốn kém và phải được chứng minh. –

0

Điều đó có ý nghĩa với tôi rằng việc đúc sẽ là (gần như) chính xác như đắt tiền khi sử dụng toán tử as. Trong cả hai trường hợp, việc kiểm tra thời gian chạy trên loại đối tượng phải được thực hiện và phải được xác định xem nó có tương thích với loại mục tiêu hay không. Việc kiểm tra được yêu cầu để cho phép hoạt động diễn viên ném một số InvalidCastException nếu cần.

Để đặt theo cách khác, nhà điều hành aslà hoạt động đúc - nó cũng có đức hạnh cho phép diễn viên thất bại mà không ném ngoại lệ (bằng cách trả lại null). Điều này cũng có thể được thực hiện với sự kết hợp của nhà điều hành is và dàn diễn viên, nhưng điều đó sẽ tăng gấp đôi khối lượng công việc.

+0

Tôi nhận thấy một loại kiểm tra đang xảy ra nhưng tại sao nó đắt hơn nhiều? –

+0

đắt hơn những gì? Kết quả của riêng bạn cho thấy việc truyền và toán tử 'as' cơ bản giống nhau. (như tôi cho là họ nên làm) Và ví dụ "kiểm soát" đầu tiên của bạn là một nhiệm vụ - hầu như là không có op. (đối với "drop cast" của bạn, trình biên dịch đã thực hiện tất cả công việc cần thiết để kiểm tra tính hợp lệ, vì vậy không có hiệu năng thời gian chạy nào) –

+0

Xin lỗi, tôi biết rằng 'as' và' (T []) 'đều là phôi, Tôi có nghĩa là để hỏi tại sao họ đã quá chậm so với trường hợp kiểm soát của đúc string-> đối tượng. –

Các vấn đề liên quan