2015-01-21 15 views
15

Tôi đang viết một chương trình tính toán chuyên sâu với VB.NET 2010 và tôi muốn tối ưu hóa tốc độ. Tôi thấy rằng các toán tử AndAlsoOrElse chậm một cách bất thường nếu kết quả của phép toán được gán cho một biến cấp lớp. Ví dụ, trong khi các báo cáoAndAlso OrElse có thể chậm bất thường

a = _b AndAlso _c 
_a = a 

mất khoảng 6 chu kỳ máy giữa chúng trong exe biên soạn, tuyên bố đơn

_a = _b AndAlso _c 

mất khoảng 80 chu kỳ máy. Ở đây _a, _b_c là biến Boolean riêng của Form1 và các câu lệnh được đề cập trong thủ tục mẫu là Form1, trong đó a là biến Boolean cục bộ.

Tôi không thể tìm thấy lý do tại sao câu lệnh đơn mất quá nhiều thời gian. Tôi đã khám phá nó bằng cách sử NetReflector xuống mức của mã CIL, mà có vẻ tốt:

Instruction    Explanation        Stack 
00: ldarg.0    Push Me (ref to current inst of Form1) Me 
01: ldarg.0    Push Me         Me, Me 
02: ldfld bool Form1::_b Pop Me, read _b and push it    _b, Me 
07: brfalse.s 11   Pop _b; if false, branch to 11   Me 
09: ldarg.0    (_b true) Push Me      Me, Me 
0a: ldfld bool Form1::_c (_b true) Pop Me, read _c and push it _c, Me 
0f: brtrue.s 14   (_b true) Pop _c; if true, branch to 14 Me 
11: ldc.i4.0    (_b, _c not both true) Push result 0  result, Me 
12: br.s 15    Jump unconditionally to 15    result, Me 
----- 
14: ldc.i4.1    (_b, _c both true) Push result 1   result, Me 
15: stfld bool Form1::_a Pop result and Me; write result to _a (empty) 
1a: 

bất cứ ai có thể rụng bất kỳ ánh sáng về lý do tuyên bố _a = _b AndAlso _c mất 80 chu kỳ máy thay vì dự đoán 5 hay như vậy?

Tôi đang sử dụng Windows XP với .NET 4.0 và Visual Studio Express 2010. Tôi đã đo thời gian với đoạn trích thẳng thắn của riêng mình, về cơ bản sử dụng đối tượng Đồng hồ bấm giờ cho thời gian vòng lặp For-Next với 1000 lần lặp mã trong câu hỏi và so sánh nó với một vòng lặp For-Next trống; nó bao gồm một hướng dẫn vô ích trong cả hai vòng để lãng phí một vài chu kỳ và ngăn chặn việc xử lý ngừng. Thô nhưng đủ tốt cho mục đích của tôi.

+0

Xin lỗi nếu nhận xét này có vẻ như một số, nhưng nếu tôi đang tìm tốc độ tính toán và thời gian đo trong chu kỳ - có thể tôi sẽ không sử dụng VB.NET hoặc .NET nói chung. – TyCobb

+0

có thể nếu bạn đăng thêm mã, chúng tôi có thể đưa ra các đề xuất khác để cải thiện hiệu quả. – Jeremy

+0

Bạn đã thử chỉ sử dụng AND? Đặc biệt là kể từ khi bạn đang làm việc với biến Booleans. –

Trả lời

12

Có hai yếu tố ở đây làm cho mã này chậm. Bạn không thể thấy điều này từ IL, chỉ có mã máy mới có thể cung cấp cho bạn thông tin chi tiết.


Đầu tiên là quy tắc chung được liên kết với toán tử AndAlso. Nó là toán tử mạch ngắn, toán hạng bên phải không được đánh giá nếu toán hạng bên trái đánh giá sai. Điều này đòi hỏi một chi nhánh trong mã máy. Phân nhánh là một trong những điều chậm nhất mà bộ vi xử lý có thể thực hiện, phải đoán tại nhánh phía trước để tránh nguy cơ phải tuôn ra đường ống. Nếu nó đoán sai sau đó nó sẽ có một hit perf lớn. Được bao phủ rất tốt trong this post. Mất hoàn toàn thông thường nếu biến số a là rất ngẫu nhiên và chi nhánh do đó được dự đoán thấp, là khoảng 500%.

Bạn tránh rủi ro này bằng cách sử dụng toán tử And thay vào đó, nó không yêu cầu chi nhánh trong mã máy. Nó chỉ là một lệnh duy nhất, AND được thực hiện bởi bộ vi xử lý. Không có lợi ích gì cả AndAlso trong một biểu thức như thế, không có gì sai nếu toán hạng bên phải được đánh giá. Không áp dụng ở đây, nhưng ngay cả khi IL hiển thị một nhánh thì jitter vẫn có thể làm cho mã máy trở nên ít nhánh hơn với lệnh CMOV (di chuyển có điều kiện).


Nhưng điều quan trọng nhất trong trường hợp của bạn là lớp Biểu mẫu kế thừa từ lớp MarshalByRefObject. Chuỗi kế thừa là MarshalByRefObject> Component> Control> ScrollableControl> ContainerControl> Form.

MBRO được xử lý đặc biệt bởi trình biên dịch Just-in-Time, mã có thể đang hoạt động với proxy cho đối tượng lớp với đối tượng thực đang sống trong một AppDomain hoặc một máy khác.Một proxy là minh bạch cho jitter cho hầu như bất kỳ loại thành viên của lớp, chúng được thực hiện như các cuộc gọi phương thức đơn giản. Ngoại trừ các trường, chúng không thể được proxy vì truy cập vào một trường được thực hiện với bộ nhớ đọc/ghi, không phải là một cuộc gọi phương thức. Nếu jitter không thể chứng minh rằng đối tượng là cục bộ thì nó buộc phải gọi vào CLR, sử dụng các phương thức trợ giúp có tên là JIT_GetFieldXxx() và JIT_SetFieldXxx(). CLR biết liệu tham chiếu đối tượng có phải là proxy hay giao dịch thực và giao dịch với sự khác biệt. Các chi phí là khá đáng kể, 80 chu kỳ âm thanh về quyền.

Không có nhiều bạn có thể làm về điều này miễn là các biến là thành viên của lớp Biểu mẫu của bạn. Di chuyển chúng vào một lớp trợ giúp là cách giải quyết.

+0

Rất tuyệt! Cảm ơn sự thông minh của bạn :) – Jeremy

+3

Tuyệt vời! Cảm ơn bạn rất nhiều. Khi tôi di chuyển các biến đến một lớp trợ giúp, lệnh này mất trung bình 3½ chu kỳ thay vì 80. –

+2

Kết quả tốt đẹp. Q + A không thể tốt hơn thế này. Vui lòng cập nhật câu hỏi của bạn và dành một vài từ về cách bạn đo lường, không đủ lập trình viên làm điều này. –

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