Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance leaq有那么慢吗?还是有另一个原因使较小的程序集列表比较长的程序集列表慢?_Performance_Assembly_X86_Intel - Fatal编程技术网

Performance leaq有那么慢吗?还是有另一个原因使较小的程序集列表比较长的程序集列表慢?

Performance leaq有那么慢吗?还是有另一个原因使较小的程序集列表比较长的程序集列表慢?,performance,assembly,x86,intel,Performance,Assembly,X86,Intel,我不知道任何真正的程序集,但可以读取GCC-S输出,以评估给定C代码的实际成本 这个问题不是关于评测和基准的,而是教育性的。我需要有人向我解释为什么[1]代码段不比第二个快 好吧,过去的想法是:“是的,像MUL这样的一些操作非常昂贵,但是如果一个组件比另一个组件大X倍,它应该会慢一些。” 在我遇到这两个人之前,这是完全正确的: unsigned char bytes[4] = {0, 0, 0, 5}; // 1 int32_t val = *((int32_t*)bytes);

我不知道任何真正的程序集,但可以读取GCC
-S
输出,以评估给定C代码的实际成本

这个问题不是关于评测和基准的,而是教育性的。我需要有人向我解释为什么[1]代码段不比第二个快

好吧,过去的想法是:“是的,像MUL这样的一些操作非常昂贵,但是如果一个组件比另一个组件大X倍,它应该会慢一些。”

在我遇到这两个人之前,这是完全正确的:

unsigned char bytes[4] = {0, 0, 0, 5};

// 1
int32_t val = *((int32_t*)bytes);      
/* produces:
        leaq    -16(%rbp), %rax
        movl    (%rax), %eax
        movl    %eax, -4(%rbp)
        movl    $0, %eax
*/

// 2   
val = bytes[3] |                               
      (bytes[2] << 8) |                        
      (bytes[1] << 16) |
      (bytes[0] << 24);
/* produces: 
        movzbl  -13(%rbp), %eax
        movzbl  %al, %eax
        movzbl  -14(%rbp), %edx
        movzbl  %dl, %edx
        sall    $8, %edx
        orl     %eax, %edx
        movzbl  -15(%rbp), %eax
        movzbl  %al, %eax
        sall    $16, %eax
        orl     %eax, %edx
        movzbl  -16(%rbp), %eax
        movzbl  %al, %eax
        sall    $24, %eax
        orl     %edx, %eax
        movl    %eax, -4(%rbp)
        movl    $0, %eax
*/

更新:
事实证明,结果是特定于硬件的,因此,虽然[1]在我的笔记本电脑(x64 i5-4260U)上速度较慢,但在我的电脑上速度较快(但只有很小的一部分,如5%)。

更新:额外的加载/存储可以减少Sandybridge系列上的存储转发延迟,因此,CODE2中的额外工作可能加快了两个循环所依赖的循环承载依赖链的速度。(因为
-O0
将循环计数器保留在内存中)

这个效果是,包括我在写这个答案时的沙桥,OP的哈斯韦尔,年的布罗德韦尔,年的天湖


你应该做什么 如果您对代码如何编译为asm感到好奇,请将代码放在无法优化的函数中。在上,您可以看到gcc 5.1及更高版本(位于
-O3-march=native-mtune=native
)如何看穿字节的ORing,并在加载时动态使用
movbe
(move big-endian)来加载bwap。icc、clang和较旧的gcc发出指令,加载单个字节并将它们移动到位

令我失望的是,编译器没有看透字节的ORing,即使我颠倒了顺序,只进行了少量的endian加载,而不是大端ian加载。(请参阅godbolt上的本机、big和little endian函数。)即使将类型更改为uint32_t和uint8_t,对gcc>=5.1以外的编译器也没有帮助

显然,随着优化的进行,编译器会丢弃那些只设置了一个未使用变量的循环。他们只需调用
clock
两次,printf,然后将答案立即移动到
eax
。如果您希望实际对某个对象进行基准测试,请将其与使用常量数据调用它的函数分开编译。这样,您就可以有简单的代码,将其工作作为函数参数,并且它不能内联到传递常量数据的调用程序中

此外,gcc将
main
视为一个“冷”函数,并没有像普通函数那样对其进行大量优化。在大多数程序中,
main
不包含内部循环


