2011-11-14 14 views
2

我試圖測量VS雙精度除法在C++原因有和沒有opt.flag -O3(C++/C)不同的速度浮點除法的

這裏單精度除法的速度差是簡單我寫的代碼。

#include <iostream> 
#include <time.h> 

int main(int argc, char *argv[]) 
{ 

    float  f_x = 45672.0; 
    float  f_y = 67783.0; 
    double d_x = 45672.0; 
    double d_y = 67783.0; 

    float  f_answer; 
    double d_answer; 

    clock_t start,stop; 
    int  N = 200000000 //2*10^8 


start = clock(); 
for (int i = 0; i < N; ++i) 
    { 
    f_answer = f_x/f_y; 
    } 
stop = clock(); 
std::cout<<"Single Precision:"<< (stop-start)/(double)CLOCKS_PER_SEC<<" "<<f_answer <<std::endl; 


start = clock(); 
for (int i = 0; i < N; ++i) 
    { 
    d_answer = d_x/d_y; 
    } 
stop = clock(); 
std::cout<<"Double precision:" <<(stop-start)/(double)CLOCKS_PER_SEC<<" "<< d_answer<<std::endl; 

return 0; 
} 

當我編譯的代碼沒有優化,g++ test.cpp我得到了下面的輸出

Desktop: ./a.out 
Single precision:8.06 0.673797 
Double precision:12.68 0.673797 

但是,如果我編譯這與g++ -O3 test.cpp然後我得到

Desktop: ./a.out 
Single precision:0 0.673797 
Double precision:0 0.673797 

我怎麼會這樣劇烈的表現增加?由於clock()函數的分辨率較低,第二種情況下顯示的時間爲0。編譯器是否檢測到每個循環迭代都獨立於以前的迭代?

+0

我編輯了我的答案,給你一個性能測試優化器不能優化存在。它使用複數,因此對複數的每個簡單操作都涉及多個乘法,增加和減少基礎數據類型。 – Omnifarious

+0

代替對這些值進行硬編碼,讀取它的運行時間並查看其差異,我猜的是編譯器優化,並在編譯時自己計算出值 – vrbilgi

+0

@ user430294:即使您在運行時讀取這些值,編譯器會注意到它們在循環過程中永遠不會改變,並將循環優化爲一次「迭代」。 – Omnifarious

回答

5

看着你從g++ -O3 -S獲得大會,這是相當明顯的循環和所有的浮點計算的(除了那些涉及的時間)進行了優化不復存在:

 .section  .text.startup,"ax",@progbits 
     .p2align 4,,15 
     .globl main 
     .type main, @function 
main: 
.LFB970: 
     .cfi_startproc 
     pushq %rbp 
     .cfi_def_cfa_offset 16 
     .cfi_offset 6, -16 
     pushq %rbx 
     .cfi_def_cfa_offset 24 
     .cfi_offset 3, -24 
     subq $24, %rsp 
     .cfi_def_cfa_offset 48 
     call clock 
     movq %rax, %rbx 
     call clock 
     movq %rax, %rbp 
     movl $.LC0, %esi 
     movl std::cout, %edi 
     subq %rbx, %rbp 
     call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) 

看到兩個打電話給clock,一個接着一個接一個?在這之前,只有一些堆棧維護說明。是的,那些循環完全消失了。

您只能使用f_answerd_answer打印出可在編譯時計算的答案,編譯器可以看到。即使擁有它們也沒有意義。並且如果沒有意義,那麼f_x,f_y,d_xd_y也沒有意義。全沒了。

要解決此問題,您需要讓循環的每次迭代都取決於上次迭代的結果。這是我解決這個問題的方法。我用的是complex模板做參與計算Mandlebrot設置一些計算:

#include <iostream> 
#include <time.h> 
#include <complex> 

int main(int argc, char *argv[]) 
{ 
    using ::std::complex; 
    using ::std::cout; 

    const complex<float> f_coord(0.1, 0.1); 
    const complex<double> d_coord(0.1, 0.1); 

    complex<float> f_answer(0, 0); 
    complex<double> d_answer(0, 0); 

    clock_t start, stop; 
    const unsigned int N = 200000000; //2*10^8 

    start = clock(); 
    for (unsigned int i = 0; i < N; ++i) 
    { 
     f_answer = (f_answer * f_answer) + f_coord; 
    } 
    stop = clock(); 
    cout << "Single Precision: " << (stop-start)/(double)CLOCKS_PER_SEC 
     << " " << f_answer << '\n'; 


    start = clock(); 
    for (unsigned int i = 0; i < N; ++i) 
    { 
     d_answer = (d_answer * d_answer) + d_coord; 
    } 
    stop = clock(); 
    cout << "Double precision: " <<(stop-start)/(double)CLOCKS_PER_SEC 
     << " " << d_answer << '\n'; 

    return 0; 
} 
+0

謝謝Omnifarious的答案。我運行了你的代碼,奇怪的是,雙精度計算的性能優於單精度計算(有和沒有優化)。例如通過優化,我得到單精度計算爲'2.33'秒,雙精度計算爲'0.99'秒?那個怎麼樣?我會認爲單精度計算會更快。 – smilingbuddha

+0

@smilingbuddha:我並不過分驚訝。我不會驚訝地發現,你的數字被轉換爲雙打來做計算,然後又回到浮動存儲。一些CPU有128位長的fp寄存器,就是這樣。 有了gcc,你可以試試'-march = native'和'-mtune = native'選項。這可能是gcc爲你的架構選擇了糟糕的fp操作。 – Omnifarious

7

可能是因爲編譯器將循環優化爲單次迭代。它甚至可能在編譯時完成了分割。

檢查可執行文件的彙編程序以確保(使用例如objdump)。

+0

更好的是,它應該在編譯時評估表達式值。 –

+0

更好的是,根本沒有理由評估任何浮點表達式。結果從未在任何地方使用。如果循環優化到簡單的計數器,我不會感到驚訝。作爲一般規則,gcc並不會優化循環,因爲這會使編寫定時循環變得更加困難。 – Omnifarious

+2

@Omni:是的。 –

1

如果你在你的花車和雙打的定義添加volatile預選賽中,編譯器不會優化掉未使用的計算。

+1

這會工作,但它會以某種方式妨礙優化器。我認爲其中一點就是要發現優化器在被允許正常工作時是否會生成浮點數和雙精度數之間存在大量時間差的代碼。 – Omnifarious

相關問題