2010-03-09 38 views
24

Tôi đã đọc cuốn sách Cwalina (các khuyến nghị về phát triển và thiết kế các ứng dụng .NET).Tại sao kích thước 16 byte được đề nghị cho cấu trúc trong C#?

Ông nói rằng cấu trúc được thiết kế tốt phải có kích thước nhỏ hơn 16 byte (cho mục đích hiệu suất).

Tại sao chính xác?

Và (quan trọng hơn) tôi có thể có cấu trúc lớn hơn với cùng một hiệu quả nếu tôi chạy NET của tôi 3,5 (sớm để được .NET 4.0) 64-bit ứng dụng trên hệ điều hành Windows Core i7   7 x64 (là CPU hạn chế này/OS dựa)?

Chỉ cần nhấn mạnh một lần nữa - Tôi cần cấu trúc hiệu quả nhất có thể. Tôi cố gắng giữ nó trên ngăn xếp mọi lúc. Ứng dụng này có nhiều luồng và chạy trên các khoảng thời gian mili giây, và kích thước hiện tại của cấu trúc là 64 byte.

+2

Điều gì khiến bạn tin rằng việc giữ dữ liệu trên ngăn xếp có hiệu quả hơn? Stack so với heap là một chi tiết thực hiện trong.NET và các nhà phát triển không nên quan tâm (xem http://stackoverflow.com/questions/477101/heap-versus-stack-allocation-implications-net/477333#477333) –

+1

Hỏi về hiệu quả mà không nói số liệu bạn đang sử dụng là một chút mơ hồ. Nó giống như yêu cầu chiếc xe hiệu quả nhất - mà không đề cập đến việc bạn có muốn tiết kiệm tiền đi lại hay cố gắng vận chuyển 30 tấn hàng hóa hay không. –

+1

Divo bạn sai về nguyên tắc. Tôi nên quan tâm điều gì để làm cho ứng dụng của tôi tốt hơn bên ngoài chủ đề này. Cảm ơn –

Trả lời

8

Chỉ có bạn biết cấu trúc của bạn đang được sử dụng như thế nào trong chương trình của bạn. Nhưng nếu không có gì khác, bạn luôn có thể kiểm tra nó cho chính mình. Ví dụ, nếu nó thường xuyên thông qua chức năng khác, sau đây có thể thắp sáng bạn:

class MainClass 
{ 
    static void Main() 
    { 
     Struct64 s1 = new Struct64(); 
     Class64 c1 = new Class64(); 
     DoStuff(s1); 
     DoStuff(c1); 
     Stopwatch sw = new Stopwatch(); 
     sw.Start(); 
     for (int i = 0; i < 10000; i++) 
     { 
      s1 = DoStuff(s1); 
     } 
     sw.Stop(); 
     Console.WriteLine("Struct {0}", sw.ElapsedTicks); 
     sw.Reset(); 

     sw.Start(); 
     for (int i = 0; i < 10000; i++) 
     { 
      c1 = DoStuff(c1); 
     } 
     sw.Stop(); 
     Console.WriteLine("Class {0}", sw.ElapsedTicks); 
     sw.Reset(); 

     Console.ReadLine(); 
    } 
} 

với:

public class Class64 
{ 
    public long l1; 
    public long l2; 
    public long l3; 
    public long l4; 
    public long l5; 
    public long l6; 
    public long l7; 
    public long l8; 
} 
public struct Struct64 
{ 
    public long l1; 
    public long l2; 
    public long l3; 
    public long l4; 
    public long l5; 
    public long l6; 
    public long l7; 
    public long l8; 
} 

Hãy thử các loại điều này với cấu trúc đại diện/classes, và xem những gì kết quả bạn nhận được .(Trên máy của tôi, trên thử nghiệm, lớp học dường như ~ 3 lần nhanh hơn)

+1

Tương tự ở đây .. Đối với 16 byte tôi đã có 24/18 (struct vs class), cho 32 byte 30/17 vor 64 byte 42/18. Có vẻ như 16 byte thực sự là một ngưỡng hợp lý bất kể chiều rộng CPU/bus. –

+1

Thay thế điểm chuẩn bằng một vòng lặp cho cấu trúc có 'for (int i = 0; i <100000; i ++) {mystruct.l1 + = i;}', rồi thử nó cho một cấu trúc và lớp với 'for (int i = 0; i <100000; i ++) {mything = new Bất cứ điều gì (mything.l1 + i, mything.l2, mything.l3, mything.l4, mything.l5, mything.l6, mything.l7, mything.l8)}; 'Cấu trúc biến đổi đơn giản sẽ giành chiến thắng một dặm, với thời gian thực thi độc lập với số lượng trường. Phiên bản "không thay đổi cấu trúc kiểu" có khả năng hoạt động tốt hơn phiên bản lớp bằng một biên độ đáng kể, tuy nhiên; thêm hàng chục trường 'long' sẽ không thay đổi điều đó. – supercat

4
  • Cấu trúc lớn hơn không hiệu quả, nhưng sau đó .... nếu bạn CÓ thêm dữ liệu, bạn có nó. Không có ý nghĩa nói về hiệu quả ở đó.

  • 64 byte phải OK.

  • Lý do chính có thể là hoạt động sao chép .... sẽ chậm hơn nếu cấu trúc của IIRC lớn hơn. Và nó phải được sao chép xung quanh khá nhiều.

Tôi thường khuyên bạn nên sử dụng một lớp ở đây;) Nhưng không biết nội dung của cấu trúc, nó hơi phức tạp một chút.

