2015-04-21 27 views
5

Liệu NET trình biên dịch tối ưu hóa này:NET tối ưu hóa giá trị trả về

public MyObject GetNewObject() 
{ 
    var newCurrentObject = myObjectFactory.CreateNew(
           DateTime.Now, 
           "Frank", 
           41, 
           secretPassword); 

    return newCurrentObject; 
} 

để thực hiện với cùng một số hướng dẫn/bộ nhớ như thế này:

public MyObject GetNewObject() 
{ 
    return myObjectFactory.CreateNew(
         DateTime.Now, 
         "Frank", 
         41, 
         secretPassword); 
} 

Hoặc sẽ là kết quả biến địa phương trong thêm thời gian và bộ nhớ được dùng để tạo một tham chiếu (newObject) cho MyObject chỉ phá hủy nó dòng tiếp theo khi nó nằm ngoài phạm vi.

Tôi hỏi vì, hiệu suất đều giống nhau, tôi thấy đầu tiên dễ đọc hơn như tên biến cục bộ thường có thể cung cấp cho nhà phát triển tiếp theo một số ngữ cảnh về những gì chúng tôi đang làm ở đây.

+1

Nó sẽ không thực sự làm một trong hai, bạn không cố gắng trả về 'newObject' trong khối đầu tiên. Và vì nó là kiểu tham chiếu, khi nó trả về 'new MyObject()' trong khối thứ hai, nó sẽ đơn giản trả về một tham chiếu tới phân bổ bộ nhớ có sẵn cho nó, vì vậy nó sẽ không tạo/hủy và thêm biến. –

+1

@AlexeiLevenkov Không phải tất cả các tối ưu hóa. Một số tối ưu hóa được thực hiện bởi trình biên dịch C#. –

+0

Tự hỏi tại sao nhận xét của tôi bị xóa ... Câu hỏi thậm chí nói * Trình biên dịch ** sẽ tối ưu hóa điều này *, vậy tại sao chúng ta nói về JIT thay vì IL? –

Trả lời

6

Giả sử MyObject là loại tham chiếu cùng x86 sẽ được tạo cho cả hai trường hợp. JIT khá giỏi trong việc tối ưu hóa các thời gian và các bài tập vô hướng. Đây là một trong những tối ưu hóa cơ bản nhất có. Khá nhiều trình tối ưu hóa sử dụng biểu mẫu SSA trong nội bộ và tối ưu hóa này gần như rơi ra khỏi biểu mẫu SSA.

Giả sử MyObjectstruct: Tôi đã thử nghiệm rộng rãi .NET 4.5 JIT và RyuJIT mới để tối ưu hóa cấu trúc. .NET JITs thường không tối ưu hóa các bài tập cấu trúc và các biến cục bộ. Mã được dịch theo nghĩa đen trừ một trường hợp nhỏ không áp dụng ở đây. Mong đợi mã máy hoàn toàn theo nghĩa đen. Ngay cả khi bạn nói a = a; hoặc a.x = 1; a.x = 1; bạn nhận được chính xác mã đó như mã máy. Gửi thư cho nhóm nếu cấu trúc quan trọng đối với bạn. Bây giờ vẫn còn thời gian để thực hiện các thay đổi.

3

Đối tượng được trả về theo tham chiếu. Tham chiếu được tạo trong cả hai trường hợp và nó nằm ở trên cùng của ngăn xếp. Nó là một tham chiếu và tham chiếu sau đó được trả về, vì vậy nó là như nhau. Đối tượng không được di chuyển đến một vị trí khác trong heap, do đó tham chiếu không thể thay đổi. Bạn có lẽ sẽ phải nhân bản đối tượng và sau đó trả về tham chiếu đến đối tượng nhân bản. Sau đó, bạn sẽ có một tham chiếu thêm và cũng là một không gian thêm được phân bổ trên heap.

Sự cố duy nhất sẽ phát sinh nếu thay vì MyObject bạn đã sử dụng primitive type, ví dụ: int, nhưng kiểu trả về vẫn sẽ là Đối tượng:

public object BoxingOccurs() 
{ 
    int i = 5; 
    return i; // or shortly return 5; 
} 

