Có ai biết câu trả lời và / hoặc có ý kiến về điều này?
Vì các bộ giá trị thường không lớn lắm, nên tôi cho rằng sẽ hợp lý hơn khi sử dụng các cấu trúc hơn là các lớp cho các bộ này. Bạn nói gì?
Có ai biết câu trả lời và / hoặc có ý kiến về điều này?
Vì các bộ giá trị thường không lớn lắm, nên tôi cho rằng sẽ hợp lý hơn khi sử dụng các cấu trúc hơn là các lớp cho các bộ này. Bạn nói gì?
Câu trả lời:
Microsoft đã tạo ra tất cả các kiểu tham chiếu tuple type vì sự đơn giản.
Cá nhân tôi nghĩ rằng đây là một sai lầm. Các bộ dữ liệu có nhiều hơn 4 trường là rất bất thường và dù sao cũng nên được thay thế bằng một kiểu thay thế khác (chẳng hạn như loại bản ghi trong F #), vì vậy chỉ những bộ mã nhỏ mới được quan tâm thực tế. Các điểm chuẩn của riêng tôi cho thấy rằng các bộ không đóng hộp lên đến 512 byte vẫn có thể nhanh hơn các bộ đóng hộp.
Mặc dù hiệu quả bộ nhớ là một mối quan tâm, nhưng tôi tin rằng vấn đề nổi trội là chi phí của bộ thu gom rác .NET. Việc phân bổ và thu thập rất tốn kém trên .NET vì trình thu gom rác của nó chưa được tối ưu hóa nhiều (ví dụ như so với JVM). Hơn nữa, .NET GC (máy trạm) mặc định vẫn chưa được song song hóa. Do đó, các chương trình song song sử dụng các bộ giá trị bị dừng lại vì tất cả các lõi đều tranh giành bộ thu gom rác dùng chung, phá hủy khả năng mở rộng. Đây không chỉ là mối quan tâm chính mà AFAIK, đã hoàn toàn bị Microsoft bỏ qua khi họ kiểm tra vấn đề này.
Một mối quan tâm khác là công văn ảo. Các kiểu tham chiếu hỗ trợ các kiểu con và do đó, các thành viên của chúng thường được gọi thông qua công văn ảo. Ngược lại, các kiểu giá trị không thể hỗ trợ các kiểu con nên việc gọi thành viên là hoàn toàn không rõ ràng và luôn có thể được thực hiện như một lời gọi hàm trực tiếp. Điều phối ảo là cực kỳ đắt trên phần cứng hiện đại vì CPU không thể dự đoán nơi bộ đếm chương trình sẽ kết thúc. JVM sử dụng rất nhiều thời gian để tối ưu hóa việc gửi ảo nhưng .NET thì không. Tuy nhiên, .NET cung cấp một lối thoát khỏi điều phối ảo dưới dạng các loại giá trị. Vì vậy, việc biểu diễn các bộ giá trị dưới dạng các loại giá trị có thể đã cải thiện đáng kể hiệu suất ở đây. Ví dụ, gọiGetHashCode
trên 2 tuple một triệu lần mất 0,17 giây nhưng gọi nó trên cấu trúc tương đương chỉ mất 0,008 giây, tức là kiểu giá trị nhanh hơn 20 lần so với kiểu tham chiếu.
Một tình huống thực tế mà các vấn đề về hiệu suất với các bộ giá trị thường phát sinh là trong việc sử dụng các bộ giá trị làm khóa trong từ điển. Tôi thực sự tình cờ tìm thấy chủ đề này bằng cách theo một liên kết từ câu hỏi Stack Overflow F # chạy thuật toán của tôi chậm hơn Python! trong đó chương trình F # của tác giả hóa ra lại chậm hơn so với Python của anh ta chính xác vì anh ta đang sử dụng các bộ giá trị đóng hộp. Việc mở hộp theo cách thủ công bằng struct
kiểu viết tay khiến chương trình F # của anh ấy nhanh hơn nhiều lần và nhanh hơn Python. Những vấn đề này sẽ không bao giờ phát sinh nếu các bộ giá trị được đại diện bởi các loại giá trị chứ không phải các loại tham chiếu để bắt đầu bằng ...
Tuple<_,...,_>
loại có thể đã được niêm phong, trong trường hợp đó, không cần công văn ảo mặc dù là các loại tham chiếu. Tôi tò mò hơn về lý do tại sao chúng không được niêm phong hơn là tại sao chúng là loại tham chiếu.
Lý do rất có thể là vì chỉ các bộ giá trị nhỏ hơn mới có ý nghĩa như các kiểu giá trị vì chúng sẽ có dấu vết bộ nhớ nhỏ. Các bộ giá trị lớn hơn (tức là các bộ có nhiều thuộc tính hơn) sẽ thực sự bị ảnh hưởng về hiệu suất vì chúng sẽ lớn hơn 16 byte.
Thay vì có một số bộ giá trị là các loại giá trị và những bộ khác là các loại tham chiếu và buộc các nhà phát triển phải biết đó là loại mà tôi sẽ tưởng tượng, những người ở Microsoft nghĩ rằng việc tạo ra tất cả các loại tham chiếu đơn giản hơn.
Ah, những nghi ngờ đã được xác nhận! Vui lòng xem Building Tuple :
Quyết định quan trọng đầu tiên là xem có nên coi các bộ giá trị như một loại tham chiếu hoặc giá trị hay không. Vì chúng không thay đổi được bất cứ khi nào bạn muốn thay đổi các giá trị của một bộ giá trị, bạn phải tạo một bộ giá trị mới. Nếu chúng là các kiểu tham chiếu, điều này có nghĩa là có thể có nhiều rác được tạo ra nếu bạn đang thay đổi các phần tử trong một bộ theo một vòng lặp chặt chẽ. Bộ giá trị F # là các loại tham chiếu, nhưng có cảm giác từ nhóm rằng họ có thể nhận ra sự cải thiện hiệu suất nếu hai và có lẽ ba, bộ giá trị thay thế là các loại giá trị. Một số nhóm đã tạo các bộ giá trị nội bộ đã sử dụng giá trị thay vì các loại tham chiếu, vì các kịch bản của họ rất nhạy cảm với việc tạo nhiều đối tượng được quản lý. Họ nhận thấy rằng việc sử dụng một loại giá trị mang lại cho họ hiệu suất tốt hơn. Trong bản nháp đầu tiên của chúng tôi về đặc điểm kỹ thuật tuple, chúng tôi đã giữ các bộ giá trị hai, ba và bốn phần tử làm kiểu giá trị, với phần còn lại là kiểu tham chiếu. Tuy nhiên, trong một cuộc họp thiết kế bao gồm các đại diện từ các ngôn ngữ khác, người ta đã quyết định rằng thiết kế "tách rời" này sẽ gây nhầm lẫn, do ngữ nghĩa hơi khác nhau giữa hai loại. Sự nhất quán trong hành vi và thiết kế được xác định là ưu tiên cao hơn so với việc tăng hiệu suất tiềm năng. Dựa trên đầu vào này, chúng tôi đã thay đổi thiết kế để tất cả các bộ giá trị đều là loại tham chiếu, mặc dù chúng tôi đã yêu cầu nhóm F # thực hiện một số điều tra hiệu suất để xem liệu nó có bị tăng tốc khi sử dụng một loại giá trị cho một số kích thước của bộ giá trị hay không. Nó có một cách tốt để kiểm tra điều này, vì trình biên dịch của nó, được viết bằng F #, là một ví dụ điển hình về một chương trình lớn sử dụng các bộ giá trị trong nhiều tình huống khác nhau. Cuối cùng, nhóm F # nhận thấy rằng nó không cải thiện được hiệu suất khi một số bộ giá trị là kiểu giá trị thay vì kiểu tham chiếu. Điều này khiến chúng tôi cảm thấy tốt hơn về quyết định sử dụng các loại tham chiếu cho tuple.
Nếu các loại .NET System.Tuple <...> được định nghĩa là cấu trúc, chúng sẽ không thể mở rộng. Ví dụ: một bộ ba số nguyên dài hiện có tỷ lệ như sau:
type Tuple3 = System.Tuple<int64, int64, int64>
type Tuple33 = System.Tuple<Tuple3, Tuple3, Tuple3>
sizeof<Tuple3> // Gets 4
sizeof<Tuple33> // Gets 4
Nếu bộ ba bậc ba được định nghĩa là một cấu trúc, kết quả sẽ như sau (dựa trên một ví dụ thử nghiệm mà tôi đã triển khai):
sizeof<Tuple3> // Would get 32
sizeof<Tuple33> // Would get 104
Vì các bộ giá trị có hỗ trợ cú pháp tích hợp trong F # và chúng được sử dụng cực kỳ thường xuyên trong ngôn ngữ này, các bộ giá trị "struct" sẽ khiến các lập trình viên F # có nguy cơ viết các chương trình kém hiệu quả mà không hề hay biết. Nó sẽ xảy ra rất dễ dàng:
let t3 = 1L, 2L, 3L
let t33 = t3, t3, t3
Theo ý kiến của tôi, các bộ giá trị "struct" sẽ gây ra khả năng cao là tạo ra sự kém hiệu quả đáng kể trong lập trình hàng ngày. Mặt khác, các bộ giá trị "lớp" hiện đang tồn tại cũng gây ra sự kém hiệu quả nhất định, như @Jon đã đề cập. Tuy nhiên, tôi nghĩ rằng tích của "xác suất xảy ra" nhân với "thiệt hại tiềm ẩn" với các cấu trúc sẽ cao hơn nhiều so với hiện tại với các lớp. Do đó, việc thực hiện hiện tại là ít ác hơn.
Lý tưởng nhất là sẽ có cả bộ giá trị "class" và bộ giá trị "struct", cả hai đều có hỗ trợ cú pháp trong F #!
Chỉnh sửa (2017-10-07)
Các bộ dữ liệu cấu trúc hiện được hỗ trợ đầy đủ như sau:
ref
hoặc có thể không thích thực tế là không phải là "cấu trúc bất biến", đặc biệt là khi được đóng hộp. Thật tệ .net không bao giờ triển khai khái niệm truyền tham số bởi một thực thi const ref
, vì trong nhiều trường hợp, ngữ nghĩa như vậy là thứ thực sự cần thiết.
Dictionary
, ví dụ: tại đây: stackoverflow.com/questions/5850243 /…
Đối với 2 bộ giá trị, bạn vẫn luôn có thể sử dụng KeyValuePair <TKey, TValue> từ các phiên bản trước của Hệ thống loại chung. Đó là một kiểu giá trị.
Một sự làm rõ nhỏ đối với bài viết của Matt Ellis là sự khác biệt về ngữ nghĩa sử dụng giữa các kiểu tham chiếu và giá trị chỉ là "nhỏ" khi tính bất biến có hiệu lực (tất nhiên, sẽ là trường hợp ở đây). Tuy nhiên, tôi nghĩ tốt nhất là trong thiết kế BCL không đưa ra sự nhầm lẫn khi để Tuple chuyển qua một loại tham chiếu ở một số ngưỡng.
Tôi không biết nhưng nếu bạn đã từng sử dụng F # Tuples là một phần của ngôn ngữ. Nếu tôi tạo .dll và trả về một loại Tuples, thật tuyệt khi có một loại để đưa nó vào. Tôi nghi ngờ rằng F # là một phần của ngôn ngữ (.Net 4), một số sửa đổi đối với CLR đã được thực hiện để phù hợp với một số cấu trúc phổ biến trong F #
Từ http://en.wikibooks.org/wiki/F_Sharp_Programming/Tuples_and_Records
let scalarMultiply (s : float) (a, b, c) = (a * s, b * s, c * s);;
val scalarMultiply : float -> float * float * float -> float * float * float
scalarMultiply 5.0 (6.0, 10.0, 20.0);;
val it : float * float * float = (30.0, 50.0, 100.0)
ValueTuple<...>
. Xem tài liệu tham khảo tại C # các loại tuple