Assembly 执行更多指令如何加快执行速度

Assembly 执行更多指令如何加快执行速度,assembly,intel,timing,Assembly,Intel,Timing,当我运行下面的函数时,我得到了一些意想不到的结果 在我的机器上,下面的代码持续运行大约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 +

当我运行下面的函数时,我得到了一些意想不到的结果

在我的机器上,下面的代码持续运行大约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指令填充代码,看看这是否是一个对齐问题,并将关联设置为单个处理器。两者都没有任何区别。

简单的答案是因为现代CPU极其复杂。在观察者看来,有很多事情是不可预测或随机的

插入额外的指令可能会导致它以不同的方式调度指令,这在这样的紧密循环中可能会产生不同的效果。但这只是一个猜测

就我所见,它与前面的指令接触相同的缓存线,因此它似乎不是一种预取。我真的想不出一个合乎逻辑的解释,但同样,CPU利用大量未记录的启发式和猜测来尽可能快地执行代码,有时,这意味着奇怪的情况下,它们失败了,代码变得比您预期的慢


您在不同的CPU型号上测试过这个吗?看看这是否只是在您的特定CPU上,或者其他x86 CPU是否表现出相同的特性,这将是一件很有趣的事情。

没有那么糟糕。平均而言,整个循环需要2.6 ns才能执行,而另一个循环需要1.9 ns。假设一个2GHz的CPU,其周期为0.5ns,那么每个循环的差异大约为
(2.6-1.9)/0.5=1个时钟周期,这并不奇怪。
不过,由于您请求的周期数,时差变得非常明显:
0.5 ns*2330000000=1.2秒,即您观察到的时差。

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
特德·c

#包括
#包括
无效运行汇编代码(void);
内部主(空)
{
易失性无符号整数ra,rb;
ra=(无符号整数)时间(空);
runAssemblyCode();
rb=(无符号整数)时间(空);
printf(“%u\n”,rb-ra);
返回(0);
}
gcc-O2 ted.c bob.s-o ted

这是额外的指示:

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
0000000000 4005D4:
4005d4:b8 ff ff mov$0xffffffff,%eax
0000000000 4005D9:
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十二月%eax
4005f7:75 e0 jne 4005d9
4005f9:c3 retq
4005fa:90无
我看不出有什么不同,也许你可以更正我的代码,或者其他人可以在他们的系统上尝试,看看他们看到了什么

这是一条非常痛苦的指令,如果您正在执行的不是基于字节的内存递减,而是未对齐的,并且对内存系统来说将是痛苦的。因此,这个例程应该对缓存线以及核心数等敏感

不管有没有额外的指令,都需要大约13秒的时间

amd phenom 9950四核处理器

英特尔(R)核心(TM)2处理器6300

无论是否有额外指令,都需要9-10秒

双处理器: 英特尔(R)至强(TM)CPU

无论是否有额外的指令,都需要大约13秒

关于这一点: Intel(R)Core(TM)2双CPU 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秒,带或不带

啊,找到了一个单核,但xeon: 英特尔(R)至强(TM)CPU


15秒有或没有额外的指令

没什么奇怪的吗?执行一条以上的指令可以缩短循环迭代的执行时间一个周期,这为什么不令人惊讶呢?OP并不是问它会带来多大的不同,而是问为什么会存在差异。发生了什么事情使得短版本的速度变慢了?@jalf:哦,等等,我误解了这个问题。我觉得越短的版本越快;)啊,那么你的回答就更有意义了出于好奇,在开始使用nops之前,您是否尝试过填充以从该端调整缓存线?您可能只是在平滑任务在解码和执行路径上的分布,可能已经发现了一个使其恶化的序列,然后用额外的指令提供了补救措施。愚蠢的问题:我如何组装它,这是intel语法是吗?我可以使用gcc/gas吗?明白了,你是在尝试减少字节、单词、DWORD吗?我想应该是
decq
,但我不知道它会有多大区别。我只在其中一台机器上尝试了decq,无论是否使用该指令都没有区别。我并不是试图以任何方式反驳最初的问题,而是希望找到一种方法或地点来重复它。
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