2017-01-29 18 views
9

Giả sử tôi đã chương trình sau đây:hành vi Weird của C# biên dịch bộ nhớ đệm do đại biểu

static void SomeMethod(Func<int, int> otherMethod) 
{ 
    otherMethod(1); 
} 

static int OtherMethod(int x) 
{ 
    return x; 
} 

static void Main(string[] args) 
{ 
    SomeMethod(OtherMethod); 
    SomeMethod(x => OtherMethod(x)); 
    SomeMethod(x => OtherMethod(x)); 
} 

Tôi không thể hiểu biên dịch mã il (nó sử dụng quá thêm code). Đây là phiên bản được đơn giản hóa:

class C 
{ 
    public static C c; 
    public static Func<int, int> foo; 
    public static Func<int, int> foo1; 
    static C() 
    { 
     c = new C(); 
    } 
    C(){} 
    public int b(int x) 
    { 
     return OtherMethod(x); 
    } 
    public int b1(int x) 
    { 
     return OtherMethod(x); 
    } 
} 

static void Main() 
{ 
    SomeMethod(new Func<int, int>(OtherMethod)); 
    if (C.foo != null) 
     SomeMethod(C.foo) 
    else 
    { 
     C.foo = new Func<int, int>(c, C.b) 
     SomeMethod(C.foo); 
    } 
    if (C.foo1 != null) 
     SomeMethod(C.foo1) 
    else 
    { 
     C.foo1 = new Func<int, int>(c, C.b1) 
     SomeMethod(C.foo1); 
    } 
} 

Tại sao trình biên dịch tạo ra phương thức không bằng nhau b/b1? Bình đẳng có nghĩa là họ có cùng mã

+0

SomeMethod (x => OtherMethod (x)); cũng có kết quả với 1 – becike

+0

@becike, vâng, tất nhiên. Tôi đồng ý với bạn. Nhưng việc triển khai có vẻ lạ lùng – LmTinyToon

+0

nếu không tôi không chắc chắn bạn muốn mã này làm gì, vì vậy bạn thiếu một vài đoạn – becike

Trả lời

17

Câu hỏi của bạn là: tại sao các trình biên dịch không nhận ra rằng hai dòng

SomeMethod(x => OtherMethod(x)); 
SomeMethod(x => OtherMethod(x)); 

đều giống nhau và viết những dòng này như

if (delegate is not created) 
    create the delegate and stash it away 
SomeMethod(the delegate); 
SomeMethod(the delegate); 

? Vâng, hãy để tôi trả lời câu hỏi đó theo nhiều cách.

Trước hết, trình biên dịch có được phép để thực hiện tối ưu hóa đó không? Vâng. Các đặc điểm kỹ thuật gọi ra rằng một trình biên dịch C# là cho phép để làm cho hai lambdas làm chính xác cùng một điều vào một đại biểu duy nhất. Và trên thực tế, bạn có thể thấy rằng nó đã thực hiện tối ưu hóa một phần này: nó tạo mỗi đại biểu một lần và lưu nó đi để không phải tạo lại sau khi mã được gọi lại. Lưu ý rằng đây là một sự lãng phí bộ nhớ trong trường hợp mã chỉ được gọi một lần.

Thứ hai, là trình biên dịch yêu cầu để tối ưu hóa bộ nhớ đệm? Không. Đặc điểm kỹ thuật chỉ ra rằng trình biên dịch chỉ được cho phép thực hiện tối ưu hóa, nhưng không được yêu cầu.

Trình biên dịch có yêu cầu để thực hiện tối ưu hóa bạn muốn không? Rõ ràng là không, bởi vì nó không. Đó là được phép và có thể là phiên bản tương lai của trình biên dịch. Trình biên dịch là mã nguồn mở; nếu bạn quan tâm đến việc tối ưu hóa này, hãy viết nó và gửi yêu cầu kéo.

Thứ ba, có phải là có thể để thực hiện tối ưu hóa bạn muốn không? Vâng. Trình biên dịch có thể lấy tất cả các cặp lambdas xuất hiện trong cùng một phương thức, biên dịch chúng sang định dạng cây bên trong và so sánh cây để xem chúng có cùng nội dung hay không và sau đó tạo ra cùng một trường sao lưu tĩnh cho cả hai.

