2011-09-22 40 views
7

Tôi đã đọc rằng tiêu chuẩn C++ cho phép tối ưu hóa đến một điểm mà nó thực sự có thể cản trở với chức năng dự kiến. Khi tôi nói điều này, tôi đang nói về tối ưu hóa giá trị trả về, nơi bạn có thể thực sự có một số logic trong hàm tạo bản sao, nhưng trình biên dịch tối ưu hóa cuộc gọi.Câu hỏi về tối ưu hóa trong C++

Tôi thấy điều này là hơi xấu, như trong một người không biết điều này có thể dành một chút thời gian sửa chữa một lỗi do điều này.

Điều tôi muốn biết là liệu có bất kỳ tình huống nào khác khi tối ưu hóa quá mức từ trình biên dịch có thể thay đổi chức năng hay không.

Ví dụ, một cái gì đó như:

int x = 1; 
x = 1; 
x = 1; 
x = 1; 

có thể được tối ưu hóa để một đơn x = 1;

Giả sử tôi đã:

class A; 

A a = b; 
a = b; 
a = b; 

thể này có thể cũng được tối ưu hóa? Có lẽ không phải là ví dụ tốt nhất, nhưng tôi hy vọng bạn hiểu ý tôi chứ ...

+4

Tôi không đồng ý với kết thúc bỏ phiếu. Đây là một câu hỏi thực sự, có thể trả lời được. –

+4

Khi eliding sao chép ctors dẫn đến một lỗi trong mã của bạn, sau đó bạn thiết kế ctor bản sao của bạn sai trong đầu. Mã của bạn không nên phụ thuộc vào số lượng đối tượng xung quanh hoặc tần suất mọi thứ được sao chép/gán. – PlasmaHH

+1

Logic trong ctor sao chép phải là logic để sao chép đối tượng. Nếu nó không được sao chép, thì tại sao sao chép logic ctor cần phải chạy? –

Trả lời

12

hoạt động Eliding bản sao phải là trường hợp duy nhất mà một trình biên dịch được phép tối ưu hóa đến mức tác dụng phụ rõ ràng thay đổi. Không dựa vào các nhà xây dựng bản sao được gọi, trình biên dịch có thể tối ưu hóa các cuộc gọi đó.

Đối với mọi thứ khác, quy tắc "as-if" được áp dụng: Trình biên dịch có thể tối ưu hóa như mong muốn, miễn là các hiệu ứng phụ nhìn thấy được giống như trình biên dịch chưa được tối ưu hóa.

("tác dụng phụ có thể nhìn thấy" bao gồm, ví dụ, công cụ ghi vào giao diện điều khiển hoặc hệ thống tập tin, nhưng không phải thời gian chạy và tốc độ quạt CPU.)

+0

+1: "tác dụng phụ rõ rệt thay đổi". Có thể có giá trị mở rộng về những gì một hiệu ứng phụ có thể nhìn thấy thực sự là. –

+1

Tôi xin lưu ý rằng điều này trước khi các giá trị r được giới thiệu. Điều này cho phép tối ưu hóa. Bây giờ giá trị r tồn tại, các tối ưu hóa đó có lẽ không liên quan (sao chép bản sao vẫn nhanh hơn lời gọi tới hàm khởi tạo, nhưng không phải bởi cùng lề), hành vi được bảo toàn, không phải cho tính tương thích ngược, mà vì nó vẫn cung cấp một lợi ích và mọi người đã học được không sử dụng constructor sao chép cho thủ thuật anyway. –

+0

@Rob: Điều đó có đủ cho khẩu vị của bạn không?) – sbi

1

Điều này sẽ phụ thuộc vào cách class A được thực hiện, cho dù trình biên dịch có thể thấy việc triển khai và liệu nó có đủ thông minh hay không. Ví dụ: nếu operator=() trong class A có một số tác dụng phụ như tối ưu hóa sẽ thay đổi hành vi của chương trình và không thể thực hiện được.

3

Nó có thể được tối ưu hóa, có. Nhưng bạn vẫn có thể kiểm soát quá trình này, ví dụ, mã giả sử:

int x = 1; 
x = 1; 
x = 1; 
x = 1; 
volatile int y = 1; 
y = 1; 
y = 1; 
y = 1; 

Với điều kiện là không x, cũng không phải y được sử dụng bên dưới đoạn này, VS 2010 tạo ra mã:

 
    int x = 1; 
    x = 1; 
    x = 1; 
    x = 1; 
    volatile int y = 1; 
010B1004 xor   eax,eax 
010B1006 inc   eax 
010B1007 mov   dword ptr [y],eax 
    y = 1; 
010B100A mov   dword ptr [y],eax 
    y = 1; 
010B100D mov   dword ptr [y],eax 
    y = 1; 
010B1010 mov   dword ptr [y],eax 

