- 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的最佳选择。没有其他方法比它有任何优势,并且它比任何其他方法都至少具有某些优势。英特尔和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位操作数大小。因此,即使由于将r8..r15清零而仍需要REX前缀时,也应使用xor r10d,r10d
,而不是xor r10,r10
。
GP整数示例:
xor eax, eax ; RAX = 0. Including AL=0 etc.
xor r10d, r10d ; R10 = 0
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 everywhere else 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 al, al ; false dep on some CPUs, not a zeroing idiom. Use xor eax,eax
mov al, 0 ; only 2 bytes, and probably better than xor al,al *if* you need to leave the rest of EAX/RAX 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)或任何未来向量扩展置零的不错选择。但是,
vpxor ymm, ymm, ymm
不需要花费任何额外的字节来编码,并且在Intel上运行相同,但是在Zen2之前(2微秒)在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.
k0..7
掩码寄存器。 SSE / AVX
vpcmpeqd
在许多方面都是断断续续的(尽管仍然需要uop来写1),但是ZMM regs的AVX512
vpternlogd
甚至还没有断断续续。在循环内部,请考虑从另一个寄存器复制,而不是使用ALU uop(特别是使用AVX512)重新创建一个。
sub same,same
一样将
xor
识别为清零习惯,但是所有识别任何清零习惯的CPU都可以识别
xor
。只需使用
xor
,就不必担心哪个CPU可以识别哪个清零习惯。
xor
(与
mov reg, 0
不同,它是一种公认的归零惯用法)具有一些明显和微妙的优点(摘要列表,然后我将对其进行扩展):
mov reg,0
的代码大小。 (所有CPU)
mov r32, imm32
),因此,通过调度程序的完美决策(实际上并不总是这样),即使HSW即使它们始终保持每个时钟4 oups都需要ALU执行端口。
xor
是在寄存器重命名阶段处理的,不需要执行单元(未融合域中的零uops),但错过了它仍然存在的事实融合域中的一个uop。现代的Intel CPU可以每个时钟发出和退出4个融合域uops。这就是每个时钟限制4个零的来源。寄存器重命名硬件的复杂性增加只是将设计的宽度限制为4的原因之一。(Bruce写了一些非常出色的博客文章,例如他强烈推荐的
FP math and x87 / SSE / rounding issues系列文章)。
mov immediate
在与
xor
相同的EX0 / EX1整数执行端口上运行。
mov reg,reg
也可以在AGU0 / 1上运行,但这仅用于寄存器复制,而不能用于立即数设置。所以AFAIK,在AMD上,
xor
优于
mov
的唯一优势是较短的编码。它也可能节省物理寄存器资源,但是我还没有看到任何测试。
xor
会将寄存器标记为上半部分为零,因此
xor eax, eax
/
inc al
/
inc eax
避免了IvB之前的CPU常见的部分寄存器损失。即使没有
xor
,仅当高8位(
AH
)被修改然后读取整个寄存器时,IvB才需要合并uop,Haswell甚至将其删除。
; 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
mov reg, 0
不被视为归零习惯。如果他们花晶体管在以后的CPU上检测它,我会感到非常惊讶。
xor
设置标志,这意味着在测试条件时必须小心。不幸的是,由于
setcc
仅适用于8位目标地址,因此您通常需要注意避免部分注册处罚。
setcc r/m
,并且谓词编码在r / m字段的源寄存器3位字段中,那就太好了。 (其他一些单操作数指令将它们用作操作码位的方式)。但是他们没有这样做,这对x86-32毫无帮助。
xor
/设置标志/
setcc
/读取完整寄存器:
...
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
cmp/jle
,
sete
,或者您没有备用寄存器,或者您想将
xor
完全排除在未使用的代码路径之外。
mov reg, 0
/
setcc
在较旧的Intel CPU上将有很大的损失,而在较新的Intel上仍然会更糟。
setcc
/
movzx r32, r8
可能是Intel P6&SnB系列的最佳选择。这应该比在异或归零后重复测试更好。 (甚至不考虑
sahf
/
lahf
或
pushf
/
popf
)。 IvB可以消除
movzx r32, r8
(即通过寄存器重命名来处理它,而无需执行单元或等待时间,例如异或归零)。 Haswell及其以后仅消除常规的
mov
指令,因此
movzx
采用执行单元并且具有非零延迟,这使得test /
setcc
/
movzx
比
xor
/ test /
setcc
差,但仍然至少与test /
mov r,0
/
setcc
一样好(在较旧的CPU上更好)。
setcc
/
movzx
是不好的,因为它们不会分别跟踪子寄存器的dep。寄存器的旧值将有错误的查询。如果不选择
mov reg, 0
/ test /
setcc
,则使用
xor
/
setcc
进行调零/打破依赖关系可能是最佳选择。
setcc
的输出宽于8位,则不需要将任何内容归零。但是,如果选择的寄存器最近是长依赖性链的一部分,请当心对除P6 / SnB以外的CPU的虚假依赖性。 (并且要小心,如果您调用的函数可能会保存/恢复您正在使用的寄存器的一部分,则会导致部分注册表停止或额外的uop。)
and
在我所知道的任何CPU上都没有特殊情况,因为它与旧值无关,因此不会破坏依赖链。与
xor
相比,它没有优点,但有很多缺点。
sub same,same
在某些但不是所有CPU上,而
xor same,same
在所有CPU上都可以识别。)
mov
确实打破了对寄存器的旧值(与源值无关,是否为零,因为
mov
是这样工作的)。
xor
仅在src和dest是同一寄存器的特殊情况下才断开依赖关系链,这就是为什么
mov
不在特殊识别的依赖破坏者列表中的原因。 (此外,因为它不被视为归零习惯,还有其他好处。)
xor
-zeroing视为依赖破坏者,只是为了避免部分寄存器停顿而将其作为归零习惯,因此在某些情况下值得使用依次
mov
和
xor
置零以中断dep,然后再次置零+将内部标记位设置为高位为零,所以EAX = AX = AL。 (请参阅Agner Fog的Example 6.17。在他的microarch pdf文件中。他说这也适用于P2,P3甚至是(早期?)PM。
A comment on the linked blog post说只有PPro对此进行了监督,但是我已经在Katmai上进行了测试。 PIII和@Fanael在奔腾M上进行了测试,我们都发现它并没有打破对延迟绑定的
imul
链的依赖性。不幸的是,这证实了Agner Fog的结果。)
mov
设置为零以避免触及这些标志。但是,避免使用破坏性标志是不使用
xor
的唯一明智的原因。
关于performance - 在x86汇编中将寄存器设置为零的最佳方法是什么:xor,mov或and?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46481490/
我无法理解如何使用一些旧的 VGA 代码在这个示例中设置序列 Controller 寄存器: mov dx,SC_INDEX mov ax,0604h out dx,ax
我希望对 zmm 0-31 寄存器集的四字元素执行整数算术运算并保留这些运算产生的进位位。看来这只有在通用寄存器集中处理数据时才有可能。 因此,我想将信息从 zmm 0-31 寄存器之一复制到通用寄存
ARM 64中包含多种寄存器,下面介绍一些常见的寄存器。 1 通用寄存器 ARM 64包含31个64bit寄存器,记为X0~X30。 每一个通用寄存器,它的低32bit都可以被访问,记为W0~W
1.寄存器 组合逻辑存在一个最大的缺点就是存在竞争与冒险,系统会产生不定态;使用时序逻辑电路就会极大的改善这种情况 寄存器具有存储功能,一般是由D触发器构成,由时钟脉冲控制,每个D触发器能够
使用 $gp 是否存在危险?注册以存储值?我想我的问题是 $gp 的真正功能是什么?它是否以某种方式在幕后调用,以便如果我使用它,事情可能会变得非常非常错误? 最佳答案 那么,$gp register
我遇到了这段代码的问题,我无法弄清楚问题出在哪里。所以当我运行这段代码时:if $row["count"] > 0 else块运行和 $_SESSION["error"]设置。 当$row["coun
所以我正在做二进制炸弹的变体。这就是阶段 0x0000000000401205 : sub $0x8,%rsp 0x0000000000401209 : cmp $0x3,
我在一个名为 (EmployeeDetailKey - varchar(10)) 的字段中获得了一个值,其中包含顺序值,例如 00001, 00002, 00003.... 它位于 Employeed
我有一个要求,应该为每个调用的线程分配一个计数器变量。但我没有得到预期的结果,实际上计数器在线程中重复。我创建了一个虚拟表和一个过程来将计数器值插入到表中。无论如何,是否可以更改代码以便线程获得递增的
预期输出:需要打印第4季度的wage_amt +--------------+--------------+--------------+--------------+ | wages_amt_q1
如何匹配模式 abc_[someArbitaryStringHere]_xyz? 为了澄清,我希望正则表达式能够匹配以下性质的字符串: abc_xyz、abc_asdfsdf_xyz、abc_32rw
从下拉列表(自定义)中选择一个值而不是常规下拉列表,它有很多下拉值 我可以用代码选择第一个值 find('.selected', :text=>arg1,exact: false).click 但无法
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况有关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
我有 .csv 文件中的数据,它包含 2 列 x 轴和 y 轴。从 .csv 文件读取轴,然后使用拉伸(stretch)指数函数拟合数据,但显示错误。 这里我给出示例数据以方便理解。 我的函数是f(x
我正在尝试使用以下汇编代码将磁盘扇区加载到内存中,但正如我在终端中使用一些 int 0x10 时发现的那样,它不起作用的原因是它陷入了无限循环。我以为循环会自动为我递减 cx 寄存器。下面是代码,其中
我正在尝试编写一个脚本,该脚本将在 vim 中打开一个文件并将其中的特定行复制到 vim 的寄存器之一中。当脚本再次运行时,它会决定再次打开文件,然后将 vim 寄存器中的值粘贴回。实际上,脚本应该在
我目前正在尝试弄清楚如何将指针寄存器 SI 指向的内存中的第一个字节添加到 AX 寄存器的当前内容中。 因此,如果 SI 包含某个地址,并且该地址在内存中的值是:00 和 01,我希望将 00 添加到
我试图将两个 16 位数字与以下 NASM 代码相乘: mov ax, [input1] mov bx, [input2] mul bx 前面代码的结果存储在 DX:AX 我试图使用来自单独库“pri
我正在尝试修改 rip 寄存器(只是为了好玩)。 buffer 应该是内存地址,所以不知道为什么会得到Error: operand type mismatch for 'movq' #include
我可以告诉gcc-style inline assembly把我的__m512i变量到特定 zmm注册,如 zmm31 ? 最佳答案 就像在根本没有特定寄存器约束的目标(如 ARM)上一样,使用 lo
我是一名优秀的程序员,十分优秀!