+0

Tom cảm ơn bạn đời. Bạn có thể vui lòng cung cấp một số thông tin mà vạch ra lý do tại sao 64 byte là OK cho CPU của tôi? BTW - cấu trúc là lựa chọn tự nhiên để tránh khóa. Chi phí phát triển sẽ cao hơn nhiều nếu tôi sử dụng lớp bất biến hoặc khóa thay thế ... Vì vậy, cho phép vượt qua trên lớp vs struct .. –

+0

Vâng, không có tham chiếu. Tôi jus tthink 64 byte có thể không quá nhiều. Nó thực sự phụ thuộc vào dữ liệu trong đó. Tôi KHÔNG BAO GIỜ không sử dụng cấu trúc như thế. Tôi có một ứng dụng giao dịch tài chính (tấn dữ liệu đi qua - ít, khoảng 64 byte, khoảng 50.000 mục/giây đến) và tôi sử dụng các lớp. – TomTom

+0

Tôi có thể sai về struct vs class ... Nó có thể là giá trị để nhìn vào nó. –

20

Bạn đang misquoting cuốn sách (ít nhất là ấn bản thứ 2). Jeffrey Richter khẳng định các loại giá trị có thể có nhiều hơn 16 byte nếu:

Bạn không có ý định để vượt qua họ để phương pháp khác hoặc sao chép chúng đến và đi từ một lớp bộ sưu tập.

Ngoài Eric Gunnerson thêm (liên quan đến giới hạn 16 byte)

Sử dụng hướng dẫn này như một kích hoạt để làm tra hơn.

Đơn giản là không đúng là cấu trúc "phải nhỏ hơn 16 byte về kích thước". Tất cả phụ thuộc vào cách sử dụng.

Nếu bạn đang tạo cấu trúc và cũng tiêu thụ nó và lo lắng về hiệu suất thì hãy sử dụng trình lược tả so sánh cấu trúc so với lớp để xem cái nào phù hợp nhất với bạn.

+1

Tôi đã nói - nó phải được coi là một thiết kế tốt. Vui lòng trích dẫn toàn bộ câu. Cos từ cách bạn distored từ của tôi là - struct phải được 16 byte hoặc ít hơn ... Không có nó đã không! Tôi đã đọc cuốn sách này hôm qua. Tôi biết Richter đã nói gì và điều này không áp dụng được cho đơn của tôi vì thế tôi chỉ phớt lờ điều này ... Bởi vì chủ đề KHÔNG phải là để thảo luận về cuốn sách nhưng giúp tôi làm cho ứng dụng MY hiệu quả hơn Cảm ơn bạn rất nhiều! Và đối với Gunnerson - đó chính xác là những gì tôi đang cố gắng làm ở đây - điều tra thêm! –

+0

@ Maxima120. Tôi đã không bóp méo lời nói của bạn. Bạn đã đánh giá sai cuốn sách, tôi đã sửa lại bạn. Cuốn sách có trạng thái để tránh các cấu trúc không có kích thước cá thể dưới 16 byte (sau đó thêm cẩn thận). Điều đó rõ ràng không giống với "cấu trúc được thiết kế tốt phải có kích thước nhỏ hơn 16 byte". – RichardOD

1

Tôi nghĩ một điểm quan trọng khác là nếu bạn có danh sách với một triệu cấu trúc, đó vẫn chỉ là một cấp phát bộ nhớ trên heap, trong khi danh sách với một triệu lớp trong nó sẽ có khoảng một triệu đối tượng heap riêng biệt. Tải thu gom rác là một thứ khá khác nhau trong cả hai trường hợp. Từ quan điểm này, cấu trúc có thể đi ra phía trước.

+0

nếu bạn lưu trữ các cấu trúc của bạn trong một đối tượng được phân bổ heap, bạn có thể phân bổ chúng tốt hơn! nếu không mọi thứ trở nên thực sự buồn cười khi stackframe hiện tại của bạn chết (cùng với các đối tượng được phân bổ stack) và bạn cố gắng truy cập chúng thông qua đối tượng được cấp phát đống. Đó là một lý do tại sao tất cả các thành viên (bao gồm cả giá trị đã nhập) được lưu trữ trên heap –

+2

@Rune FS - đúng, nhưng toàn bộ vec-tơ của các cấu trúc sẽ nằm trong một khối bộ nhớ lớn liền kề. Trái ngược với một triệu mảnh nhỏ với những kiếp sống cá nhân. – Tarydon

+0

Điều này đúng với việc triển khai danh sách dựa trên nút. Tuy nhiên, 'System.Collections.Generic.List ' được nội bộ hỗ trợ bởi một 'T []' mà thay đổi kích thước khi cần thiết. –

