- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
以下所有指令都做同样的事情:将 %eax
设置为零。哪种方式是最佳的(需要最少的机器周期)?
xorl %eax, %eax
mov $0, %eax
andl $0, %eax
最佳答案
TL;DR 摘要 : xor same, same
是所有 CPU 的最佳选择 0x2518411921没有其他方法比它有任何优势,它至少比任何其他方法都有一些优势。它是 Intel 和 AMD 官方推荐的,以及编译器的作用。在 64 位模式下,仍然使用 xor r32, r32
,因为 writing a 32-bit reg zeros the upper 32 。 xor r64, r64
是一个字节的浪费,因为它需要一个REX前缀。
更糟糕的是,Silvermont 只将 xor r32,r32
识别为 dep-breaking,而不是 64 位操作数大小。因此 即使仍然需要 REX 前缀,因为您将 r8..r15 归零,请使用 xor r10d,r10d
,而不是 xor r10,r10
0x251819212134。
GP-整数示例:
xor eax, eax ; RAX = 0. Including AL=0 etc.
xor r10d, r10d ; R10 = 0. Still prefer 32-bit operand-size.
xor edx, edx ; RDX = 0
; small code-size alternative: cdq ; zero RDX if EAX is already zero
; SUB-OPTIMAL
xor rax,rax ; waste of a REX prefix, and extra slow on Silvermont
xor r10,r10 ; bad on Silvermont (not dep breaking), same as r10d on other CPUs because a REX prefix is still needed for r10d or r10.
mov eax, 0 ; doesn't touch FLAGS, but not faster and takes more bytes
and eax, 0 ; false dependency. (Microbenchmark experiments might want this)
sub eax, eax ; same as xor on most but not all CPUs; bad on Silvermont for example.
xor cl, cl ; false dep on some CPUs, not a zeroing idiom. Use xor ecx,ecx
mov cl, 0 ; only 2 bytes, and probably better than xor cl,cl *if* you need to leave the rest of ECX/RCX unmodified
pxor xmm, xmm
完成。这通常是 gcc 所做的(甚至在使用 FP 指令之前)。xorps xmm, xmm
是有道理的。它比 pxor
短一个字节,但 xorps
在 Intel Nehalem 上需要执行端口 5,而 pxor
可以在任何端口(0/1/5)上运行。 (Nehalem 的整数和 FP 之间的 2c 旁路延迟延迟通常不相关,因为乱序执行通常可以将其隐藏在新依赖链的开始处)。xorps
和 pxor
的处理方式相同(作为向量整数指令)。vpxor xmm, xmm, xmm
是归零 YMM(AVX1/AVX2) 或 ZMM(AVX512) 或任何 future 向量扩展的不错选择。不过,vpxor ymm, ymm, ymm
不需要任何额外的字节来编码,并且在 Intel 上运行相同,但在 Zen2(2 uop)之前在 AMD 上速度较慢。 AVX512 ZMM 归零需要额外的字节(对于 EVEX 前缀),因此应该首选 XMM 或 YMM 归零。 # Good:
xorps xmm0, xmm0 ; smallest code size (for non-AVX)
pxor xmm0, xmm0 ; costs an extra byte, runs on any port on Nehalem.
xorps xmm15, xmm15 ; Needs a REX prefix but that's unavoidable if you need to use high registers without AVX. Code-size is the only penalty.
# Good with AVX:
vpxor xmm0, xmm0, xmm0 ; zeros X/Y/ZMM0
vpxor xmm15, xmm0, xmm0 ; zeros X/Y/ZMM15, still only 2-byte VEX prefix
#sub-optimal AVX
vpxor xmm15, xmm15, xmm15 ; 3-byte VEX prefix because of high source reg
vpxor ymm0, ymm0, ymm0 ; decodes to 2 uops on AMD before Zen2
# Good with AVX512
vpxor xmm15, xmm0, xmm0 ; zero ZMM15 using an AVX1-encoded instruction (2-byte VEX prefix).
vpxord xmm30, xmm30, xmm30 ; EVEX is unavoidable when zeroing zmm16..31, but still prefer XMM or YMM for fewer uops on probable future AMD. May be worth using only high regs to avoid needing vzeroupper in short functions.
# Good with AVX512 *without* AVX512VL (e.g. KNL / Xeon Phi)
vpxord zmm30, zmm30, zmm30 ; Without AVX512VL you have to use a 512-bit instruction.
# sub-optimal with AVX512 (even without AVX512VL)
vpxord zmm0, zmm0, zmm0 ; EVEX prefix (4 bytes), and a 512-bit uop. Use AVX1 vpxor xmm0, xmm0, xmm0 even on KNL to save code size.
见 Is vxorps-zeroing on AMD Jaguar/Bulldozer/Zen faster with xmm registers than ymm? 和k0..7
掩码寄存器。 SSE/AVX vpcmpeqd
对许多人来说是破坏性的(尽管仍然需要一个 uop 来写 1s),但用于 ZMM regs 的 AVX512 vpternlogd
甚至不是破坏性的。在循环内部考虑从另一个寄存器复制而不是使用 ALU uop 重新创建寄存器,尤其是使用 AVX512。sub same,same
识别为归零习语,如 xor
,但 识别任何归零习语的所有 CPU 都识别 0x25181223181342121243131342121只需使用 xor
这样您就不必担心哪个 CPU 识别哪个归零习语。xor
(作为公认的归零习语,与 xor
不同)有一些明显和一些微妙的优势(总结列表,然后我将扩展这些):mov reg, 0
更小的代码大小。 (所有 CPU) mov reg,0
,因此通过调度程序的完美决策(这在实践中并不总是发生),即使它们都需要,HSW 仍然可以维持每个时钟 4 uop ALU 执行端口。mov r32, imm32
在寄存器重命名阶段处理而无需执行单元(未融合域中的零 uop),但错过了它仍然是一个 uop 的事实融合域。现代英特尔 CPU 每个时钟可以发出和停用 4 个融合域 uops。这就是每个时钟限制 4 个零的来源。寄存器重命名硬件的复杂性增加只是将设计宽度限制为 4 的原因之一。(Bruce 写了一些非常出色的博客文章,比如他关于 FP math and x87 / SSE / rounding issues 的系列文章,我强烈推荐)。xor
在与 mov immediate
相同的 EX0/EX1 整数执行端口上运行。 xor
也可以在 AGU0/1 上运行,但这仅用于寄存器复制,不适用于立即数设置。因此,AFAIK,在 AMD 上,mov reg,reg
比 xor
的唯一优势是更短的编码。它也可能节省物理寄存器资源,但我还没有看到任何测试。mov
将 将寄存器标记为将上半部分归零 ,所以 xor
/xor eax, eax
/0x251818431234 部分 CPU 避免了/0x2518431234134133434334334333433433433434334343343434343434343434334343434343434343434343434343434343434343343343334即使没有 inc al
,当修改高 8 位( inc eax
)然后读取整个寄存器时,IvB 只需要合并 uop,Haswell 甚至将其删除。The processor recognizes the XOR of a register with itself as settingit to zero. A special tag in the register remembers that the high partof the register is zero so that EAX = AL. This tag is remembered evenin a loop:
; Example 7.9. Partial register problem avoided in loop
xor eax, eax
mov ecx, 100
LL:
mov al, [esi]
mov [edi], eax ; No extra uop
inc esi
add edi, 4
dec ecx
jnz LL
(from pg82): The processor remembers that the upper 24 bits of EAX are zero as long asyou don't get an interrupt, misprediction, or other serializing event.
xor
不被认为是归零习惯用法,至少在 PIII 或 PM 等早期 P6 设计中是这样。如果他们在后来的 CPU 上使用晶体管来检测它,我会感到非常惊讶。AH
设置标志 ,这意味着您在测试条件时必须小心。不幸的是,由于 mov reg, 0
仅适用于 8 位目标 ,因此您通常需要注意避免部分寄存器惩罚。xor
,并将谓词编码在 r/m 字段的源寄存器 3 位字段(其他一些单操作数指令将它们用作操作码位的方式)。但是他们没有这样做,无论如何这对 x86-32 没有帮助。setcc
/set flags/setcc r/m
/read full register:...
call some_func
xor ecx,ecx ; zero *before* the test
test eax,eax
setnz cl ; cl = (some_func() != 0)
add ebx, ecx ; no partial-register penalty here
这在所有 CPU 上都具有最佳性能(没有停顿、合并 uops 或错误依赖)。xor
、 setcc
,并且您要么没有备用寄存器,要么希望将 cmp/jle
完全排除在未采用的代码路径之外。sete
/xor
会对较旧的 Intel CPU 产生重大影响,但在较新的 Intel CPU 上仍然会更糟。mov reg, 0
/setcc
可能是英特尔 P6 和 SnB 系列的最佳选择,如果您不能在标志设置指令之前进行异或零。这应该比在异或归零后重复测试要好。 (甚至不要考虑 setcc
/movzx r32, r8
或 sahf
/lahf
)。 IvB 可以消除 pushf
(即通过寄存器重命名处理它,没有执行单元或延迟,如异或归零)。 Haswell的,后来只有消除常规popf
指令,因此movzx r32, r8
需要一个执行单元,具有非零延迟,使得测试/mov
/movzx
比setcc
/测试/movzx
差,但至少还要好,因为测试/xor
/setcc
(和在较旧的 CPU 上要好得多)。mov r,0
/setcc
而不先归零是不好的,因为它们不会单独跟踪子寄存器的 deps。寄存器的旧值会有一个错误的依赖。当 setcc
/test/movzx
不是一个选项时,使用 mov reg, 0
/setcc
进行归零/依赖破坏可能是最好的选择。xor
的输出宽于 8 位,则不需要将任何内容归零。但是,如果您选择的寄存器最近是长依赖链的一部分,请注意对 P6/SnB 以外的 CPU 的错误依赖。 (如果您调用的函数可能会保存/恢复您正在使用的寄存器的一部分,请注意导致部分 reg 停顿或额外的 uop。)setcc
立即为零 不是特殊情况,独立于我所知道的任何 CPU 上的旧值,因此它不会破坏依赖链。它与 setcc
相比没有任何优势,但有许多缺点。and
是一些但不是所有的CPU,而xor
上大家公认的。)sub same,same
确实打破了依赖链上的寄存器的旧值(无论源值如何,零与否,因为这就是 xor same,same
的工作方式)。 mov
仅在 src 和 dest 是同一个寄存器的特殊情况下破坏依赖链,这就是为什么 mov
被排除在特别识别的依赖破坏者列表之外的原因。 (另外,因为它不被认为是归零习语,还有其他好处。)xor
-zeroing 识别为依赖项破坏者,只是为了避免部分寄存器停顿 0x2513411921 的目的的归零习语,所以在某些情况下值得mov
和 xor
都归零以破坏 dep 然后再次归零 + 设置内部标记位,即高位为零,因此 EAX=AX=AL。mov
链的依赖。不幸的是,这证实了 Agner Fog 的结果。xor
归零以避免触及标志,只要您不引入代码大小以外的性能问题。避免破坏标志是不使用 imul
的唯一合理原因,但有时如果您有备用寄存器,您可以在设置标志之前进行异或零。mov
- xor
之前的零比 mov
之后的延迟更好(英特尔除外,当您可以选择不同的寄存器时),但代码大小更糟。
关于performance - 在 x86 程序集 : xor, mov 或 and 中将寄存器设置为零的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33666617/
这个问题在这里已经有了答案: Differences between general purpose registers in 8086: [bx] works, [cx] doesn't? (3
我最近开始探索计算机体系结构领域。在研究指令集体系结构时,我遇到了“ mov”指令,该指令将数据从一个位置复制到另一个位置。我知道某些类型的mov'指令是有条件的,而有些则需要添加偏移量或位移来查找特
我正在研究使用模拟 MSP430 CPU 的 Microcorruption CTF。 我见过几个 mov 指令示例,例如: mov sp, r4 ;将堆栈指针的值移至寄存器4 mov #0xfffc
我不明白 MOV 和 MOV ptr 之间的区别。 例如,在这段 C 代码中: unsigned char x, y; x = 2; 汇编中的第二行是: `MOV x, 2` 但是这个 C 代码的第二
MOV可能是每个人在学习ASM时都会学到的第一条指令。 刚才我遇到了一本书Assembly Language Programming in GNU/Linux for IA32 Architectur
下面两行有什么区别? mov ax, bx mov ax, [bx] 如果bx包含值100h,并且内存地址100h处的值是23,那么第二个是否将23复制到ax? 另外,下面两行有什么区别? mov a
我编写了一个基本的 C 程序,它定义了一个整型变量 x,将其设置为零并返回该变量的值: #include int main(int argc, char **argv) { int x;
我是一个初学者,正在编写汇编程序以使用以下代码打印从 1 到 9 的数字: section .text global _start _start:
mov (%rax),%eax有什么区别和 mov %rax,%eax ?我确定这是一个简单的问题,但我在任何地方都找不到答案。 这是提示我的问题的原始代码: mov -0x8(%rbp),%r
有人可以解释一下这三个指令的功能吗? ORG 1000H MOV AX,CS MOV DS,AX 我知道理论上的代码、数据和额外段是什么,但是: 在这个程序中它们是如何实现的? 为什么整个
在 8086 架构的 16 位 MS-DOS 应用程序中,mov bx,ax 和 mov bh,ah 之间的速度有区别吗? 最佳答案 您没有指定架构,但至少在 8086 中指定, 286 , 386和
我正在反汇编一些代码,我发现: mov eax, cr3 mov cr3, eax 这些线的作用是什么? 这是 x86 低级(BIOS/固件/引导加载程序之前)初始化代码。我们甚至还没有设置缓存。 最
使用 nasm 组装此代码时: BITS 64 mov eax, 0x1 mov rax, 0x1 我得到这个输出: b8 01 00 00 00 b8 01 00 00 00 这是 mov eax,
我试图理解 Intel 语法和 AT&T 语法之间的差异(我使用 GNU as)。 我有两个文件,intel.s: .intel_syntax noprefix val: mov eax, v
我需要一种非常精确的方法来加速音频。 我正在为 OpenDCP(一种用于制作数字电影包的开源工具)准备电影,以便在影院放映。 我的源文件通常是 23.976fps 和 48.000kHz 音频的 qu
通过查看英特尔指令卷,我发现了这一点: 1) 88/r MOV r/m8,r8 2) 8A/r MOV r8,r/m8 当我在 NASM 中写下这样的一行,并使用列表选项将其组装时: mov al
Intel 手册说 mov 有两种变体,涉及内存和 32 位立即操作数: MOV r/m32, imm32 MOV r/m64, imm32 第一个复制四个字节,第二个复制八个字节,采用给定的 32
我已经处理了一天了,最后不得不出来问。我想获取一个无声的 prores mov 文件(但显然确实有时间码轨道)并将其与 6 个单声道 wav 文件无损混合,使 6 个单声道 wav 在最终 mov 中
这是我的代码: section .data digit db 0,10 section .text global _start _start: call _printRAXD
我在问 mov需要计算该地址的指令,即(在 at&t 语法中mov i(r, r, i), reg或 mov reg, i(r, reg, i)必须在端口 1 上执行,因为它们实际上是带有 3 个操作
我是一名优秀的程序员,十分优秀!