[英]Slow Instructions in Simple Loop on x86
我有一個簡單的循環,我用C ++編寫,因為我想在我的CPU上分析乘法指令的性能。 我在匯總代碼時發現了一些有趣的細微差別。
這是C ++程序:
#define TESTS 10000000
#define BUFSIZE 1000
uint32_t buf_in1[BUFSIZE];
uint32_t buf_in2[BUFSIZE];
uint32_t volatile buf_out[BUFSIZE];
unsigned int i, j;
for (i = 0; i < BUFSIZE; i++) {
buf_in1[i] = i;
buf_in2[i] = i;
}
for (j = 0; j < TESTS; j++) {
for (i = 0; i < BUFSIZE; i++) {
buf_out[i] = buf_in1[i] * buf_in2[i];
}
}
我用以下標志編譯:
優化:
代碼生成:
雖然我在64位計算機上運行它,但它是在Win32下的visual studio 2012中編譯的。
注意buf_out上的volatile限定符。 它只是阻止編譯器優化循環。
我通過分析器( AMD的CodeXL )運行此代碼,我發現乘法指令不會占用大部分CPU時間。 imul指令占用了大約30%,但是其他兩個指令也花費了大約60%:
請注意,Timer列顯示了探測器在此指令中找到代碼的計時器滴答數。 計時器滴答是1ms,因此2609滴答在該指令上花費約2609ms。
除乘法指令之外占用大量時間的兩個指令是mov指令和jb(滿足條件時跳轉)指令。
mov指令,
mov [esp+eax+00001f40h],ecx
正在將乘法(ecx)的結果移回到eax的緩沖區buf_out緩沖區(這是表示i的寄存器)。 這是有道理的,但為什么這比其他mov指令要花這么長時間? 就是這個:
mov ecx,[esp+eax+00000fa0h]
它們都從內存中的類似位置讀取,數組長度為1000 uint32_t或長度為4000字節。 那是4000 * 3 = 12kB。 我的L1緩存是64kB所以它應該很容易適合L1,據我所知...
以下結果顯示了Coreinfo的緩存大小等:
至於跳轉指令:
jb $-1ah (0x903732)
我不知道為什么它占用程序執行時間的33%。 我的處理器行大小為64字節,跳轉僅向后跳躍0x1A字節或26字節。 可能是因為這個跳過了一個64字節的邊界? (0x903740是64字節邊界)
那么有人可以解釋這些行為嗎?
謝謝。
正如神秘學所提到的,你所看到的時間並不是一對一的責任,而是顯示出來的指示。
現代處理器並行運行許多指令( imul
和eax的add
4都可以並行運行, mov
imul
涉及的數學也使用ALU,並且可以在imul
完成之前計算)。
大多數分析器計算其時序的方式是使用定時中斷,您看到的是中斷時執行的指令。
要正確使用分析器,您需要針對大型程序運行,並查看該程序是否花費了大量時間。 在每個指令的基礎上,它沒有太大的價值。
如果你真的想進行速度測試,你想在循環之前和之后使用CPU計時器,看看如何以某種方式改進它以使其更快地運行。
我不認為這一切都適合你的L1,因為你正在調試的代碼不是唯一使用CPU的代碼(除非你啟動你的機器來運行那些代碼,實際上這將是你的操作系統)。
還要注意那里有一個模式:最慢的操作都需要主存儲器訪問。 由於此訪問時間不受CPU控制,因此很難指出為什么它不會更快。 這將需要硬件分析。
希望這可以幫助。
不幸的是,你沒有給出一次通過循環所需的時間,但我認為它是三個CPU周期。 如果這是真的,那么恰好得到時間的三條指令就是當時鍾滴答時處理器正式開啟的三條指令。 其他三條指令與三條正式耗時的指令並行執行,隱藏在它們后面。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.