2011-01-03 44 views
17

Tôi đã được dẫn dắt để tin rằng đúc có thể, trong một số trường hợp, trở thành một trở ngại đo lường về hiệu suất. Điều này có thể là trường hợp khi chúng tôi bắt đầu xử lý các trang web không liên quan của ngoại lệ khó chịu khi ném.Giao diện đúc và đúc lớp

Cho rằng tôi muốn tạo ra các chẩn đoán chính xác hơn khi nói đến lập trình, tôi đã được nhắc đặt câu hỏi này cho các chuyên gia .NET. Có giao diện đúc nhanh hơn so với truyền lớp không?

Để cung cấp cho một mã ví dụ, chúng ta hãy nói điều này tồn tại:

public interface IEntity { IParent DaddyMommy { get; } } 
public interface IParent : IEntity { } 
public class Parent : Entity, IParent { } 
public class Entity : IEntity 
{ 
    public IParent DaddyMommy { get; protected set; } 
    public IParent AdamEve_Interfaces 
    { 
     get 
     { 
      IEntity e = this; 
      while (e.DaddyMommy != null) 
       e = e.DaddyMommy as IEntity; 
      return e as IParent; 
     } 
    } 
    public Parent AdamEve_Classes 
    { 
     get 
     { 
      Entity e = this; 
      while (e.DaddyMommy != null) 
       e = e.DaddyMommy as Entity; 
      return e as Parent; 
     } 
    } 
} 

Vì vậy, là AdamEve_Interfaces nhanh hơn AdamEve_Classes? Nếu có thì bao nhiêu? Và, nếu bạn biết câu trả lời, tại sao?

+37

Thực tế, phiên bản OOP hiện đại của mã spaghetti là ** mã lasagna **: nhiều lớp ... :-) – rsenna

+0

@rsenna ~ Hahaha! Bạn biết đấy, tôi đã sửa chữa: P – Squirrelsama

+0

@rsenna: Thật tuyệt vời. – NotMe

Trả lời

7

Hãy xem ở đây:

http://thatstoday.com/robbanp/blog/6/25/csharp-performance--cast-vs-interface

Và, vâng, bạn dường như là đúng.

Chỉnh sửa Vâng, có vẻ như tôi đã sai. Và giống như của tôi "patrício" Martinho Fernandes nhận xét dưới đây, liên kết trên là hoàn toàn không có thật (nhưng tôi sẽ giữ nó ở đây, vì lợi ích của chỉnh sửa trung thực).

Tôi có một số thời gian rảnh rỗi hiện nay, vì vậy tôi đã viết một mã số đo hiệu suất đơn giản:

public partial class Form1 : Form 
{ 
    private const int Cycles = 10000000; 

    public interface IMyInterface 
    { 
     int SameProperty { get; set; } 
    } 

    public class InterfacedClass : IMyInterface 
    { 
     public int SameProperty { get; set; } 
    } 

    public class SimpleClass 
    { 
     public int SameProperty { get; set; } 
    } 

    public struct InterfacedStruct : IMyInterface 
    { 
     public int SameProperty { get; set; } 
    } 

    public struct SimpleStruct 
    { 
     public int SameProperty { get; set; } 
    } 

    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void Form1_Load(object sender, EventArgs e) { 
     var simpleClassTime = MeasureSimpleClass(); 
     var interfacedClassTime = MeasureInterfacedClass(); 
     var simpleStructTime = MeasureSimpleStruct(); 
     var interfacedStructTime = MeasureInterfacedStruct(); 

     var message = string.Format(
      "simpleClassTime = {0}\r\ninterfacedClassTime = {1}\r\nsimpleStructTime = {2}\r\ninterfacedStructTime = {3}", 
      simpleClassTime, 
      interfacedClassTime, 
      simpleStructTime, 
      interfacedStructTime 
     ); 

     textBox.Text = message; 
    } 

    private static long MeasureSimpleClass() { 
     var watch = Stopwatch.StartNew(); 
     var obj = new SimpleClass(); 

     for (var i = 0; i < Cycles; i++) 
     { 
      obj.SameProperty = i; 
      var j = obj.SameProperty; 
      obj.SameProperty = j; 
     } 

     return watch.ElapsedMilliseconds; 
    } 

    private static long MeasureInterfacedClass() { 
     var watch = Stopwatch.StartNew(); 
     IMyInterface obj = new InterfacedClass(); 

     for (var i = 0; i < Cycles; i++) { 
      obj.SameProperty = i; 
      var j = obj.SameProperty; 
      obj.SameProperty = j; 
     } 

     return watch.ElapsedMilliseconds; 
    } 

    private static long MeasureSimpleStruct() 
    { 
     var watch = Stopwatch.StartNew(); 
     var obj = new SimpleStruct(); 

     for (var i = 0; i < Cycles; i++) 
     { 
      obj.SameProperty = i; 
      var j = obj.SameProperty; 
      obj.SameProperty = j; 
     } 

     return watch.ElapsedMilliseconds; 
    } 

