2013-01-24 16 views
16

Nói rằng tôi có đoạn mã này:Trình biên dịch có tự động tối ưu hóa các cuộc gọi lặp lại tới các hàm toán học không?

#include <cmath> 

// ... 

float f = rand(); 
std::cout << sin(f) << " " << sin(f); 

Như sin(f) là một chức năng được xác định rõ có một tối ưu hóa dễ dàng:

float f = rand(); 
float sin_f = sin(f); 
std::cout << sin_f << " " << sin_f; 

này một tối ưu hóa mà đó là hợp lý để mong đợi một hiện đại, C là ++ biên dịch tự làm gì? Hoặc không có cách nào để trình biên dịch xác định rằng sin(f) phải luôn trả lại cùng một giá trị cho một giá trị bằng nhau của f?

+2

Trừ khi 'sin' được định nghĩa trong cùng một đơn vị biên dịch, trình biên dịch không biết cách' sin' được triển khai, vì vậy tốt nhất điều này sẽ xảy ra tại thời gian liên kết. – Thomas

+2

@Thomas Không nhất thiết. Một số trình biên dịch xử lý các chức năng của một số tên đặc biệt bởi vì chúng biết chúng được định nghĩa trong thư viện chuẩn, và tiêu chuẩn bảo đảm một cái gì đó về chúng. Ngoài ra còn có các thuộc tính chức năng của trình biên dịch cụ thể mà các tiêu đề do người dùng định nghĩa có thể sử dụng để khai báo chúng một cách thuần túy, mặc dù tôi không biết liệu kiến ​​thức này có được sử dụng để tối ưu hóa hay không. – delnan

+0

Việc tối ưu hóa thực sự được thực hiện với gcc. –

Trả lời

18

Sử dụng g ++ được xây dựng với cờ tối ưu hóa mặc định:

float f = rand(); 
40117e: e8 75 01 00 00   call 4012f8 <_rand> 
401183: 89 44 24 1c    mov %eax,0x1c(%esp) 
401187: db 44 24 1c    fildl 0x1c(%esp) 
40118b: d9 5c 24 2c    fstps 0x2c(%esp) 
std::cout << sin(f) << " " << sin(f); 
40118f: d9 44 24 2c    flds 0x2c(%esp) 
401193: dd 1c 24    fstpl (%esp) 
401196: e8 65 01 00 00   call 401300 <_sin> <----- 1st call 
40119b: dd 5c 24 10    fstpl 0x10(%esp) 
40119f: d9 44 24 2c    flds 0x2c(%esp) 
4011a3: dd 1c 24    fstpl (%esp) 
4011a6: e8 55 01 00 00   call 401300 <_sin> <----- 2nd call 
4011ab: dd 5c 24 04    fstpl 0x4(%esp) 
4011af: c7 04 24 e8 60 40 00 movl $0x4060e8,(%esp) 

Được xây dựng với -O2:

float f = rand(); 
4011af: e8 24 01 00 00   call 4012d8 <_rand> 
4011b4: 89 44 24 1c    mov %eax,0x1c(%esp) 
4011b8: db 44 24 1c    fildl 0x1c(%esp) 
std::cout << sin(f) << " " << sin(f); 
4011bc: dd 1c 24    fstpl (%esp) 
4011bf: e8 1c 01 00 00   call 4012e0 <_sin> <----- 1 call 

Từ điều này chúng ta có thể thấy rằng nếu không tối ưu hóa trình biên dịch sử dụng 2 cuộc gọi và chỉ 1 với tối ưu hóa, theo kinh nghiệm tôi đoán, chúng ta có thể nói trình biên dịch tối ưu hóa t anh ấy gọi.

17

Tôi dấu GCC khá chắc chắn sin với thuộc tính thuần túy phi tiêu chuẩn, tức là __attribute__ ((pure));

này có hiệu lực thi hành sau:

Nhiều chức năng không có tác dụng trừ giá trị trả về và họ trở về giá trị chỉ phụ thuộc vào các tham số và/hoặc các biến toàn cục. Một chức năng như vậy có thể bị loại trừ phổ biến subexpression và tối ưu hóa vòng lặp cũng giống như một toán tử số học sẽ được.

http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

Và như vậy có một cơ hội rất tốt mà các cuộc gọi tinh khiết như vậy sẽ được tối ưu hóa với việc loại bỏ subexpression chung.

(Cập nhật: thực cmath đang sử dụng constexpr, mà ngụ ý tối ưu hóa tương tự)

+0

Chỉ cần thêm vào điều này, MSC biết về một vài chức năng như vậy và có thể tạo mã theo nội tuyến. –

+0

'constexpr' không _not_ hàm ý thuần túy. Có thể thực hiện một 'constexpr' phù hợp ngẫu nhiên ném ngoại lệ cho một số đầu vào nhất định. –

+1

gcc cũng tối ưu hóa nếu bạn chỉ bao gồm không có thuộc tính, nó biết về chúng trong nội bộ, và đánh dấu tội là "const" trừ khi bạn vượt qua -trung toán-toán mà nó chỉ là "thuần". –

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