2011-10-05 51 views
4

當我運行以下函數時,我得到了一些意想不到的結果。如何執行更多的指令加速執行

在我的機器上,下面的代碼一直需要大約6秒鐘才能運行。但是,如果我取消註釋「;dec [variable + 24]」行,,因此執行更多代碼大約需要4.5秒運行。爲什麼?

.DATA 
variable dq 0 dup(4) 
.CODE    

runAssemblyCode PROC 
    mov rax, 2330 * 1000 * 1000 
start: 
    dec [variable] 
    dec [variable + 8] 
    dec [variable + 16] 
    ;dec [variable + 24] 
    dec rax 
    jnz start 
    ret 
runAssemblyCode ENDP 
END 

我注意到,已經有堆棧溢出類似的問題,但他們的代碼樣本並不如此簡單,我找不到任何簡潔的回答這個問題。

我已經嘗試用nop填充代碼以查看它是否是對齊問題,並且還將親和性設置爲單個處理器。沒有任何區別。

+0

出於好奇,你嘗試開始與空指令來調整從該端緩存行之前填充?您可能只是簡化了解碼和執行路徑上的任務分配,可能會發現一個加劇它們的序列,然後通過額外的指令提供補救措施。 –

+0

愚蠢的問題:我如何組裝這個,這是英特爾語法是嗎?我可以使用gcc/gas嗎? –

+0

明白了,你是否想要減少字節,單詞,dword? –

回答

3

簡單的答案是,因爲現代CPU非常複雜。在引擎蓋下有很多事情對觀察者來說顯得不可預知或隨機。

插入該額外的指令可能會導致它安排不同的指令,在這樣的緊密循環中,可能會產生差異。但這只是一個猜測。

就我所見,它觸及與前一條指令相同的緩存行,所以它似乎不是一種預取。我無法真正想到一個合理的解釋,但是CPU再一次使用了大量的無文檔啓發式和猜測來儘可能快地執行代碼,有時候,這意味着奇怪的角落案例,他們失敗了,代碼變爲比你想象的要慢。

你在不同的CPU型號上測試過嗎?看看它是否在你的特定CPU上,或者其他x86 CPU是否展現相同的東西,會很有趣。

0

這並不壞。平均而言,完整循環需要2.6 ns執行,而另一個需要1.9 ns。假設2GHz的CPU具有0.5ns的週期,每個迴路的差別大約爲(2.6 - 1.9)/0.5 = 1 clock cycle,這並不奇怪。
雖然由於您要求的週期數,時差會變得非常明顯:0.5 ns * 2330000000 = 1.2 seconds,您觀察到的差異。

+0

毫不奇怪?爲什麼執行一條更多的指令減少循環迭代執行時間的一個週期並不令人驚訝? OP並沒有問它將會產生多大的差異,而是爲何存在差異。發生什麼使得較短的版本*較慢*? – jalf

+0

@jalf:哦,等等,我誤解了這個問題。我雖然較短的版本是更快的版本;) – BlackBear

+0

啊,你的答案更有意義。 :) – jalf

1

bob.s

.data 
variable: 
    .word 0,0,0,0 
    .word 0,0,0,0 
    .word 0,0,0,0 
    .word 0,0,0,0 
    .word 0,0,0,0 
    .word 0,0,0,0 

.text 
.globl runAssemblyCode 
runAssemblyCode: 
    mov $0xFFFFFFFF,%eax 

start_loop: 
    decl variable+0 
    decl variable+8 
    decl variable+16 
    ;decl variable+24 
    dec %eax 
    jne start_loop 
    retq 

ted.c

#include <stdio.h> 
#include <time.h> 

void runAssemblyCode (void); 

int main (void) 
{ 
    volatile unsigned int ra,rb; 

    ra=(unsigned int)time(NULL); 
    runAssemblyCode(); 
    rb=(unsigned int)time(NULL); 
    printf("%u\n",rb-ra); 
    return(0); 
} 

GCC -02 ted.c bob.s -o特德

,這是與額外的指令:

00000000004005d4 <runAssemblyCode>: 
    4005d4: b8 ff ff ff ff   mov $0xffffffff,%eax 

00000000004005d9 <start_loop>: 
    4005d9: ff 0c 25 28 10 60 00 decl 0x601028 
    4005e0: ff 0c 25 30 10 60 00 decl 0x601030 
    4005e7: ff 0c 25 38 10 60 00 decl 0x601038 
    4005ee: ff 0c 25 40 10 60 00 decl 0x601040 
    4005f5: ff c8     dec %eax 
    4005f7: 75 e0     jne 4005d9 <start_loop> 
    4005f9: c3      retq 
    4005fa: 90      nop 

我沒有看到區別,也許你可以糾正我的代碼或其他人可以嘗試在他們的系統,看看他們看到了什麼......

這是一個非常痛苦的指令,如果你正在做的東西以外的字節爲基礎的內存遞減未對齊,並將痛苦的內存系統。所以這個例程應該對高速緩存行以及內核數量敏感。

無論有沒有額外的指令,它都需要大約13秒。

的AMD Phenom 9950四核處理器

英特爾(R)核心(TM)2 CPU 6300

歷時約9-10秒有或沒有額外的指令。

兩個處理器: 英特爾(R)至強(TM)CPU

了約13秒,或沒有額外的指令。

在此: 英特爾(R)核心(TM)2 Duo處理器T7500

8秒有或無。

所有正在運行的Ubuntu 64位10.04或10.10,可能是11.04在那裏。

一些更多的機器中,64位,的ubuntu

英特爾(R)至強(R)CPU X5450(8芯)

6秒具有或不具有額外的指令。

英特爾(R)至強(R)CPU E5405(8芯)

9秒有或無。

系統中DDR/DRAM的速度是多少?你正在運行什麼樣的處理器(如果在Linux上是cat/proc/cpuinfo)。

英特爾(R)至強(R)CPU E5440(8芯)

6秒具有或不具有

稀釋,發現單核,至強雖然: 英特爾(R)至強(TM) CPU

15秒,或者沒有額外的指令

+0

我認爲它應該是'decq',我不知道它會帶來多少不同。 – user786653

+0

我只在其中一臺機器上試過decq,沒有或沒有這個指令的區別。我並不想以任何方式反駁原始問題,希望找到一種重複的方式或地點。 –