    private static long MeasureInterfacedStruct() 
    { 
     var watch = Stopwatch.StartNew(); 
     IMyInterface obj = new InterfacedStruct(); 

     for (var i = 0; i < Cycles; i++) 
     { 
      obj.SameProperty = i; 
      var j = obj.SameProperty; 
      obj.SameProperty = j; 
     } 

     return watch.ElapsedMilliseconds; 
    } 
} 

Và kết quả là:

simpleClassTime = 274 
interfacedClassTime = 339 
simpleStructTime = 247 
interfacedStructTime = 302 

Tôi thực sự đã từng nghĩ rằng một giao diện sẽ nhanh hơn đối với các loại class và chậm hơn đối với struct (vì quyền anh/unboxing có liên quan đến thứ hai), nhưng đó không phải là trường hợp: tham chiếu lớp/cấu trúc cụ thể luôn nhanh hơn, có vẻ như vậy.

Ngoài ra, người có thể quan tâm: Tôi tin rằng hiệu suất là không phải là tiêu chí tốt để quyết định xem có nên sử dụng giao diện hay không. Sự khác biệt là, giống như những người khác nói ở đây, không đáng kể.

+1

@Martinho Fernandes: wow, đơn giản "Bạn sai" sẽ đủ. Một lần nữa, "nó giống như đánh một con ngựa chết", quả thật vậy. :-) Tôi sẽ cố gắng tìm một liên kết phù hợp hơn, hãy chú ý theo dõi. – rsenna

+6

Tôi thấy "Bạn sai" mà không giải thích là thô lỗ. Đó giống như một downvote mà không có một lời giải thích. –

+2

@Martinho Fernandes: Vâng, bạn thô lỗ như nhau; bạn chỉ mất một chút thời gian để đến đó. Nhưng, xin vui lòng, hãy xem câu trả lời đã chỉnh sửa của tôi. Bạn đã đúng, tôi đã sai, vì vậy * mea maxima culpa *. – rsenna

7

Bạn sẽ phải đo lường.

Nhưng nếu quá trình truyền đang trở thành một nút cổ chai (tiềm năng) trong mã của bạn, bạn đã vượt qua spaghetti trên menu sự cố.

+0

Tôi đồng ý hết lòng. Nhưng tôi có điều này ở đâu, cho thời gian có sẵn, cải thiện mã của tôi là phải. Gọi nó là thủ dâm trí tuệ, nếu bạn phải. : P – Squirrelsama

2

Trước hết, bạn không cần truyền ở đây vì mã phải hoạt động mà không cần truyền. IParentIEntity vì vậy nó chỉ hoạt động.

Quá trình truyền có tác động đến hiệu suất không? Hơi nếu nó liên quan đến chuyển đổi (nếu loại thực hiện IConvertible và chuyển đổi là cần thiết). Nếu không, nó là không đáng kể vì tất cả những gì cần làm là thực hiện kiểm tra loại sẽ nhanh như chớp.

+0

Trong khi tôi đánh giá cao sự điều chỉnh, mục đích là để chứng minh sự khác biệt mà tôi đã thực sự nói đến.:) – Squirrelsama

+0

Cho dù một diễn viên là tiềm ẩn hay không không loại bỏ thực tế rằng có một diễn viên xảy ra. Các giao diện cho các giao diện yêu cầu trình biên dịch tìm ra thực thi có nguồn gốc cao nhất, vì một lớp cơ sở có thể thực hiện một giao diện (thậm chí không phải là hầu như) và sau đó một lớp dẫn xuất có thể tái triển khai giao diện. –

4

Giả sử không có toán tử chuyển đổi tĩnh được xác định, truyền sẽ mất cùng một khoảng thời gian.Có khả năng một số tối ưu hóa "nội tuyến" có thể xảy ra khi gọi một phương thức trên một lớp chứ không phải là giao diện, nhưng điều đó sẽ không được chú ý trừ khi gọi một phương thức là số điên.

Trên tất cả; không có vấn đề về hiệu suất đáng kể nào. Hoặc để đặt nó theo một cách khác: cho đến khi tôi đã lược tả và hiển thị vấn đề này, tôi sẽ tìm ở nơi khác trước.

4

Bạn đã thử nghiệm nó chưa? Đây là một vòng lặp chạy 10.000.000 lần. Trên máy của tôi, phiên bản giao diện mất khoảng 440 ms và phiên bản lớp khoảng 410 ms. Vì vậy, khá gần nhưng tổng thể phiên bản lớp thắng.

using System; 

