2013-07-16 24 views
14

Xét các chương trình thử nghiệm sau đây:Tại sao dereferencing làm cho chương trình của tôi nhanh hơn?

Loop value on the stack

int main(void) { 
    int iterations = 1000000000; 

    while (iterations > 0) 
     -- iterations; 
} 

Loop value on the stack (dereferenced)

int main(void) { 
    int iterations = 1000000000; 
    int * p = & iterations; 

    while (* p > 0) 
     -- * p; 
} 

Loop value on the heap

#include <stdlib.h> 

int main(void) { 
    int * p = malloc(sizeof(int)); 
    * p = 1000000000; 

    while (*p > 0) 
     -- * p; 
} 

Bằng biên soạn chúng với -O0, tôi nhận được t anh sau thời gian thực hiện:

case1.c 
real 0m2.698s 
user 0m2.690s 
sys  0m0.003s 

case2.c 
real 0m2.574s 
user 0m2.567s 
sys  0m0.000s 

case3.c 
real 0m2.566s 
user 0m2.560s 
sys  0m0.000s 

[sửa] Sau đây là mức trung bình trên 10 hành:

case1.c 
2.70364 

case2.c 
2.57091 

case3.c 
2.57000 

Tại sao thời gian thực hiện lớn hơn với các trường hợp thử nghiệm đầu tiên, mà có vẻ là đơn giản nhất?

Kiến trúc hiện tại của tôi là máy ảo x86 (Archlinux). Tôi nhận được những kết quả này cả với gcc (4.8.0) và clang (3.3).

[sửa 1] Mã lắp ráp được tạo gần như giống hệt nhau ngoại trừ mã thứ hai và thứ ba có nhiều hướng dẫn hơn mã đầu tiên.

[sửa 2] Những màn trình diễn này có thể tái sản xuất (trên hệ thống của tôi). Mỗi lần thực hiện sẽ có cùng thứ tự độ lớn.

[sửa] Tôi không thực sự quan tâm đến màn trình diễn của một chương trình không được tối ưu hóa, nhưng tôi không hiểu tại sao nó lại chậm hơn và tôi tò mò.

+9

Bạn đã thử xem mã được tạo chưa? Tại sao bạn quan tâm đến hiệu suất của mã chưa được tối ưu hóa? –

+8

Hae bạn đã thử chạy chúng theo một thứ tự khác? Có phải những lần phát một lần này, hay trung bình trên một số lượng đáng kể các lần chạy? – EJP

+0

@CarlNorum Hầu như cùng một mã được tạo, ngoại trừ việc có thêm hướng dẫn (di chuyển và tải) trong hai ví dụ cuối cùng. Tôi không quan tâm đến màn trình diễn, nhưng tôi vẫn tò mò :) –

Trả lời

6

Thật khó để nói nếu đây là lý do vì tôi đang làm một số đoán và bạn đã không đưa ra một số chi tiết cụ thể (như mục tiêu bạn đang sử dụng). Nhưng những gì tôi thấy khi biên dịch mà không optimziations với một mục tiêu x86 là trình tự sau đây cho decrementign các iterations biến:

Trường hợp 1:

L3: 
    sub DWORD PTR [esp+12], 1 
L2: 
    cmp DWORD PTR [esp+12], 0 
    jg L3 

Trường hợp 2:

L3: 
    mov eax, DWORD PTR [esp+12] 
    mov eax, DWORD PTR [eax] 
    lea edx, [eax-1] 
    mov eax, DWORD PTR [esp+12] 
    mov DWORD PTR [eax], edx 
L2: 
    mov eax, DWORD PTR [esp+12] 
    mov eax, DWORD PTR [eax] 
    test eax, eax 
    jg L3 

Một sự khác biệt lớn mà bạn thấy trong trường hợp 1 là hướng dẫn tại L3 đọc và ghi vị trí bộ nhớ. Nó được theo sau ngay lập tức bởi một hướng dẫn đọc cùng một vị trí bộ nhớ vừa được viết. Loại trình tự hướng dẫn này (cùng một vị trí bộ nhớ được viết ngay lập tức được sử dụng trong lệnh tiếp theo) thường gây ra một số loại gian hàng đường ống trong các CPU hiện đại.

Bạn sẽ lưu ý ghi ngay sau đó bởi một chi của cùng một vị trí không có mặt trong trường hợp 2.

Again - câu trả lời này là một chút suy đoán thông tin.

+0

Bạn đã thử nghiệm của bạn vì mã asm của bạn khác với mã OP của – aaronman

+0

OP có vẻ là một từ Clang. Tôi có cùng asm với gcc 4.8. – snf

+2

@snf sự thật là nghi ngờ câu hỏi này sẽ có một câu trả lời đủ thú vị cho bất cứ ai để được hài lòng với – aaronman

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