【问题标题】:How can executing more instructions speed up exection执行更多指令如何加快执行速度
【发布时间】:2011-10-05 09:01:12
【问题描述】:

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

在我的机器上,以下代码始终需要大约 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

我注意到 Stack Overflow 上已经有类似的问题了,但是他们的代码示例并没有这么简单,我找不到任何关于这个问题的简洁答案。

我尝试用 nop 指令填充代码以查看它是否是对齐问题,并将关联设置为单个处理器。两者都没有任何区别。

【问题讨论】:

  • 出于好奇,您是否在使用 nops 开始之前尝试填充以从该端调整缓存行?您可能只是在解码和执行路径上平滑任务的分配,可能已经找到了加重它们的序列,然后通过额外的指令提供了补救措施。
  • 愚蠢的问题:我如何组装这个,这是英特尔语法吗?我可以使用 gcc/gas 吗?
  • 知道了,你想减少字节、字、双字吗?

标签: assembly intel timing


【解决方案1】:

简单的答案是因为现代 CPU 非常复杂。幕后发生的许多事情对观察者来说似乎是不可预测或随机的。

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

据我所见,它与上一条指令触及相同的缓存行,因此它似乎不是一种预取。我真的想不出一个合乎逻辑的解释,但同样,CPU 利用大量未记录的启发式方法和猜测来尽可能快地执行代码,有时,这意味着它们失败的奇怪极端情况,代码变成比你预期的要慢。

您是否在不同的 CPU 型号上对此进行了测试?看看这是否只是在您的特定 CPU 上,或者其他 x86 CPU 是否表现出同样的事情会很有趣。

【讨论】:

    【解决方案2】:

    鲍勃.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 -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
    

    我看不出有什么不同,也许您可​​以更正我的代码,或者其他人可以尝试在他们的系统上查看他们看到的内容...

    这是一个非常痛苦的指令,而且如果您正在做的事情不是基于字节的内存递减,这是未对齐的并且会给内存系统带来痛苦。所以这个例程应该对缓存行以及内核数量等敏感。

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

    AMD 飞鸿 9950 四核处理器

    在一个

    Intel(R) Core(TM)2 CPU 6300

    不管有没有额外的指令,大约需要 9-10 秒。

    两个处理器: Intel(R) Xeon(TM) CPU

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

    关于这个: Intel(R) Core(TM)2 双核 CPU T7500

    8 秒有或没有。

    所有都在运行 Ubuntu 64 位 10.04 或 10.10,可能是 11.04。

    更多机器,64 位,ubuntu

    Intel(R) Xeon(R) CPU X5450(8 核)

    6 秒有或没有额外的指令。

    Intel(R) Xeon(R) CPU E5405(8 核)

    9 秒有或没有。

    您的系统中 DDR/DRAM 的速度是多少?您正在运行哪种处理器(如果在 linux 上,则为 cat /proc/cpuinfo)。

    Intel(R) Xeon(R) CPU E5440(8 核)

    6 秒有或没有

    啊,找到了一个单核,但至强: Intel(R) Xeon(TM) CPU

    15 秒有或没有额外的指令

    【讨论】:

    • 我觉得应该是decq,不过我不知道会有多大的不同。
    • 我只在其中一台机器上尝试了 decq,不管有没有那个指令都没有区别。我并不是想以任何方式反驳最初的问题,而是希望找到一种方式或地方来重复它。
    【解决方案3】:

    没那么糟。平均而言,执行完整的循环需要 2.6 ns,而另一个需要 1.9 ns。假设一个 2GHz CPU,其周期为 0.5 ns,差异大约为每个循环 (2.6 - 1.9) / 0.5 = 1 clock cycle,这不足为奇。
    但是,由于您请求的周期数,时间差异变得如此明显:0.5 ns * 2330000000 = 1.2 seconds,您观察到的差异。

    【讨论】:

    • 没什么奇怪的?为什么再执行一条指令将循环迭代的执行时间缩短一个周期并不奇怪? OP 并没有问它会产生多大的差异,而是问为什么存在差异。是什么导致较短的版本变慢
    • @jalf:哦等等,我误解了这个问题。我虽然较短的版本更快;)
    • 啊,那你的回答就更有意义了。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-23
    • 1970-01-01
    相关资源
    最近更新 更多