namespace ConsoleApplication1 
{ 
    public interface IEntity { IParent DaddyMommy { get; } } 
    public interface IParent : IEntity { } 
    public class Parent : Entity, IParent { } 
    public class Entity : IEntity 
    { 
     public IParent DaddyMommy { get; protected set; } 
     public IParent AdamEve_Interfaces 
     { 
      get 
      { 
       IEntity e = this; 
       while (this.DaddyMommy != null) 
        e = e.DaddyMommy as IEntity; 
       return e as IParent; 
      } 
     } 
     public Parent AdamEve_Classes 
     { 
      get 
      { 
       Entity e = this; 
       while (this.DaddyMommy != null) 
        e = e.DaddyMommy as Entity; 
       return e as Parent; 
      } 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      Entity X = new Entity(); 
      Parent P; 
      IParent IP; 
      System.Diagnostics.Stopwatch ST = new System.Diagnostics.Stopwatch(); 
      Int32 i; 

      ST.Start(); 
      for (i = 0; i < 10000000; i++) 
      { 
       IP = X.AdamEve_Interfaces; 
      } 
      ST.Stop(); 
      System.Diagnostics.Trace.WriteLine(ST.ElapsedMilliseconds); 

      ST.Reset(); 

      ST.Start(); 
      for (i = 0; i < 10000000; i++) 
      { 
       P = X.AdamEve_Classes; 
      } 
      ST.Stop(); 
      System.Diagnostics.Trace.WriteLine(ST.ElapsedMilliseconds); 
     } 
    } 

} 
+2

bạn có thể có nghĩa là phiên bản ** class ** thắng (nhanh hơn). – rsenna

+0

@rsenna, cảm ơn, bạn đã đúng! Tôi đã viết mã xung quanh sau khi tôi viết nó để đảm bảo kết quả không phụ thuộc vào thứ tự tải hay thứ gì đó. –

16

Một số câu trả lời ở đây đề xuất điểm chuẩn, là một bước đi đúng hướng, nhưng chỉ là bước đầu tiên trong hành trình.

Nhóm của tôi đã thực hiện rất nhiều hồ sơ và điểm chuẩn trong lĩnh vực này. Phiên bản ngắn là , có các tình huống trong đó giao diện áp đặt chi phí hiệu suất nhỏ nhưng có thể đo lường được. Tuy nhiên chi phí thực tế phụ thuộc vào nhiều yếu tố, bao gồm số lượng giao diện được hỗ trợ, số lượng giao diện được đưa ra cho tham chiếu đó, mẫu truy cập là gì, v.v. Các CLR có rất nhiều heuristics tại chỗ được thiết kế để tăng tốc độ truy cập giao diện trong các trường hợp phổ biến.

Nếu bạn đang đánh giá một trong những trường hợp phổ biến, nhưng chương trình thực tế của bạn rơi vào trường hợp ít phổ biến hơn, thì điểm chuẩn của bạn là tích cực gây hại vì nó đang mang lại cho bạn dữ liệu gây hiểu nhầm.

Tốt hơn để thực hiện các phép đo hiệu suất thực tế trên mã thực. Sử dụng một profiler, viết mã cả hai cách, và xem liệu một trong hai cách có thể đo lường, lặp lại nhanh hơn theo cách có thể nhìn thấy và có liên quan đến người dùng.

Để bạn tham khảo để ném và bắt: chi phí hiệu suất của việc ném và đánh bắt sẽ không liên quan. Ngoại lệ theo định nghĩa ngoại lệ, không phải phổ biến. Hơn nữa, ngoại lệ thường chỉ ra rằng một cái gì đó sẽ tạm dừng; nó thường không quan trọng cho dù một cái gì đó tạm dừng càng nhanh càng tốt. Nếu bạn đang ở trong một tình huống mà hiệu suất của bạn được gated bởi ngoại lệ sau đó bạn có vấn đề lớn hơn để giải quyết: ngừng ném rất nhiều trường hợp ngoại lệ. Một ngoại lệ được ném nên cực kỳ hiếm.

+1

Câu trả lời hay. Không thể đồng ý nhiều hơn về ngoại lệ. – Squirrelsama

+0

Tôi đã ngạc nhiên khi thấy một tình huống với XBox bằng cách sử dụng C# của XNA, nơi một số lớp đã triển khai một số giao diện có vẻ hợp lý và có một hình phạt đáng kể để truyền cho chúng. Tôi sẽ tìm kiếm thêm thông tin về lý do tại sao nó nhiều hơn tôi mong đợi nhưng tôi chỉ muốn thả một lưu ý ở đây để nói rằng đôi khi nó là nhiều hơn một vấn đề hiệu suất nhỏ. Đối với tôi, loại bỏ một số phôi tái định hình với một thời gian cast & cache lưu tôi vài mili giây đó là rất nhiều khi bạn xem xét chỉ có 16 trong số họ có sẵn để duy trì một màn hình 60hz. –