TL; DR:爲什麼乘法/鑄造數據在size_t
速度較慢,爲什麼這會因平臺而異?從size_t鑄造性能到雙倍
我有一些性能問題,我不完全理解。上下文是一個相機圖像採集卡,其中128x128 uint16_t圖像以幾百赫茲的頻率讀取和後處理。
在後處理我生成的直方圖這是uint32_t
並且具有thismaxval
= 2^16個元素,基本上我相符所有的強度值。使用這個柱狀圖我計算之和的平方之和:
double sum=0, sumsquared=0;
size_t thismaxval = 1 << 16;
for(size_t i = 0; i < thismaxval; i++) {
sum += (double)i * frame->histo[i];
sumsquared += (double)(i * i) * frame->histo[i];
}
繪製與配置文件中的代碼,我得到了以下(樣本,百分比,代碼):
58228 32.1263 : sum += (double)i * frame->histo[i];
116760 64.4204 : sumsquared += (double)(i * i) * frame->histo[i];
,或者第一行佔據了32 CPU時間的百分比,第二行64%。
我做了一些基準測試,它似乎是數據類型/鑄造有問題。當我將代碼更改爲
uint_fast64_t isum=0, isumsquared=0;
for(uint_fast32_t i = 0; i < thismaxval; i++) {
isum += i * frame->histo[i];
isumsquared += (i * i) * frame->histo[i];
}
它運行速度快了10倍。但是,這種性能影響也因平臺而異。在工作站上,Core i7 CPU 950 @ 3.07GHz的代碼速度提高了10倍。在我的MacBook 8,1上,它具有Intel Core i7 Sandy Bridge 2.7 GHz(2620M),代碼速度只有兩倍。
現在我想知道:
- 爲什麼原來的代碼很慢,容易加快?
- 爲什麼每個平臺的差異如此之大?
更新:
我編譯上面的代碼與
g++ -O3 -Wall cast_test.cc -o cast_test
UPDATE2:
我通過一個分析器(Instruments在Mac跑優化碼,像Shark ),並發現兩件事:
1)在某些情況下,循環本身需要相當長的時間。 thismaxval
是size_t
。
- 需要17%,我總運行時間
for(uint_fast32_t i = 0; i < thismaxval; i++)
需要3.5%for(int i = 0; i < thismaxval; i++)
在探查顯示不出來,我以爲這是小於0。1%
2)的數據類型和澆鑄物如下:
sumsquared += (double)(i * i) * histo[i];
15%(與size_t i
)sumsquared += (double)(i * i) * histo[i];
36%(與uint_fast32_t i
)isumsquared += (i * i) * histo[i];
13%(與uint_fast32_t i
,uint_fast64_t isumsquared
)isumsquared += (i * i) * histo[i];
11%(與int i
,uint_fast64_t isumsquared
,uint_fast64_t isumsquared
)
令人驚訝的是,int
比uint_fast32_t
更快?
UPDATE4:
我跑了不同的數據類型和不同的編譯器更多的測試,一臺機器上。結果如下。
對於TESTD 0 - 2相關的代碼是
for(loop_t i = 0; i < thismaxval; i++)
sumsquared += (double)(i * i) * histo[i];
與sumsquared
雙,和loop_t
size_t
,uint_fast32_t
和int
用於試驗0,1和2。
對於試驗3-- 5代碼是
for(loop_t i = 0; i < thismaxval; i++)
isumsquared += (i * i) * histo[i];
與類型的和loop_t
再次size_t
,uint_fast32_t
和int
對於測試3,4和5
我所用的編譯器的gcc 4.2.1,GCC 4.4.7,4.6.3 GCC和gcc 4.7.0。時間以代碼總CPU時間的百分比表示,因此它們顯示相對的性能,而不是絕對的(儘管運行時間在21s時相當穩定)。 CPU時間是兩行,因爲我不太確定分析器是否正確分隔了兩行代碼。
gcc: 4.2.1 4.4.7 4.6.3 4.7.0 ---------------------------------- test 0: 21.85 25.15 22.05 21.85 test 1: 21.9 25.05 22 22 test 2: 26.35 25.1 21.95 19.2 test 3: 7.15 8.35 18.55 19.95 test 4: 11.1 8.45 7.35 7.1 test 5: 7.1 7.8 6.9 7.05
或:
在此基礎上,似乎鑄造是昂貴的,不管我用什麼樣的整數類型。
此外,似乎gcc 4.6和4.7不能正確優化循環3(size_t和uint_fast64_t)。
你也可以用'uint_fast32_t'來試試嗎?人們普遍猜測,由於第二種數據類型與機器指令(64位)具有相同的位長,所以速度更快。猜測你至少有一臺64位機器。我預計fast32也會變慢。你也可以測試'uint_fast32_t'和'uint_fast64_t'的大小嗎?我的猜測是32位實際上是64位。 – Yuri
你的意思是'uint_fast32_t isum'?我可以嘗試,但我認爲這可能會溢出,這就是爲什麼我使用uint_fast64_t。 – Tim
嗯,對於1 .:原因以某種方式決定了將浮點數賦值爲float並進行浮點操作應該比直接進行int操作要慢(儘管int-to-float不應該像float-to-int一樣邪惡),甚至更多所以沒有那個最佳的x87堆棧。你是否用SSE支持來編譯它? –