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 复杂寻址模式是否有额外的内存加载开销?_Performance_Assembly_X86_Micro Optimization - Fatal编程技术网

Performance 复杂寻址模式是否有额外的内存加载开销?

Performance 复杂寻址模式是否有额外的内存加载开销?,performance,assembly,x86,micro-optimization,Performance,Assembly,X86,Micro Optimization,这些mov加载指令之间的性能是否存在差异?与简单寻址模式相比,更复杂的寻址模式是否有额外的开销(延迟或吞吐量) # AT&T syntax # Intel syntax: movq (%rsi), %rax mov rax, [rsi] movq (%rdi, %rsi), %rax mov rax, [rdi + rsi] movq (%rdi, %rsi, 4), %rax

这些
mov
加载指令之间的性能是否存在差异?与简单寻址模式相比,更复杂的寻址模式是否有额外的开销(延迟或吞吐量)

# AT&T syntax                       # Intel syntax:
movq (%rsi), %rax                   mov  rax, [rsi]

movq (%rdi, %rsi), %rax             mov  rax, [rdi + rsi]

movq (%rdi, %rsi, 4), %rax          mov  rax, [rdi + rsi*4]

取决于具体的CPU;主要是“不,没有额外的开销”。然而

大多数CPU都有无序的内核,这意味着它们以最快的顺序执行指令,而不是以给出指令的顺序执行指令。为此,一条指令(例如,
movq(%rdi,%rsi,4),%rax
)不能发生,直到它所依赖的事情完成(例如,
rdi
rsi
中的值已知)

例如,这两条指令可以并行执行(因为第二条指令不依赖于第一条指令):

这两条指令不能并行执行(第二条指令必须等到第一条指令完成):

还要注意,一段代码的瓶颈可能不是执行。如果瓶颈是指令获取,那么较大的指令将更糟糕;如果瓶颈是指令解码,那么更复杂的指令可能更糟;如果瓶颈是数据缓存带宽,那么任何读/写到内存的东西都可能变得更糟,等等


基本上;你不能孤立地看个别说明,然后决定它们是好是坏。您必须查看多条指令的整个序列,以便了解对以前指令的任何依赖性(及其延迟);您必须知道瓶颈是什么(例如,通过性能监控工具);如果你知道所有这些,那么你可以做出一个“有根据的猜测”,它只对少数CPU真正有用(因为不同的CPU有不同的特性)。

是的,在最近的Intel CPU上“复杂寻址”会有开销。成本是额外的一个延迟周期(例如,使用复杂寻址的正常GP负载为5个周期,而使用简单寻址的为4个周期)

简单寻址是任何形式的
[reg+offset]
,其中立即
偏移量
介于0和2047之间(包括0和2047之间)

复杂寻址不是简单寻址

特别是,任何带有两个寄存器的寻址模式,如示例
[rdi+rsi]
[rdi+rsi*4]
都是复杂的寻址,需要额外的周期

有一种例外情况:如果索引寄存器1是通过归零习惯用法归零的(比如
xor edi,edi
,而不是
mov edi,0
),则不需要支付复杂的寻址罚款



1索引寄存器是1、2、4或8的乘积,即
[rdi+rsi*4]
中的
rsi
。在这种情况下,两个寄存器都不显示乘法器,如
[rdi+rsi]
乘法器是
1
,您必须检查汇编器以了解如何指定哪个是
索引
,哪个是
位移
。nasm似乎使用第二个寄存器作为索引。

一些微体系结构可能需要额外的延迟周期来生成复杂寻址模式下的地址(因为IDK是这部分的答案,所以不会将其作为答案发布),但吞吐量不应受到影响。对于非
mov
-加载的指令,寻址模式的选择在英特尔SnB系列上很重要,因为。所以
mov%rax,(%rdi,%rsi)
是2个计量单位,就像
add(%rdi,%rsi),%rax
,但是
mov(%rdi,%rsi),%rax
是1个计量单位。你有什么具体的微体系结构吗?e、 g.英特尔SnB/Haswell系列?AMD推土机家族?英特尔原子?@PeterCordes没有考虑任何特定的体系结构。普通的x86-64系列。这三个微体系结构系列都实现了x86-64 ISA,但具有不同的性能特征。在不同的微体系结构中,有几件事情是相同的,但许多事情是不同的。你可以比较单个指令的吞吐量和延迟,假设它们的所有输入都准备好了。e、 g.
lea(%rax,%rax),%ecx具有1c延迟和2个每周期吞吐量(Intel Haswell,使用p1/p5),而
imul$2,%eax,%ecx
具有3c延迟和1个每周期吞吐量(仅限Haswell,p1)。两者都做同样的事情,但你总是会选择lea,因为它在各个方面都更好。(Agner Fog的表格显示,
lea
的三分量地址只有3c延迟和更少的吞吐量,仅在p1上运行。我以乘以2为例,因此代码大小也不是问题。)使用不同的寻址模式意味着改变周围的代码,这是一个公平的观点。使用索引作为循环变量而不是指针增量意味着您可以向上计数到零,从而从循环中删除1个uop。(基址寄存器=超过阵列末端的一个,因此您可以使用
inc
/
jz
或其他东西退出环路。)这是否也可以避免微熔合负载的反层压。这是否仍然适用于
[zerored_reg+rsi*4]
?(你可以用它在输入数组中比输出数组快4倍,但这是一个延伸。)这提醒了我,我一直想问的是,如何构造一个对存储地址延迟敏感的测试用例,这样我们就可以测量作为存储地址的
[rdi+16]
是否比
[rdi+4096]
快。英特尔的优化手册只提到使用简单寻址模式的负载延迟较低。上次尝试时,我在尝试通过存储/重新加载来创建一个依赖链时遇到了问题,地址中有额外的
imul reg,1
。@PeterCordes-这只是我预期的位移,尽管原始答案文本似乎表明不是这样。我修正了它。@PeterCordes-人们可以把测量存储延迟的困难看作是一种迹象,表明它在实际代码中不太有趣,
movq (%rdi), %edi
movq (%rsi), %rax
movq (%rdi), %rdi
movq (%rdi, %rsi), %rax