2014-02-06 109 views
2

的開銷時間的變化在這裏是C++代碼,我用vs2013,釋放模式約函數指針:爲什麼當函數的內容改變

#include <ctime> 
#include <iostream> 

void Tempfunction(double& a, int N) 
{ 
    a = 0; 
    for (double i = 0; i < N; ++i) 
    { 
    a += i; 
    } 
} 

int main() 
{ 
    int N = 1000; // from 1000 to 8000 

    double Value = 0; 
    auto t0 = std::time(0); 
    for (int i = 0; i < 1000000; ++i) 
    { 
     Tempfunction(Value, N); 
    } 
    auto t1 = std::time(0); 
    auto Tempfunction_time = t1-t0; 
    std::cout << "Tempfunction_time = " << Tempfunction_time << '\n'; 

    auto TempfunctionPtr = &Tempfunction; 

    Value = 0; 
    t0 = std::time(0); 
    for (int i = 0; i < 1000000; ++i) 
    { 
     (*TempfunctionPtr)(Value, N); 
    } 
    t1 = std::time(0); 
    auto TempfunctionPtr_time = t1-t0; 
    std::cout << "TempfunctionPtr_time = " << TempfunctionPtr_time << '\n'; 

    std::system("pause"); 
} 

我N個從1000的值更改爲8000,並記錄Tempfunction_time和TempfunctionPtr_time。 結果是怪異:

N=1000 , Tempfunction_time=1, TempfunctionPtr_time=2; 
N=2000 , Tempfunction_time=2, TempfunctionPtr_time=6; 
N=4000 , Tempfunction_time=4, TempfunctionPtr_time=11; 
N=8000 , Tempfunction_time=8, TempfunctionPtr_time=21; 

TempfunctionPtr_time - Tempfunction_time不是恆定的, 和TempfunctionPtr_time = 2〜3 ​​ Tempfunction_time。 差異應該是一個常數,它是函數指針的開銷。

出了什麼問題?

編輯:

假設VS2013內聯Tempfunction如果它通過Tempfunction(所謂的),並且不內聯,如果它是由(* TempfunctionPtr)調用,那麼我們就可以解釋的差異。所以,如果這是真的,爲什麼不能編譯器內聯(* TempfunctionPtr)?

+1

我現在看到你在發佈模式下構建它。我確信現在是優化。關掉所有可能的優化(我不知道該怎麼做vs 2013),然後再試一次。 –

+1

std :: time與秒一起工作,也許你需要毫秒精度的措施。 – Narkha

+0

實際上,您可以在Windows上使用QueryPerformanceCounter。 –

回答

0

我在Linux機器上用g ++編譯了現有代碼,發現時間太短而無法在幾秒鐘內精確測量,因此重寫了它以使用std::chrono更精確地測量時間 - 我也必須「使用「變量Value(因此在下面打印」499500「),否則編譯器會完全優化掉第一個循環。然後,我得到以下結果:

Tempfunction_time = 1.47983 
499500 
TempfunctionPtr_time = 1.69183 
499500 

現在,結果我是GCC(!4.6.3版本 - 其他版本可供選擇,並可能給其他結果),這是不一樣的編譯器爲微軟,因此結果可能會有所不同 - 不同的編譯器有時會以不同的方式優化代碼。我真的很驚訝,編譯器不知道TempFunction的結果只需要計算一次。但是,嘿,讓它更容易編寫沒有欺騙的基準。我的第二個觀察結果是,在我的編譯器中,如果我用代碼圍繞主代碼替換int N=1000;,這兩種情況之間沒有或很少有區別 - 我不完全確定爲什麼,因爲代碼看起來完全相同(沒有通過函數指針調用,因爲編譯器知道函數指針是一個常量),並且在兩種情況下內聯都是內聯的。 (當N等於1000時,發生相同的「相等」 - 所以我很難確定這裏發生了什麼......

要實際測量函數指針和直接函數調用之間的差異,將需要移動TempFUnction到一個單獨的文件中,「隱藏」存儲在TempFunctionPtr使得編譯器不搞清楚你在做什麼實際的價值。

最後,我結束了這樣的事情:

typedef void (*FunPtr)(double &a, int N); 

void Tempfunction(double& a, int N) 
{ 
    a = 0; 
    for (double i = 0; i < N; ++i) 
    { 
    a += i; 
    } 
} 

FunPtr GetFunPtr() 
{ 
    return &Tempfunction; 
} 

而且這樣的 「主」 代碼:

#include <iostream> 
#include <chrono> 

typedef void (*FunPtr)(double &a, int N); 

extern void Tempfunction(double& a, int N); 
extern FunPtr GetFunPtr(); 

int main() 
{ 
    for(int N = 1000; N <= 8000; N *= 2) 
    { 
    std::cout << "N=" << N << std::endl; 
    double Value = 0; 
    auto t0 = std::chrono::system_clock::now(); 
    for (int i = 0; i < 1000000; ++i) 
    { 
     Tempfunction(Value, N); 
    } 
    auto t1 = std::chrono::system_clock::now();; 
    std::chrono::duration<double> Tempfunction_time = t1-t0; 
    std::cout << "Tempfunction_time = " << Tempfunction_time.count() << '\n'; 
    std::cout << Value << std::endl; 

    auto TempfunctionPtr = GetFunPtr(); 

    Value = 0; 
    t0 = std::chrono::system_clock::now(); 
    for (int i = 0; i < 1000000; ++i) 
    { 
     (*TempfunctionPtr)(Value, N); 
    } 
    t1 = std::chrono::system_clock::now(); 
    std::chrono::duration<double> TempfunctionPtr_time = t1-t0; 
    std::cout << "TempfunctionPtr_time = " << TempfunctionPtr_time.count() << '\n'; 
    std::cout << Value << std::endl; 
    } 
} 

然而,差異是千分之一秒,變體是一個明顯的贏家,唯一的結論是明顯的,「調用一個函數比內聯它慢」。

N=1000 
Tempfunction_time = 1.78323 
499500 
TempfunctionPtr_time = 1.77822 
499500 
N=2000 
Tempfunction_time = 3.54664 
1.999e+06 
TempfunctionPtr_time = 3.54687 
1.999e+06 
N=4000 
Tempfunction_time = 7.0854 
7.998e+06 
TempfunctionPtr_time = 7.08706 
7.998e+06 
N=8000 
Tempfunction_time = 14.1597 
3.1996e+07 
TempfunctionPtr_time = 14.1577 
3.1996e+07 

當然,如果我們這樣做「只有一半的隱藏絕招」,使功能是已知的,在第一種情況下inlineable,而不是已知的,通過函數指針,我們或許可以期待一個區別。但通過指針調用函數本身並不昂貴。當編譯器決定內聯函數時,真正的區別就來了。

顯然,這些是GCC 4.6.3的結果,它與MSVS2013不同。您應該對上述代碼中的「時間序列」進行修改,然後查看它有什麼不同。

+0

謝謝。我在Windows上得到了類似的結果。我認爲vs2013爲我的代碼發佈了Tempology。而且,爲什麼內聯可以提供非恆定的性能提升?即,而不是Tempfunction_time - TempfunctionPtr_time =常數,我得到Tempfunction_time = 2〜3 ​​ TempfunctionPtr_time? – liangbright

+0

另一個問題:如果編譯器被函數指針調用,它不能內聯一個函數嗎? (例如,虛函數) – liangbright

+0

@ liangbright如果編譯器可以證明該方法未被覆蓋,它仍然可以內聯。現在編譯器變得越來越聰明瞭。那麼現在甚至可以跨文件內聯。 –