为什么
-O0
asm这么慢? 显然,从
-O0
,代码非常可怕,存储到内存中,甚至增加内存中的循环计数器。弄清楚为什么它的运行速度比我预期的还要慢仍然有点有趣,代码1每时钟不到一个insn

两段代码都没有显示整个循环。可能移除循环体仍会留下一个缓慢的循环。我认为循环本身就是问题所在,而且效率太低,以至于CPU有时间在代码2中执行所有额外的指令,而不会看到任何减速


TL;DR:这两个循环都受到
add$1,-0x24(%rbp)
的限制,这会增加内存中的循环计数器。循环携带的依赖链中的6个周期延迟解释了为什么两个循环的速度相同

我不知道为什么CODE2中的额外指令会以某种方式帮助接近理论上的最大每次迭代6个周期,但这并不是任何人编写的代码中都会出现的瓶颈。将循环计数器保存在寄存器中,不要将相同地址的读-修改-写指令放入循环携带的依赖链中。()


有关我对代码所做的更改,请参见。(迭代次数增加了100倍,因此运行时控制了启动开销的噪音。)为了前3个函数的好处,该链接启用了优化

GooBead没有C模式,只有C++,而且GGC-4.92比GOODBET显示了一个不坏的循环。(g++实现了一个

for(){}
循环,它与所写的完全一样,顶部是cmp/jcc,底部是jmp。即使在
-O0
中,gcc也使用
do{}while(count++
结构,底部只有一个cmp/jle

我不知道任何真正的汇编程序,但可以读取GCC-S的输出 评估给定C代码的实际成本

好吧,过去常这样想:“是的,像MUL这样的一些业务很不错 价格昂贵,但如果一个组件比另一个组件大X倍,那么 应该慢一点”

首先要寻找的是吞吐量和延迟瓶颈。在Intel上,这意味着每个时钟的吞吐量为4 UOP,如果长的依赖链是一个限制,则小于4 UOP。然后是每个执行端口的瓶颈。例如,每个时钟有两个内存操作,其中最多一个是存储。每个时钟最多一个
mul
,因为只有一个E3 ALU端口有一个整数乘法器

有关优化指南、微体系结构文档以及它们可以运行的指令延迟/吞吐量/UOP/端口表,请参阅

在SandyBridge(我的系统)和Haswell(你的系统)上,由于将循环计数器保留在内存中,您的循环严重受限,Agner Fog的表的延迟为
add
,内存目标为6个时钟。它的运行速度不可能超过每6个时钟每次迭代一次。循环中有6条指令,即每周期1 insn

实际上,我得到的吞吐量比这要低。可能作为
add
读修改写操作一部分的存储有时会被循环中的其他加载/存储延迟。IDK为什么CODE2稍快,这很奇怪。可能是
#include <stdio.h>
#include <time.h>
#include <stdint.h>

#define REPETITIONS 32
#define ITERATIONS 90000

#define CODE1                   \
  for (int i = 0; i < ITERATIONS; ++i) {    \
    val = *((int32_t*)bytes);           \
  }

#define CODE2                   \
  for (int i = 0; i < ITERATIONS; ++i) {    \
    val = bytes[3] |                \
      (bytes[2] << 8) |             \
      (bytes[1] << 16) |            \
      (bytes[0] << 24);             \
  }

int main(void) {
  clock_t minTs = 999999999, ts;
  unsigned char bytes[4] = {0, 0, 0, 5};    
  int32_t val;                  

  for (int i = 0; i < REPETITIONS; ++i) {
    ts = clock();

    CODE1; // Or CODE2

    ts = clock() - ts;
    if (ts < minTs) minTs = ts;
  }

  printf("ts: %ld\n", minTs);

  return val;
}
   ###### CODE1 inner loop, profiling on cycles
 13.97 │400680:   lea    -0x10(%rbp),%rax
       │400684:   mov    (%rax),%eax
       │400686:   mov    %eax,-0x2c(%rbp)
       │400689:   addl   $0x1,-0x24(%rbp)
 13.84 │40068d:   cmpl   $0x89543f,-0x24(%rbp)
 72.19 │400694: ↑ jle    400680 <code1+0x4a>
       ## end of the loop
        400696:   callq  4004e0 <clock@plt>
        40069b:   sub    -0x18(%rbp),%rax
       #CODE2
 15.08 │400738:   movzbl -0xd(%rbp),%eax
  0.88 │40073c:   movzbl %al,%eax
  0.05 │40073f:   movzbl -0xe(%rbp),%edx
       │400743:   movzbl %dl,%edx
 13.91 │400746:   shl    $0x8,%edx
  0.70 │400749:   or     %eax,%edx
  0.05 │40074b:   movzbl -0xf(%rbp),%eax
       │40074f:   movzbl %al,%eax
 12.70 │400752:   shl    $0x10,%eax
  0.60 │400755:   or     %eax,%edx
  0.09 │400757:   movzbl -0x10(%rbp),%eax
  0.05 │40075b:   movzbl %al,%eax
 13.03 │40075e:   shl    $0x18,%eax
  0.70 │400761:   or     %edx,%eax
  0.14 │400763:   mov    %eax,-0x2c(%rbp)
  0.09 │400766:   addl   $0x1,-0x24(%rbp)
 13.63 │40076a:   cmpl   $0x89543f,-0x24(%rbp)
 28.29 │400771: ↑ jle    400738 <code2+0x4a>
     ## end of the loop
        400773: → callq  4004e0 <clock@plt>
        400778:   sub    -0x18(%rbp),%rax 
## CODE1 ##
 Performance counter stats for './a.out 1' (4 runs):

        589.117019      task-clock (msec)         #    0.999 CPUs utilized            ( +-  1.30% )
     2,068,118,847      cycles                    #    3.511 GHz                      ( +-  0.48% )
     1,729,505,746      instructions              #    0.84  insns per cycle        
                                                  #    0.86  stalled cycles per insn  ( +-  0.01% )
     2,018,533,639      uops_issued_any           # 3426.371 M/sec                    ( +-  0.02% )
     5,648,003,370      uops_dispatched_thread    # 9587.235 M/sec                    ( +-  2.51% )
     3,170,497,726      uops_retired_all          # 5381.779 M/sec                    ( +-  0.01% )
     2,018,126,488      uops_retired_retire_slots # 3425.680 M/sec                    ( +-  0.01% )
     1,491,267,343      stalled-cycles-frontend   #   72.11% frontend cycles idle     ( +-  0.66% )
        27,192,780      stalled-cycles-backend    #    1.31% backend  cycles idle     ( +- 68.75% )

       0.589651097 seconds time elapsed                                          ( +-  1.32% )
## CODE2 ##
peter@tesla:~/src/SO$ ocperf.py stat -r4 -e task-clock,cycles,instructions,uops_issued.any,uops_dispatched.thread,uops_retired.all,uops_retired.retire_slots,stalled-cycles-frontend,stalled-cycles-backend ./a.out 2
perf stat -r4 -e task-clock,cycles,instructions,cpu/event=0xe,umask=0x1,name=uops_issued_any/,cpu/event=0xb1,umask=0x1,name=uops_dispatched_thread/,cpu/event=0xc2,umask=0x1,name=uops_retired_all/,cpu/event=0xc2,umask=0x2,name=uops_retired_retire_slots/,stalled-cycles-frontend,stalled-cycles-backend ./a.out 2
CODE2: ts: 16499
CODE2: ts: 16535
CODE2: ts: 16517
CODE2: ts: 16529

 Performance counter stats for './a.out 2' (4 runs):

        543.886795      task-clock (msec)         #    0.999 CPUs utilized            ( +-  1.01% )
     2,007,623,288      cycles                    #    3.691 GHz                      ( +-  0.01% )
     5,185,222,133      instructions              #    2.58  insns per cycle        
                                                  #    0.11  stalled cycles per insn  ( +-  0.00% )
     5,474,164,892      uops_issued_any           # 10064.898 M/sec                    ( +-  0.00% )
     7,586,159,182      uops_dispatched_thread    # 13948.048 M/sec                    ( +-  0.00% )
     6,626,044,316      uops_retired_all          # 12182.764 M/sec                    ( +-  0.00% )
     5,473,742,754      uops_retired_retire_slots # 10064.121 M/sec                    ( +-  0.00% )
       566,873,826      stalled-cycles-frontend   #   28.24% frontend cycles idle     ( +-  0.03% )
         3,744,569      stalled-cycles-backend    #    0.19% backend  cycles idle     ( +-  2.32% )

       0.544190971 seconds time elapsed                                          ( +-  1.01% )