2015-04-24 19 views
14

Tôi đang cố gắng để tính toán thời gian cần để tính toán sqrt bằng mã C đơn giản sau đây, trong đó readTSC() là một hàm đọc bộ đếm chu trình của CPU.Tại sao trình biên dịch tạo thêm sqrts trong mã assembly được biên dịch

double sum = 0.0; 
int i; 
tm = readTSC(); 
for (i = 0; i < n; i++) 
    sum += sqrt((double) i); 
tm = readTSC() - tm; 
printf("%lld clocks in total\n",tm); 
printf("%15.6e\n",sum); 

Tuy nhiên, như tôi đã in ra mã assembly bằng

gcc -S timing.c -o timing.s 

trên một máy Intel, kết quả (hình dưới đây) là đáng ngạc nhiên?

Tại sao có hai sqrts trong mã lắp ráp bằng một mã sử dụng lệnh sqrtsd và lệnh còn lại bằng cách sử dụng lệnh gọi hàm? Có liên quan đến việc bỏ vòng lặp và cố thực thi hai sqrts trong một lần lặp không?

Và làm thế nào để hiểu được dòng

ucomisd %xmm0, %xmm0 

Tại sao nó so sánh %xmm0 cho chính nó?

//----------------start of for loop---------------- 
call readTSC 
movq %rax, -32(%rbp) 
movl $0, -4(%rbp) 
jmp .L4 
.L6: 
cvtsi2sd -4(%rbp), %xmm1 
// 1. use sqrtsd instruction 
sqrtsd %xmm1, %xmm0 
ucomisd %xmm0, %xmm0 
jp .L8 
je .L5 
.L8: 
movapd %xmm1, %xmm0 
// 2. use C funciton call 
call sqrt 
.L5: 
movsd -16(%rbp), %xmm1 
addsd %xmm1, %xmm0 
movsd %xmm0, -16(%rbp) 
addl $1, -4(%rbp) 
.L4: 
movl -4(%rbp), %eax 
cmpl -36(%rbp), %eax 
jl .L6 
//----------------end of for loop---------------- 
call readTSC 
+3

Đó là mã chưa được tối ưu hóa. [Real code] (http://goo.gl/CewylI) đưa ra các nhánh đúng cách (không có nhánh nào trong trường hợp không phải là NaN), và loại bỏ 'je' vì nó sẽ luôn đúng sau một' ucomisd '. –

Trả lời

23

Sử dụng thư viện sqrt để xử lý lỗi. Xem tài liệu của glibc: 20.5.4 Error Reporting by Mathematical Functions: các hàm toán được đặt errno để tương thích với các hệ thống không có cờ ngoại lệ IEEE754. Tags: glibc's math_error(7) trang người đàn ông.

Là một tối ưu hóa, đầu tiên nó sẽ cố gắng để thực hiện các căn bậc hai của sqrtsd hướng dẫn inlined, sau đó kiểm tra kết quả chống lại bản thân bằng cách sử dụng hướng dẫn ucomisd mà đặt những lá cờ như sau:

CASE (RESULT) OF 
    UNORDERED: ZF,PF,CF 111; 
    GREATER_THAN: ZF,PF,CF 000; 
    LESS_THAN: ZF,PF,CF 001; 
    EQUAL:  ZF,PF,CF 100; 
ESAC; 

Cụ thể, so sánh một số QNaN với chính nó sẽ trả lại UNORDERED, đó là những gì bạn sẽ nhận được nếu bạn cố lấy căn bậc hai của một số âm. Điều này được bao phủ bởi chi nhánh jp. Kiểm tra je chỉ là hoang tưởng, kiểm tra sự bình đẳng chính xác.


Cũng lưu ý rằng gcc có -fno-math-errno option mà sẽ hy sinh xử lý cho tốc độ lỗi này. Tùy chọn này là một phần của -ffast-math, nhưng có thể được sử dụng riêng mà không cho phép bất kỳ tối ưu hóa thay đổi kết quả nào.

sqrtsd tự sản xuất chính xác NaN cho đầu vào âm và NaN và đặt cờ không hợp lệ IEEE754. Séc và chi nhánh chỉ là chỉ để giữ nguyên các ngữ nghĩa errno đặt hầu hết các mã không dựa vào.

-fno-math-errno là mặc định trên Darwin (OS X), trong đó thư viện toán học không bao giờ đặt errno, do đó, chức năng có thể được sắp xếp mà không cần kiểm tra này.

+2

Lưu ý rằng '-ffast-math 'có nhiều hơn là chỉ xử lý lỗi hy sinh cho tốc độ. Đặc biệt, nó cũng phá vỡ tuân thủ IEEE 754, tức là, sử dụng cẩn thận và chỉ khi bạn biết những gì bạn đang làm.Xem thêm http://stackoverflow.com/questions/7420665/what-does-gccs-ffast-math-actually-do – godfatherofpolka

+0

@godfatherofpolka có, nói chung. Tuy nhiên trong trường hợp này, đó là tất cả. – Jester

+2

vâng, điều đó đúng, tôi chỉ cảm thấy như mọi đề cập đến cờ tính toán nhanh phải mang nhãn cảnh báo, đó là lý do tại sao tôi thêm nhận xét đó. – godfatherofpolka

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