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 为什么不是';t movinti更慢,在循环中重复存储到同一地址?_Performance_X86_Micro Optimization - Fatal编程技术网

Performance 为什么不是';t movinti更慢,在循环中重复存储到同一地址?

Performance 为什么不是';t movinti更慢,在循环中重复存储到同一地址?,performance,x86,micro-optimization,Performance,X86,Micro Optimization,根据perf,它以每周期1.82条指令的速度运行。我不明白为什么这么快。毕竟,它必须存储在内存(RAM)中,所以速度应该很慢 另外,是否存在任何循环携带依赖项 编辑 现在,每次迭代需要5个周期。为什么?毕竟,仍然没有循环携带的依赖关系。结果是可信的。循环代码由以下指令组成。根据,这些时间安排如下: section .text %define n 100000 _start: xor rcx, rcx jmp .cond .begin: movnti [array+rcx],

根据
perf
,它以每周期1.82条指令的速度运行。我不明白为什么这么快。毕竟,它必须存储在内存(RAM)中,所以速度应该很慢

另外,是否存在任何循环携带依赖项

编辑
现在,每次迭代需要5个周期。为什么?毕竟,仍然没有循环携带的依赖关系。

结果是可信的。循环代码由以下指令组成。根据,这些时间安排如下:

section .text

%define n    100000

_start:
xor rcx, rcx

jmp .cond
.begin:
    movnti [array+rcx], eax

.cond:
    add rcx, 1 
    cmp rcx, n
    jl .begin


section .data
array times n dq 0
所以

  • MOVNTI消耗2个UOP,1个在端口2或3中,1个在端口4中
  • 添加在端口0或1或5或6中消耗1个UOP
  • CMP和Jcc宏熔合到表中的最后一行,导致消耗1 uOp
因为无论是
ADD
还是
CMP+Jcc
都不取决于
moventi
的结果,所以它们可以(几乎)在最近的体系结构上并行执行,例如使用端口1、2、4、6。最坏的情况是
ADD
CMP+Jcc
之间的延迟为1

这很可能是代码中的一个设计错误:实际上,您正在向同一地址
[array]
写入100000次,因为您没有调整地址

重复写入甚至可以在以下情况下进入一级缓存:

如果为非时态存储指定的内存地址位于不可缓存(UC)或写保护(WP)内存区域中,则要写入的区域的内存类型可以覆盖非时态提示

但它看起来不像这样,也不会有很大的区别,因为即使写入内存,内存速度也将是限制因素


例如,如果您有一个3GHz的CPU和1600MHz的DDR3-RAM,这将导致每个内存周期有3/1.6=1.875个CPU周期。这似乎是有道理的。

moventi
在重复写入同一地址时,显然可以维持每个时钟一个吞吐量

我认为
moventi
一直在写入相同的文件,并且不会经常刷新,因为没有其他加载或存储发生。(该链接是关于使用SSE4.1 NT加载从WC视频内存复制,以及使用NT存储存储到普通内存。)

因此,NT写组合填充缓冲区就像一个缓存,用于将多个重叠的NT存储区缓存到同一地址,并且每次写入实际上都会命中填充缓冲区,而不是进入DRAM

DDR DRAM仅支持突发传输命令。如果每个
movinti
都产生了一个4B写操作,而该写操作实际上对内存芯片是可见的,那么它就不可能运行得那么快。内存控制器要么读取/修改/写入,要么执行中断的突发传输,因为。另见

我们可以通过同时在多个核上运行测试来进一步证明这一点因为它们根本不会降低彼此的速度,所以我们可以确定,写操作只是偶尔从CPU核心出来,并争夺内存周期。


您的实验没有显示循环以每时钟4条指令(每次迭代一个周期)运行的原因是您使用了如此小的重复计数。100k个周期几乎不占启动开销(包括
perf
的定时)

例如,在具有双通道DDR2 533MHz的Core2 E6600(Merom/Conroe)上,包括所有进程启动/退出内容的总时间为0.113846 ms。这仅为266007个周期

更合理的微基准显示每个循环一次迭代(一次
movinti
):