Kiểu nguyên thủy sẽ được đóng hộp, có nghĩa là loại giá trị được chuyển thành loại đối tượng. Kiểu nguyên thủy được gói và di chuyển đến vùng được quản lý. Thông tin thêm về boxing và unboxing có thể được tìm thấy here.

+4

Tôi không chắc chắn cách này trả lời câu hỏi. Thịt thực sự của câu hỏi là cửa hàng biến cục bộ sẽ được tối ưu hóa hay không. Không cho dù cùng một tham chiếu được trả về hay sao chép. Cấp OP gây nhầm lẫn từ dòng cuối cùng; nhưng đây không phải là câu trả lời cho câu hỏi. –

+0

Vâng, bạn có một điểm hợp lệ. Điều thực sự xảy ra là câu lệnh (cả hai) tạo một tham chiếu ở trên cùng của ngăn xếp tham chiếu đến cá thể của lớp trong heap và sau đó tham chiếu được trả về. Kịch bản rất có khả năng là sự refence được lưu trữ trong một số registry và nó vẫn ở đó cho đến khi nó hiện không cần thiết cho các tính toán. Tuy nhiên, đây là máy cụ thể và phụ thuộc vào cách MSIL được dịch sang bộ xử lý của bộ xử lý bạn đang sử dụng và cách bộ vi xử lý xử lý các loại hoạt động này. – Santhos

+0

Bạn cũng phải nhận ra rằng có nhiều hơn một tối ưu hóa trong quá trình biên dịch và sau đó diễn giải mã và ngôn ngữ được tạo ra bởi trình biên dịch chỉ là một ngôn ngữ trung gian sau đó được xử lý thêm bởi trình biên dịch JIT dịch nó sang ngôn ngữ máy thời gian chạy. – Santhos

0

newObject là một biến, không phải là tham chiếu (biến chứa tham chiếu đến đối tượng). Tôi đã thử mã (trong chế độ phát hành), và trình biên dịch không tối ưu hóa mã như thế này. Tôi không chắc chắn, nhưng nó thậm chí có thể không được phép bởi đặc điểm kỹ thuật, bởi vì các biến là một phần của sự phản ánh, và điều này có thể ảnh hưởng đến hành vi mong đợi. Tuy nhiên, nó không có nghĩa là mã sẽ không được chạy theo cách đó do JITter, mà có thể tối ưu hóa nó.

+1

* newObject là một biến, không phải là tham chiếu *? Gì? Sau đó, một tài liệu tham khảo là gì? –

+0

@Sriram Một tham chiếu là một loại giá trị đề cập đến một cái gì đó. Trong trường hợp này là một đối tượng. Nó tương tự như con trỏ, nhưng trừu tượng hơn.Mặt khác, một biến là một vị trí lưu trữ có chứa một giá trị. Trong trường hợp này, nó chứa một tham chiếu. – IllidanS4

+0

Nếu bạn thay đổi câu đầu tiên của bạn thành một câu như thế này, nó sẽ không gây hiểu lầm. 'newObject' chỉ là một tham chiếu, không phải là cá thể đối tượng. Tôi thấy nó khó hiểu khi bạn nói nó là một biến không tham chiếu. Thật vậy nó là một tham chiếu. –

4

Nói chung, khi bạn bật tối ưu hóa trong trình biên dịch C# it attempts to minimize the number of temporary/local variable storage slots used in methods. Do đó, bạn không nên quan tâm đến việc cố gắng giảm thiểu số lượng biến vì trình biên dịch sẽ làm điều đó cho bạn. Nếu không có tối ưu hóa, sẽ có một số khác biệt bởi vì mục tiêu có để nâng cao trải nghiệm gỡ lỗi và bảo toàn càng nhiều thông tin càng tốt.

Trong trường hợp cụ thể này, chính xác cùng một IL được phát ra cho cả hai phương pháp khi tối ưu hóa được bật.Dưới đây là một đoạn hoàn chỉnh bao gồm một kiểu giá trị và kiểu tham chiếu để so sánh:

using System; 

public class C { 

    private static MyStruct NewStructLocal() 
    { 
     var s = new MyStruct(); 
     return s; 
    } 

    private static MyStruct NewStructReturn() 
    { 
     return new MyStruct(); 
    } 