Đó là , tối ưu hóa tất cả các dòng với "x", và để lại tất cả bốn dòng với "y". Đây là cách dễ bay hơi hoạt động, nhưng vấn đề là bạn vẫn có quyền kiểm soát trình biên dịch nào cho bạn.

Cho dù đó là một lớp hay kiểu nguyên thủy - tất cả đều phụ thuộc vào trình biên dịch, mức độ tối ưu hóa của nó là bao nhiêu.

Một đoạn mã để nghiên cứu:

class A 
{ 
private: 
    int c; 

public: 
    A(int b) 
    { 
     *this = b; 
    } 
    A& operator = (int b) 
    { 
     c = b; 
     return *this; 
    } 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    int b = 0; 
    A a = b; 
    a = b; 
    a = b; 
    return 0; 
} 

Visual Studio 2010 tối ưu hóa dải tất cả các mã không có gì, trong phiên bản xây dựng với "đầy đủ tối ưu hóa" _tmain không chỉ không có gì và ngay lập tức trả về zero.

-1

i dont know C++ rằng có rất nhiều nhưng hiện đang đọc Trình biên dịch-Các nguyên tắc, kỹ thuật và các công cụ

đây là một đoạn trích từ phần của họ về tối ưu hóa mã:

mã tối ưu hóa giai đoạn máy độc lập cố gắng cải thiện mã trung gian để mã đích tốt hơn sẽ cho kết quả. Thông thường, có nghĩa là nhanh hơn, nhưng các mục tiêu khác có thể được mong muốn, chẳng hạn như mã ngắn hơn hoặc mã đích tiêu thụ ít năng lượng hơn. ví dụ: thuật toán đơn giản tạo mã trung gian (1.3) bằng cách sử dụng hướng dẫn cho từng toán tử trong biểu diễn cây đến từ trình phân tích ngữ nghĩa. thuật toán mã trung gian đơn giản theo sau là tối ưu hóa mã là một cách hợp lý để tạo mã đích tốt. các optimizar có thể duduce rằng các chuyển đổi của 60 từ số nguyên đến điểm nổi có thể được thực hiện một lần và cho tất cả tại thời gian biên dịch, vì vậy hoạt động inttofloat có thể được loại bỏ bằng cách thay thế các số nguyên 6 của số điểm nổi 60.0. t3 hơn nữa chỉ được sử dụng một lần để trasmit giá trị của nó để ID1 nên ưu có thể chuyển đổi 1.3 vào chuỗi ngắn hơn (1,4)

1.3 
t1 - intoffloat(60 
t2 -- id3 * id1 
ts -- id2 + t2 
id1 t3 

1.4 
t1=id3 * 60.0 
id1 = id2 + t1 

tất cả và tất cả tôi muốn nói rằng mã tối ưu hóa nên đến ở mức độ sâu sắc hơn nhiều và vì mã ở trạng thái đơn giản như vậy không ảnh hưởng đến mã của bạn, hãy thực hiện những gì mà mã của bạn thực hiện:

0

Tối ưu hóa không (trong thời hạn thích hợp) "xóa cuộc gọi để sao chép hoặc gán". Nó chuyển đổi một máy trạng thái hữu hạn ở trạng thái hữu hạn khác, máy có cùng hành vi bên ngoài.

Bây giờ, nếu bạn repeadly gọi

a=b; a=b; a=b; 

gì trình biên dịch làm phụ thuộc vào những gì thực sự là operator=. Nếu trình biên dịch phát hiện ra rằng một cuộc gọi không có cơ hội thay đổi trạng thái của chương trình (và "trạng thái của chương trình" là "mọi thứ sống lâu hơn phạm vi mà phạm vi có thể truy cập") nó sẽ loại bỏ nó. Nếu điều này không thể được "chứng minh" thì cuộc gọi sẽ được giữ nguyên.

Bất kể trình biên dịch sẽ làm gì, đừng lo lắng quá nhiều về: trình biên dịch không thể (theo hợp đồng) thay đổi logic bên ngoài của một chương trình hoặc một phần của nó.

-1

Tôi gặp sự cố với các biến const và const_cast. Trình biên dịch tạo ra kết quả không chính xác khi nó được sử dụng để tính toán cái gì khác. Biến const được tối ưu hóa, giá trị cũ của nó được tạo thành hằng số biên dịch. "Hành vi bất ngờ" thực sự.Được rồi, có lẽ không;)

Ví dụ:

const int x = 2; 
const_cast<int&>(x) = 3; 
int y = x * 2; 
cout << y << endl; 
+0

Không có gì về điều này là bất ngờ. 'const_cast' chỉ để bỏ' '' '' '' 'của tham chiếu hoặc con trỏ, để gọi đối tượng không được khai báo' const' (ví dụ: khi giao dịch với một API xấu hoặc lấy một phím tắt xác định cặp lệnh 'operator'). Ngược lại, tiêu chuẩn rất cố ý nói rằng bỏ đi 'const'ness của một biến mà ban đầu được khai báo' const' là hành vi không xác định. –