2011-10-05 54 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,没有或没有这个指令的区别。我并不想以任何方式反驳原始问题,希望找到一种重复的方式或地点。 –