    private static MyClass NewClassLocal() 
    { 
     var s = new MyClass(); 
     return s; 
    } 

    private static MyClass NewClassReturn() 
    { 
     return new MyClass(); 
    } 
} 

public struct MyStruct 
{ 
    public int I; 
} 

public class MyClass 
{ 
    public int I; 
} 

Và IL phát ra là:

.method private hidebysig static 
    valuetype MyStruct NewStructLocal() cil managed 
{ 
    // Method begins at RVA 0x2054 
    // Code size 10 (0xa) 
    .maxstack 1 
    .locals init (
     [0] valuetype MyStruct 
    ) 

    IL_0000: ldloca.s 0 
    IL_0002: initobj MyStruct 
    IL_0008: ldloc.0 
    IL_0009: ret 
} // end of method C::NewStructLocal 

.method private hidebysig static 
    valuetype MyStruct NewStructReturn() cil managed 
{ 
    // Method begins at RVA 0x206c 
    // Code size 10 (0xa) 
    .maxstack 1 
    .locals init (
     [0] valuetype MyStruct 
    ) 

    IL_0000: ldloca.s 0 
    IL_0002: initobj MyStruct 
    IL_0008: ldloc.0 
    IL_0009: ret 
} // end of method C::NewStructReturn 

.method private hidebysig static 
    class MyClass NewClassLocal() cil managed 
{ 
    // Method begins at RVA 0x2082 
    // Code size 6 (0x6) 
    .maxstack 8 

    IL_0000: newobj instance void MyClass::.ctor() 
    IL_0005: ret 
} // end of method C::NewClassLocal 

.method private hidebysig static 
    class MyClass NewClassReturn() cil managed 
{ 
    // Method begins at RVA 0x2082 
    // Code size 6 (0x6) 
    .maxstack 8 

    IL_0000: newobj instance void MyClass::.ctor() 
    IL_0005: ret 
} // end of method C::NewClassReturn 

Do đó như xa như hiệu suất là có liên quan, họ sẽ giống hệt nhau.

Trong trường hợp loại tham chiếu, đối tượng được tạo, tham chiếu được đặt ở trên cùng của ngăn xếp đánh giá và ngay lập tức được trả về từ đó.

Trong trường hợp loại giá trị, vị trí lưu trữ cục bộ được khai báo, giá trị ở vị trí lưu trữ được khởi tạo, giá trị được tải lại và sau đó được trả về. Điều này khá điển hình đối với các loại giá trị không phải là loại nguyên thủy.

Một mục tổng quát hơn để suy nghĩ là trong một phương pháp có độ phức tạp đáng kể, trình biên dịch tạo ra rất nhiều thời gian không tên mà không có tên có thể truy cập được cho lập trình viên. Xét về hiệu suất, lo lắng về việc có bao nhiêu biến cục bộ được đặt tên mà bạn có thể thấy trong mã là dành thời gian của bạn lo lắng về điều sai.

Note

Câu hỏi đặt ra đã làm thay đổi phần nào kể từ lần đầu tiên tôi đã viết câu trả lời này, tuy nhiên tôi nghĩ rằng tất cả các phân tích này vẫn đứng. Trong trường hợp cụ thể, cho bạn nhận được chính xác cùng một IL trong cả hai phương pháp khi biên dịch với tối ưu hóa. Biến cục bộ không ảnh hưởng đến IL phát ra. Bây giờ nếu bạn đã thêm nhiều biến cục bộ hơn cho các đối số khác, bạn sẽ có IL khác và sẽ cần phải kiểm tra việc tháo gỡ để xác định mức độ khác biệt đầy đủ.

+0

Đây là một câu trả lời tuyệt vời. Nó vô hiệu hóa của tôi bởi vì tôi quên tài khoản cho tối ưu hóa trình biên dịch C# có thể làm. (Trong thực tế, nhiều câu trả lời của bạn rất có giá trị như tôi vừa để ý.) – usr

+0

@usr Cảm ơn những lời tốt đẹp. Bây giờ tôi thấy rằng câu hỏi đã thay đổi (* thở dài *) nhưng tôi nghĩ phân tích của tôi vẫn đứng vững. –

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