[英]What is faster on division? doubles / floats / UInt32 / UInt64 ? in C++/C
我做了一些速度測試,以確定在對數字進行乘法或除法時最快的速度。 我必須努力工作以擊敗優化者。 我得到了無意義的結果,例如在2微秒內運行的大量循環,或者乘法與除法的速度相同(如果只是那樣)。
在我最終努力工作以擊敗足夠的編譯器優化之后,同時仍然讓它優化速度,我得到了這些速度結果。 他們可能對別人感興趣?
如果我的測試仍然懸而未決,請告訴我,但要善待,因為我只花了兩個小時寫這個垃圾:P
64 time: 3826718 us
32 time: 2476484 us
D(mul) time: 936524 us
D(div) time: 3614857 us
S time: 1506020 us
使用雙打“乘以除法”似乎是進行除法的最快方法,其次是整數除法。 我沒有測試分裂的准確性。 可能是“正確的划分”更准確嗎? 我不想在這些速度測試結果之后發現,因為我只是在基數為10的常數上使用整數除法,讓我的編譯器為我優化它;)(並且不會破壞它的優化)。
這是我用來獲得結果的代碼:
#include <iostream>
int Run(int bla, int div, int add, int minus) {
// these parameters are to force the compiler to not be able to optimise away the
// multiplications and divides :)
long LoopMax = 100000000;
uint32_t Origbla32 = 1000000000;
long i = 0;
uint32_t bla32 = Origbla32;
uint32_t div32 = div;
clock_t Time32 = clock();
for (i = 0; i < LoopMax; i++) {
div32 += add;
div32 -= minus;
bla32 = bla32 / div32;
bla32 += bla;
bla32 = bla32 * div32;
}
Time32 = clock() - Time32;
uint64_t bla64 = bla32;
clock_t Time64 = clock();
uint64_t div64 = div;
for (long i = 0; i < LoopMax; i++) {
div64 += add;
div64 -= minus;
bla64 = bla64 / div64;
bla64 += bla;
bla64 = bla64 * div64;
}
Time64 = clock() - Time64;
double blaDMul = Origbla32;
double multodiv = 1.0 / (double)div;
double multomul = div;
clock_t TimeDMul = clock();
for (i = 0; i < LoopMax; i++) {
multodiv += add;
multomul -= minus;
blaDMul = blaDMul * multodiv;
blaDMul += bla;
blaDMul = blaDMul * multomul;
}
TimeDMul = clock() - TimeDMul;
double blaDDiv = Origbla32;
clock_t TimeDDiv = clock();
for (i = 0; i < LoopMax; i++) {
multodiv += add;
multomul -= minus;
blaDDiv = blaDDiv / multomul;
blaDDiv += bla;
blaDDiv = blaDDiv / multodiv;
}
TimeDDiv = clock() - TimeDDiv;
float blaS = Origbla32;
float divS = div;
clock_t TimeS = clock();
for (i = 0; i < LoopMax; i++) {
divS += add;
divS -= minus;
blaS = blaS / divS;
blaS += bla;
blaS = blaS * divS;
}
TimeS = clock() - TimeS;
printf("64 time: %i us (%i)\n", (int)Time64, (int)bla64);
printf("32 time: %i us (%i)\n", (int)Time32, bla32);
printf("D(mul) time: %i us (%f)\n", (int)TimeDMul, blaDMul);
printf("D(div) time: %i us (%f)\n", (int)TimeDDiv, blaDDiv);
printf("S time: %i us (%f)\n", (int)TimeS, blaS);
return 0;
}
int main(int argc, char* const argv[]) {
Run(0, 10, 0, 0); // adds and minuses 0 so it doesn't affect the math, only kills the opts
return 0;
}
有很多方法可以執行某些算術,所以可能沒有一個答案(移位,小數乘法,實際除法,通過對數單位的某些往返等等;這些可能都有不同的相對成本,具體取決於操作數和資源分配)。
讓編譯器使用它具有的程序和數據流信息。
對於適用於x86上的匯編的一些數據,您可能會看到: “AMD和Intel x86處理器的指令延遲和吞吐量”
最快的將完全取決於目標架構。 它看起來像你只對你碰巧在的平台感興趣,從你的執行時間猜測似乎是64位x86,無論是Intel(Core2?)還是AMD。
也就是說,反向浮點乘法在許多平台上都是最快的,但正如你推測的那樣,通常不如浮點除法(兩次舍入而不是一次) - 無論這對你的使用是否重要是一個單獨的問題)。 一般來說,你最好重新安排你的算法使用較少的分數,而不是跳過箍來使分割盡可能高效(最快的分工是你不做的),並確保在你之前進行基准測試花費時間進行優化,因為划分分區的算法很少而且很遠。
此外,如果您有整數源並需要整數結果,請確保在基准測試中包括整數和浮點之間的轉換成本。
由於您對特定計算機上的計時感興趣,因此您應該知道英特爾現在在其優化參考手冊(pdf)中發布此信息。 具體來說,您將對附錄C第3.1節“注冊操作數的延遲和吞吐量”表格感興趣。
請注意,整數除法時間很大程度上取決於所涉及的實際值。 根據該指南中的信息,您的計時程序似乎仍有相當大的開銷,因為您測量的性能比率與英特爾公布的信息不符。
正如Stephen提到的那樣,使用優化手冊 - 但您也應該考慮使用SSE指令。 這些可以在單個指令中進行4或8個分區/乘法。
此外,分區采用單個時鍾周期進行處理是相當普遍的。 結果可能在幾個時鍾周期(稱為延遲)中不可用,但是下一個除法可以在此期間開始(與第一個重疊),只要它不需要第一個的結果。 這是由於CPU中的管道襯里,就像在先前的負載仍在干燥時可以洗更多的衣服一樣。
乘以除法是一種常見的技巧,應該在你的除數不經常改變的地方使用。
您很有可能花費時間和精力使數學運算快速,只是發現內存訪問的速度(當您導航輸入並寫入輸出時)限制了您的最終實施。
我在MSVC 2008上寫了一個有缺陷的測試
double i32Time = GetTime();
{
volatile __int32 i = 4;
__int32 count = 0;
__int32 max = 1000000;
while( count < max )
{
i /= 61;
count++;
}
}
i32Time = GetTime() - i32Time;
double i64Time = GetTime();
{
volatile __int64 i = 4;
__int32 count = 0;
__int32 max = 1000000;
while( count < max )
{
i /= 61;
count++;
}
}
i64Time = GetTime() - i64Time;
double fTime = GetTime();
{
volatile float i = 4;
__int32 count = 0;
__int32 max = 1000000;
while( count < max )
{
i /= 4.0f;
count++;
}
}
fTime = GetTime() - fTime;
double fmTime = GetTime();
{
volatile float i = 4;
const float div = 1.0f / 4.0f;
__int32 count = 0;
__int32 max = 1000000;
while( count < max )
{
i *= div;
count++;
}
}
fmTime = GetTime() - fmTime;
double dTime = GetTime();
{
volatile double i = 4;
__int32 count = 0;
__int32 max = 1000000;
while( count < max )
{
i /= 4.0f;
count++;
}
}
dTime = GetTime() - dTime;
double dmTime = GetTime();
{
volatile double i = 4;
const double div = 1.0f / 4.0f;
__int32 count = 0;
__int32 max = 1000000;
while( count < max )
{
i *= div;
count++;
}
}
dmTime = GetTime() - dmTime;
DebugOutput( _T( "%f\n" ), i32Time );
DebugOutput( _T( "%f\n" ), i64Time );
DebugOutput( _T( "%f\n" ), fTime );
DebugOutput( _T( "%f\n" ), fmTime );
DebugOutput( _T( "%f\n" ), dTime );
DebugOutput( _T( "%f\n" ), dmTime );
DebugBreak();
然后我以32位模式在AMD64 Turion 64上運行它。 我得到的結果如下:
0.006622
0.054654
0.006283
0.006353
0.006203
0.006161
測試有缺陷的原因是使用volatile會強制編譯器從內存中重新加載變量,以防萬一它被更改。 所有這些都表明這台機器上的任何實現之間沒有什么區別(__int64顯然很慢)。
它還明確地表明MSVC編譯器通過倒數優化執行乘法運算。 我想GCC會做同樣的事情,如果不是更好的話。 如果我改變浮點數和雙除法檢驗除以“i”那么它會顯着增加時間。 雖然,雖然很多可能是從磁盤重新加載,但很明顯編譯器無法輕易地優化它。
要了解這種微觀優化,請嘗試閱讀此pdf。
我所有人都認為,如果你擔心這些事情,你顯然還沒有描述你的代碼。 在實際出現問題時,對問題進行概述和修復。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.