2

Một trong những điều tôi đã học khi thực hiện lập trình ngôn ngữ lắp ráp là bạn sắp xếp các cấu trúc của bạn (và các dữ liệu khác) trên 8 hoặc 16 byte ranh giới (có thực sự là một lệnh tiền xử lý cho nó trong MASM). Lý do cho điều này là truy cập bộ nhớ nhanh hơn đáng kể khi lưu trữ hoặc di chuyển mọi thứ xung quanh trong bộ nhớ. Việc giữ các cấu trúc của bạn dưới kích thước 16 byte sẽ đảm bảo rằng sự liên kết bộ nhớ này có thể được thực hiện thành công vì không có gì vượt qua các ranh giới liên kết.

Tuy nhiên, tôi hy vọng loại điều này sẽ bị ẩn đi phần lớn khỏi bạn khi sử dụng khuôn khổ .NET. Nếu các cấu trúc của bạn lớn hơn 16 byte thì tôi sẽ mong đợi khung công tác (hoặc trình biên dịch JIT) căn chỉnh dữ liệu của bạn trên các ranh giới 32 byte, và cứ thế. Nó sẽ là thú vị để xem một số xác nhận của các ý kiến ​​trong những cuốn sách, và xem những gì là sự khác biệt về tốc độ.

+1

cách bố trí các cấu trúc không phải là một bí mật. Tìm kiếm thuộc tính LayoutStruct C# .. Ví dụ: http://www.vsj.co.uk/articles/display.asp?id=501 –

+0

Đó là một liên kết tốt.Tôi đã nói nhiều hơn về hành vi của khung công tác/JITter, và tự hỏi tại sao 16 lại là một con số kỳ diệu (và liệu nó có còn là ma thuật hay không hoặc nó đã được tăng hay không liên quan). – slugster

7

Trình biên dịch JIT tạo mã trường hợp đặc biệt để sao chép các cấu trúc nhỏ hơn một ngưỡng nhất định và cấu trúc mục đích chung chậm hơn một chút cho các cấu trúc lớn hơn. Tôi sẽ phỏng đoán rằng khi lời khuyên đó được viết, ngưỡng là 16 byte, nhưng trong khung 32-bit ngày nay, nó có vẻ là 24 byte; nó có thể lớn hơn đối với mã 64 bit.

Điều đó đã được nói, chi phí tạo bất kỳ đối tượng lớp kích thước nào lớn hơn đáng kể so với chi phí sao chép cấu trúc chứa cùng một dữ liệu. Nếu mã tạo ra một đối tượng lớp có giá trị 32 byte và sau đó truyền hoặc sao chép tham chiếu đến đối tượng đó 1.000 lần, thì việc tiết kiệm thời gian từ việc sao chép 1.000 tham chiếu đối tượng thay vì phải sao chép 1.000 cấu trúc 32 byte có khả năng lớn hơn chi phí của tạo đối tượng lớp. Tuy nhiên, nếu thể hiện đối tượng sẽ bị hủy bỏ sau khi tham chiếu đã được sao chép chỉ hai lần, chi phí tạo đối tượng có thể vượt quá một lề lớn chi phí sao chép cấu trúc 32 byte hai lần.

Cũng lưu ý rằng có thể trong nhiều trường hợp để tránh truyền xung quanh các cấu trúc theo giá trị hoặc sao chép dự phòng chúng, nếu một nỗ lực để làm như vậy. Việc chuyển bất kỳ kích thước nào của cấu trúc dưới dạng tham số ref thành một phương thức, ví dụ, chỉ yêu cầu chuyển một địa chỉ một máy (4 hoặc 8 byte). Bởi vì .net thiếu bất kỳ loại khái niệm const ref, chỉ các trường hoặc biến có thể ghi được có thể được chuyển theo cách đó và các bộ sưu tập được tích hợp vào .net - khác với System.Array - không cung cấp phương tiện để truy cập các thành viên theo số ref. Tuy nhiên, nếu một người sẵn sàng sử dụng các mảng hoặc các bộ sưu tập tùy chỉnh, thậm chí các cấu trúc lớn (100 byte hoặc nhiều hơn) có thể được xử lý rất hiệu quả. Trong nhiều trường hợp, lợi thế hiệu suất của việc sử dụng một cấu trúc hơn là một lớp không thay đổi có thể phát triển với kích thước của dữ liệu được đóng gói.

2

Thực ra, trên máy 64 bit 32 byte là kích thước tối đa để sử dụng cho dữ liệu cấu trúc. Từ các thử nghiệm của tôi 32 cấu trúc byte (bốn thời gian) thực hiện cũng như tham chiếu đối tượng.

Bạn có thể tìm mã kiểm tra và chạy thử nghiệm mình ở đây: https://github.com/agileharbor/structBenchmark

Cặp đôi cuối cùng của thử nghiệm sao chép một số lượng lớn các cấu trúc/đối tượng từ một mảng khác, và bạn có thể nhìn thấy nơi lớp tốt hơn cấu trúc với tám longs, nhưng nó là về giống như struct với bốn longs.

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