Vì vậy, bây giờ chúng tôi có một tình huống: trình biên dịch là được phép để thực hiện tối ưu hóa cụ thể và không. Và bạn đã hỏi "tại sao không"? Đó là câu hỏi dễ trả lời: tất cả các tối ưu hóa không được triển khai cho đến khi ai đó dành thời gian và nỗ lực đáng kể cho:

  • Thiết kế cẩn thận tối ưu hóa: trong điều kiện tối ưu hóa được kích hoạt và không được kích hoạt? Làm thế nào chung nên tối ưu hóa được? Bạn đã đề nghị rằng họ phát hiện các cơ quan lambda tương tự nhưng tại sao dừng lại ở đó? Bạn có hai câu lệnh giống hệt nhau, vậy tại sao không tạo mã cho những tuyên bố này một lần thay vì hai lần? Điều gì sẽ xảy ra nếu bạn có nhóm báo cáo lặp đi lặp lại?Có một số lượng lớn công việc thiết kế để làm ở đây.
  • Cụ thể, một khía cạnh quan trọng của thiết kế là: người dùng có thể thực hiện tối ưu hóa một cách hợp lý "bằng tay" trong khi vẫn giữ mã có thể đọc được hay không. Trong trường hợp này, có họ có thể, dễ dàng. Chỉ cần gán lambda trùng lặp cho một biến và sau đó sử dụng biến. Việc tối ưu hóa tự động thực hiện điều gì đó mà người dùng quan tâm có thể thực hiện dễ dàng không thực sự là một tối ưu hóa rất thú vị hoặc hấp dẫn.
  • Ví dụ của bạn là tầm thường; mã thế giới thực thì không. Thiết kế được đề xuất của bạn làm gì với cùng một số lồng nhau lambdas? Và cứ thế.
  • Tối ưu hóa của bạn có gây ra hành vi của mã trong trình gỡ rối "trông lạ" không? Bạn có thể nhận thấy rằng khi gỡ lỗi mã được biên dịch với tối ưu hóa được bật, trình gỡ lỗi có vẻ cư xử kỳ lạ; đó là vì không còn ánh xạ rõ ràng giữa mã được tạo và mã ban đầu nữa. Tối ưu hóa của bạn có tệ hơn không? Người dùng có chấp nhận được không? Trình gỡ lỗi có cần phải biết về tối ưu hóa không? Nếu vậy, bạn sẽ phải thay đổi trình gỡ lỗi. Trong trường hợp này, có thể không, nhưng đây là những câu hỏi mà bạn phải hỏi và trả lời.
  • Nhận thiết kế được các chuyên gia đánh giá; điều này sẽ mất thời gian của họ và có thể dẫn đến những thay đổi đối với thiết kế
  • Đưa ra ước tính về ưu và khuyết điểm của tối ưu hóa - tối ưu hóa thường có chi phí ẩn, như rò rỉ bộ nhớ mà tôi đã đề cập trước đây. Cụ thể, tối ưu hóa thường là ngăn cản các tối ưu hóa khác có thể tốt hơn.
  • Đưa ra ước tính là tổng số tiền tiết kiệm trên toàn thế giới của tối ưu hóa này. Tối ưu hóa có thực sự ảnh hưởng đến mã thế giới thực không? Liệu nó có thay đổi độ chính xác của mã đó không? Có bất kỳ mã sản xuất nào, ở bất kỳ nơi nào trên thế giới, có thể phá vỡ tối ưu hóa này và khiến CTO của công ty X gọi cho CTO của Microsoft yêu cầu sửa chữa không? Nếu câu trả lời là có thì có thể bạn có thể không muốn tối ưu hóa điều này. C# không phải là đồ chơi. Hàng triệu và hàng triệu người phụ thuộc vào hoạt động chính xác của nó mỗi ngày.
  • Gánh nặng ước tính khi thực hiện tối ưu hóa trên thời gian biên dịch là là gì? Biên dịch không phải xảy ra giữa các lần nhấn phím nhưng nó phải khá nhanh. Bất cứ điều gì mà giới thiệu một thuật toán siêu tuyến tính trong một đường dẫn mã phổ biến trong trình biên dịch sẽ không thể chấp nhận được. Bạn có thể triển khai tối ưu hóa của mình để nó có tuyến tính ở kích thước mã không? Lưu ý rằng thuật toán tôi đã phác thảo trước đây - so sánh tất cả các cặp - là siêu tuyến tính trong kích thước mã. (Bài tập: hiệu quả tiệm cận xấu nhất của việc so sánh cây trên tất cả các cặp lambdas là gì?)
  • Thực sự thực hiện tối ưu hóa. Tôi khuyến khích bạn làm như vậy.
  • Kiểm tra tối ưu hóa; nó thực sự tạo ra mã tốt hơn? Về số liệu nào? Việc tối ưu hóa không gây ra thay đổi đối với bất kỳ số liệu nào không phải là tối ưu hóa.
  • Đăng ký để khắc phục lỗi trong tối ưu hóa vĩnh viễn.

Tối ưu hóa bạn muốn đơn giản là không đáp ứng thanh. Không ai viết mã như thế. Nếu họ đã làm, và họ quan tâm rằng nó nhân bản một đối tượng, họ có thể dễ dàng tự sửa chữa nó. Vì vậy, tối ưu hóa tối ưu hóa mã không tồn tại, để có được một "chiến thắng" đó là việc xây dựng một đối tượng duy nhất trong số hàng triệu và hàng triệu đối tượng mà chương trình sẽ phân bổ. Không đáng.

Nhưng một lần nữa, nếu bạn nghĩ, hãy tiếp tục và triển khai và gửi yêu cầu kéo. Hãy chắc chắn để gửi kết quả của các cuộc điều tra tôi đã lưu ý ở trên, bởi vì đó là nơi mà công việc thực sự là. Việc triển khai thường là phần nhỏ nhất trong tổng nỗ lực dành cho một đối tượng địa lý; đó là lý do tại sao C# là một ngôn ngữ thành công.

+1

Câu trả lời hay! Tôi hoàn toàn đồng ý. – LmTinyToon

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