Instruction    regs     fused  unfused  ports   Latency Reciprocal Throughput
---------------------------------------------------------------------------------------------------------------------------
MOVNTI         m,r      2      2        p23 p4  ~400     1
ADD            r,r/i    1      1        p0156   1       0.25    
CMP            r,r/i    1      1        p0156   1       0.25    
Jcc            short    1      1        p6      1       1-2    if predicted that the jump is taken
Fused CMP+Jcc  short    1      1        p6      1       1-2    if predicted that the jump is taken
()

并行运行: 我期待完美的SMP可扩展性(超线程除外),最多可扩展到任意数量的内核。e、 g.在10核Xeon上,该测试的10个副本可以同时运行(在单独的物理核上),并且每个副本将在相同的时间内完成,就像它单独运行一样。(不过,如果您测量的是挂钟时间而不是周期计数,单芯turbo与多芯turbo也将是一个因素。)


zx485的uop计数很好地解释了为什么循环不会受到前端或未使用的域执行资源的限制

然而,这推翻了他的理论,即CPU与内存时钟的比率与此有关。然而,有趣的巧合是,OP选择了一个计数,恰好使最终的IPC总数如此


另外,是否存在任何循环携带依赖项

是的,循环计数器。(1个周期)。顺便说一句,您可以通过使用
dec
/
jg
向零倒计时来保存insn,而不是使用
cmp


写后内存依赖不是正常意义上的“真正”依赖,但CPU必须跟踪它。CPU不会“注意”重复写入相同的值,因此它必须确保最后一次写入是“计数”的


这被称为一个。我认为这个术语在谈论记忆时仍然适用,而不是寄存器。

我仍然无法理解。单次操作(非环路)movnti需要约120个周期。我知道在循环中(没有依赖项),它可以并发执行很多操作。但是多少钱?问题是它是内存操作,因此必须加以限制。据说:对内存的读/写速度很慢。请解释:)嗯,我也没有一个很好的解释来解释~400的延迟值。这是实验的结果。但这并不是问题所在:问题是您正在将相同的4个字节反复写入
[array]
的同一地址,这就是前面提到的设计错误。您可以将行
movinti[array],eax
替换为
movinti array[rcx*4],eax
(您可能需要根据汇编器的语法进行调整),然后再次测量以查看是否产生不同的结果。但是,正如我在回答中所解释的,内存是中计算的瓶颈
Instruction    regs     fused  unfused  ports   Latency Reciprocal Throughput
---------------------------------------------------------------------------------------------------------------------------
MOVNTI         m,r      2      2        p23 p4  ~400     1
ADD            r,r/i    1      1        p0156   1       0.25    
CMP            r,r/i    1      1        p0156   1       0.25    
Jcc            short    1      1        p6      1       1-2    if predicted that the jump is taken
Fused CMP+Jcc  short    1      1        p6      1       1-2    if predicted that the jump is taken
global _start
_start:
    xor ecx,ecx
.begin:
    movnti  [array], eax
    dec     ecx
    jnz     .begin         ; 2^32 iterations

    mov eax, 60     ; __NR_exit
    xor edi,edi
    syscall         ; exit(0)

section .bss
array resb 81920
$ asm-link movnti-same-address.asm
+ yasm -felf64 -Worphan-labels -gdwarf2 movnti-same-address.asm
+ ld -o movnti-same-address movnti-same-address.o
$ perf stat -e task-clock,cycles,instructions ./movnti-same-address 

 Performance counter stats for './movnti-same-address':

       1835.056710      task-clock (msec)         #    0.995 CPUs utilized          
     4,398,731,563      cycles                    #    2.397 GHz                    
    12,891,491,495      instructions              #    2.93  insns per cycle        
       1.843642514 seconds time elapsed
$ time ./movnti-same-address; time ./movnti-same-address & time ./movnti-same-address &

real    0m1.844s / user    0m1.828s    # running alone
[1] 12523
[2] 12524
peter@tesla:~/src/SO$ 
real    0m1.855s / user    0m1.824s    # running together
real    0m1.984s / user    0m1.808s
# output compacted by hand to save space