Performance 有没有办法在32位x86中使用MOV移动2个字节而不引起模式切换或cpu暂停?

Performance 有没有办法在32位x86中使用MOV移动2个字节而不引起模式切换或cpu暂停?,performance,assembly,x86,intel,Performance,Assembly,X86,Intel,如果我想将2个无符号字节从内存移到32位寄存器中,我可以使用MOV指令而不使用模式开关吗 我注意到您可以使用MOVSE和MOVZE指令来实现这一点。例如,使用MOVSE,B7的编码0F将16位移动到32位寄存器。不过,它是一条3周期指令 或者,我想我可以将4个字节移到寄存器中,然后以某种方式只处理其中的两个字节 在32位x86上检索和比较16位数据的最快策略是什么?请注意,我主要是做32位操作,所以我不能切换到16位模式并停留在那里 仅供外行参考:这里的问题是,32位Intel x86处理器可以

如果我想将2个无符号字节从内存移到32位寄存器中,我可以使用MOV指令而不使用模式开关吗

我注意到您可以使用MOVSE和MOVZE指令来实现这一点。例如,使用MOVSE,B7的编码0F将16位移动到32位寄存器。不过,它是一条3周期指令

或者,我想我可以将4个字节移到寄存器中,然后以某种方式只处理其中的两个字节

在32位x86上检索和比较16位数据的最快策略是什么?请注意,我主要是做32位操作,所以我不能切换到16位模式并停留在那里


仅供外行参考:这里的问题是,32位Intel x86处理器可以移动8位数据和16位或32位数据,具体取决于它们所处的模式。此模式称为D位设置。您可以使用特殊前缀0x66和0x67来使用非默认模式。例如,如果您处于32位模式,并且在指令前加上0x66前缀,这将导致操作数被视为16位。唯一的问题是这样做会对性能造成很大影响。

坚持32位模式,使用16位指令

mov eax, 0         ; clear the register
mov ax, 10-binary  ; do 16 bit stuff
或者,我想我可以将4个字节移到寄存器中,然后以某种方式只处理其中的两个字节


坚持32位模式并使用16位指令

mov eax, 0         ; clear the register
mov ax, 10-binary  ; do 16 bit stuff
或者,我想我可以将4个字节移到寄存器中,然后以某种方式只处理其中的两个字节

使用movzx在现代CPU上加载窄数据。或者movsx,如果将其符号扩展而不是零扩展是有用的,但是movzx有时更快,而且从不慢

movzx只是在古老的微体系结构上进展缓慢,而不是本世纪制造的任何东西。基于最新微体系结构的奔腾品牌CPU,如奔腾G3258 Haswell,原奔腾20周年纪念版,性能与同等的i3相同,但没有AVX、BMI1/2或超线程

不要根据P5准则/数字调整现代代码。然而,Xeon Phi基于改进的P54C微体系结构,因此它也可能具有较慢的movzx。Agner Fog和KNC都没有每指令吞吐量/延迟数

使用16位操作数大小指令不会将整个管道切换到16位模式,也不会导致较大的性能命中。请参阅以了解各种x86 CPU微体系结构的速度到底是慢还是慢,其中包括像Intel P5原始奔腾一样古老的微体系结构,您似乎出于某种原因在谈论这些微体系结构

在英特尔P6系列上进行合并时,写入16位寄存器,然后读取完整的32/64位寄存器在某些CPU部分寄存器暂停时速度较慢。在另一些情况下,写入16位寄存器会合并到旧值中,因此在写入时,即使从未读取完整寄存器,也会对完整寄存器的旧值产生错误依赖。看见请注意,与像Core2/Nehalem一样的Sandybridge不同,它也将AL/AX与RAX分开重命名,但合并时不会暂停

除非您特别关注订单P5或骑士角Xeon Phi,基于相同的核心,但IDK如果movzx也很慢,请使用以下方法:

cmp的操作数大小前缀在所有现代CPU上都能有效解码。在写入完整寄存器后读取16位寄存器总是很好的,另一个操作数的16位加载也很好

操作数大小前缀的长度没有更改,因为没有imm16/imm32。e、 g.cmp字[src2],0x7F可以使用符号扩展的imm8,但是 cmp字[src2],0x80需要imm16,并且LCP将在某些英特尔CPU上暂停。如果没有操作数大小前缀,同一操作码将具有imm32,即指令的其余部分将具有不同的长度。相反,使用moveax、0x80/cmp字[src2]和ax

地址大小前缀可以在32位模式disp32和disp16中改变长度,但我们不希望使用16位寻址模式来访问16位数据。我们仍在使用[ebx+1234]或rbx,而不是[bx+1234]

在现代x86上:英特尔P6/SnB系列/Atom/Silvermont,AMD,因为至少K7(即本世纪制造的任何产品)比实际的P5奔腾更新,movzx负载非常高效

在许多CPU上,加载端口直接支持movzx,有时还支持movsx,因此它仅作为加载uop运行,而不是作为加载+ALU运行

Agner Fog指令集表中的数据:注意,它们可能不涵盖所有情况,例如mov负载号可能仅适用于32/64位负载。还请注意,Agner Fog的加载延迟数不是L1D缓存的加载使用延迟;它们只是存储/重新加载存储转发延迟的一部分,但相对数字将告诉我们movzx在mov上添加了多少个周期,通常没有额外的周期

更新:有更好的测试结果,实际上反映了负载使用延迟,并且它们是自动化的,所以在更新电子表格时打字和书写错误不是问题。但是uops.info只能追溯到以前 Conroe是英特尔的第一代Core 2,也是AMD唯一的Zen

P5奔腾顺序执行:movzx加载是一条3周期指令加上来自0F前缀的解码瓶颈,而mov加载是单周期吞吐量。不过,它们仍然有延迟

英特尔:

PPro/Pentium II/III:movzx/movsx仅在一个加载端口上运行,吞吐量与普通mov相同

Core2/Nehalem:相同,包括64位movsxd,但Core2除外,其中没有微熔丝

Sandybridge系列SnB通过Skylake和更高版本:movzx/movsx负载是单uop,只是一个负载端口,其性能与mov负载相同

奔腾4 netburst:movzx仅在加载端口上运行,性能与mov相同。movsx是load+ALU,需要额外的一个周期

原子有序:Agner的表对于需要ALU的内存源movzx/movsx不清楚,但它们确实很快。延迟编号仅适用于reg、reg

Silvermont:与Atom相同:速度快,但不清楚是否需要端口

基于Silvermont的KNL:Agner将带有内存源的movzx/movsx列为使用IP0 ALU,但延迟与mov r相同,所以没有惩罚。执行单元的压力不是问题,因为KNL的解码器几乎无法保证2个ALU的供电

AMD:

Bobcat:movzx/movsx负载为每个时钟1个,延迟5个周期。mov负载为4c延迟

Jaguar:movzx/movsx负载为每个时钟1个,延迟4个周期。mov负载为每个时钟1个,32/64位的延迟为3c,mov r8/r16的延迟为4c,但仍然只是一个AGU端口,而不是像Haswell/Skylake那样的ALU合并

K7/K8/K10:movzx/movsx负载的吞吐量为每时钟2个,延迟比mov负载高1个周期。他们使用AGU和ALU

推土机系列:与K10相同,但movsx负载有5个周期延迟。movzx负载有4个周期延迟,mov负载有3个周期延迟。因此,从理论上讲,如果来自16位mov负载的错误依赖不需要额外的ALU合并,或者为循环创建循环承载的依赖,则mov cx、word[mem]和movsx eax、cx 1循环的延迟可能会更低

Ryzen:movzx/movsx加载仅在加载端口中运行,延迟与mov加载相同

通过

Via Nano 2000/3000:movzx仅在加载端口上运行,延迟与mov加载相同。movsx是LD+ALU,具有1c额外延迟

当我说以相同的方式执行时,我的意思是不计算来自更大负载的任何部分寄存器惩罚或缓存线拆分。e、 一个movzx-eax,word[rsi]避免了对Skylake上mov-ax,word[rsi]的合并惩罚,但我仍然要说mov的性能与movzx相同。我想我的意思是,没有任何缓存线分割的mov-eax,dword[rsi]和movzx-eax,word[rsi]一样快

在写入16位寄存器之前,可以避免英特尔P6系列上随后出现的部分寄存器合并暂停,以及中断错误的依赖关系

如果您也想在P5上运行良好,那么这可能会更好一些,而在任何现代CPU上都不会差很多,除了PPro到PIII,其中xor归零不会破坏dep,即使它仍然被认为是一种归零习惯用法,使得EAX在写入AL或AX后读取EAX时不会出现部分寄存器暂停

;; Probably not a good idea, maybe not faster on anything.

;mov  eax, 0             ; some code tuned for PIII used *both* this and xor-zeroing.
xor   eax, eax           ; *not* dep-breaking on early P6 (up to PIII)
mov    ax, word [src1]
cmp    ax, word [src2]

; safe to read EAX without partial-reg stalls

操作数大小前缀对于P5来说不是理想的,所以如果您确信它没有错误,跨越缓存行边界,或者导致最近的16位存储区的存储转发失败,可以考虑使用32位加载。 实际上,我认为奔腾上的16位mov加载可能比movzx/CMP2指令序列慢。对于像处理32位数据那样高效地处理16位数据,似乎真的没有一个好的选择!当然,除了包装好的MMX材料

有关Pentium的详细信息,请参阅Agner Fog的指南,但在P1原始P5和PMMX上解码操作数大小前缀需要额外的2个周期,因此此序列实际上可能比movzx负载更糟糕。在P1而不是PMMX上,movzx使用的0F转义字节也算作前缀,需要额外的一个周期来解码

显然movzx无论如何都是不可修复的。多周期movzx将隐藏cmp ax的解码延迟[src2],因此movzx/cmp可能仍是最佳选择。或者安排指令,以便movzx可以更早地完成,cmp可以与某些东西配对。无论如何,P1/PMMX的调度规则非常复杂

我在Core2 Conroe上对该循环计时,以证明xor归零避免了16位寄存器的部分寄存器暂停,以及setcc al的低8:

perf stat-r4./testloop在静态二进制文件中输出,在以下时间后进行sys\u exit系统调用:

 ;; Core2 (Conroe) with   XOR eax, eax
       469,277,071      cycles                    #    2.396 GHz
     1,400,878,601      instructions              #    2.98  insns per cycle
       100,156,594      branches                  #  511.462 M/sec
             9,624      branch-misses             #    0.01% of all branches

       0.196930345 seconds time elapsed                                          ( +-  0.23% )
每个周期有2.98条指令:3个ALU端口,所有指令都是ALU,并且没有宏融合,因此每个都是1个uop。因此,我们以前端容量的3/4运行。循环有3*4+2条指令/UOP

Core2上的情况非常不同,xor归零已被注释,并使用mov eax、imm32替代:

从3向下0.9 IPC与前端暂停2到3个周期一致,以便在每个mov edx上插入合并uop, eax

Skylake以相同的方式运行这两个循环,因为mov eax、imm32仍在破坏依赖关系。像大多数只写目的地的指令一样,但是要小心

实际上,uops_executed.thread perf计数器确实显示了一个区别:在SnB系列上,xor归零不采用执行单元,因为它是在发布/重命名阶段处理的。mov edx、eax在重命名时也被消除,因此uop计数实际上相当低。无论哪种方式,循环计数都在小于1%的范围内

 ;;; Skylake (i7-6700k) with xor-zeroing
 Performance counter stats for './testloop' (4 runs):

         84.257964      task-clock (msec)         #    0.998 CPUs utilized            ( +-  0.21% )
                 0      context-switches          #    0.006 K/sec                    ( +- 57.74% )
                 0      cpu-migrations            #    0.000 K/sec                  
                 3      page-faults               #    0.036 K/sec                  
       328,337,097      cycles                    #    3.897 GHz                      ( +-  0.21% )
       100,034,686      branches                  # 1187.243 M/sec                    ( +-  0.00% )
     1,400,195,109      instructions              #    4.26  insn per cycle           ( +-  0.00% )  ## dec/jg fuses into 1 uop
     1,300,325,848      uops_issued_any           # 15432.676 M/sec                   ( +-  0.00% )    ###   fused-domain
       500,323,306      uops_executed_thread      # 5937.994 M/sec                    ( +-  0.00% )    ### unfused-domain
                 0      lsd_uops                  #    0.000 K/sec                  

       0.084390201 seconds time elapsed                                          ( +-  0.22% )
lsd.uops为零,因为循环缓冲区被微码更新禁用。前端瓶颈:uops融合域/时钟=4个中的3.960个。最后的.04可能部分是操作系统开销中断等等,因为这只是计算用户空间UOP。

使用movzx在现代CPU上加载窄数据。或者movsx,如果将其符号扩展而不是零扩展是有用的,但是movzx有时更快,而且从不慢

movzx只是在古老的微体系结构上进展缓慢,而不是本世纪制造的任何东西。基于最新微体系结构的奔腾品牌CPU,如奔腾G3258 Haswell,原奔腾20周年纪念版,性能与同等的i3相同,但没有AVX、BMI1/2或超线程

不要根据P5准则/数字调整现代代码。然而,Xeon Phi基于改进的P54C微体系结构,因此它也可能具有较慢的movzx。Agner Fog和KNC都没有每指令吞吐量/延迟数

使用16位操作数大小指令不会将整个管道切换到16位模式,也不会导致较大的性能命中。请参阅以了解各种x86 CPU微体系结构的速度到底是慢还是慢,其中包括像Intel P5原始奔腾一样古老的微体系结构,您似乎出于某种原因在谈论这些微体系结构

在英特尔P6系列上进行合并时,写入16位寄存器,然后读取完整的32/64位寄存器在某些CPU部分寄存器暂停时速度较慢。在另一些情况下,写入16位寄存器会合并到旧值中,因此在写入时,即使从未读取完整寄存器,也会对完整寄存器的旧值产生错误依赖。看见请注意,与像Core2/Nehalem一样的Sandybridge不同,它也将AL/AX与RAX分开重命名,但合并时不会暂停

除非您特别关注订单P5或骑士角Xeon Phi,基于相同的核心,但IDK如果movzx也很慢,请使用以下方法:

cmp的操作数大小前缀在所有现代CPU上都能有效解码。在写入完整寄存器后读取16位寄存器总是很好的,另一个操作数的16位加载也很好

操作数大小前缀的长度没有更改,因为没有imm16/imm32。e、 g.cmp字[src2],0x7F可以使用符号扩展的imm8,但是 cmp字[src2],0x80需要imm16,并且LCP将在某些英特尔CPU上暂停。如果没有操作数大小前缀,同一操作码将具有imm32,即指令的其余部分将具有不同的长度。相反,使用moveax、0x80/cmp字[src2]和ax

地址大小前缀可以在32位模式disp32和disp16中改变长度,但我们不希望使用16位寻址模式来访问16位数据。我们仍在使用[ebx+1234]或rbx,而不是[bx+1234]

在现代x86上:英特尔P6/SnB系列/Atom/Silvermont,AMD,因为至少K7(即本世纪制造的任何产品)比实际的P5奔腾更新,movzx负载非常高效

在许多CPU上,加载端口直接支持movzx,有时还支持movsx,因此它仅作为加载uop运行,而不是作为加载+ALU运行

Agner Fog指令集表中的数据:注意,它们可能不涵盖所有情况,例如mov负载号可能仅适用于32/64位负载。还请注意,Agner Fog的加载延迟数不是L1D缓存的加载使用延迟;它们只是存储/重新加载存储转发延迟的一部分,但相对数字将告诉我们movzx在mov上添加了多少个周期,通常没有额外的周期

更新:有更好的测试结果,实际上反映了负载使用延迟,并且它们是自动化的,所以在更新电子表格时打字和书写错误不是问题。但uops.info只能追溯到英特尔的Conroe第一代Core 2,而AMD则只能追溯到Zen

P5奔腾顺序执行:movzx加载是一条3周期指令加上来自0F前缀的解码瓶颈,而mov加载是单周期吞吐量。不过,它们仍然有延迟

英特尔:

PPro/Pentium II/III:movzx/movsx仅在一个加载端口上运行,吞吐量与普通mov相同

Core2/Nehalem:相同,包括64位movsxd,但Core2除外,其中没有微熔丝

Sandybridge系列SnB通过Skylake和更高版本:movzx/movsx负载是单uop,只是一个负载端口,其性能与mov负载相同

奔腾4 netburst:movzx仅在加载端口上运行,性能与mov相同。movsx是load+ALU,取1e xtra循环

原子有序:Agner的表对于需要ALU的内存源movzx/movsx不清楚,但它们确实很快。延迟编号仅适用于reg、reg

Silvermont:与Atom相同:速度快,但不清楚是否需要端口

基于Silvermont的KNL:Agner将带有内存源的movzx/movsx列为使用IP0 ALU,但延迟与mov r相同,所以没有惩罚。执行单元的压力不是问题,因为KNL的解码器几乎无法保证2个ALU的供电

AMD:

Bobcat:movzx/movsx负载为每个时钟1个,延迟5个周期。mov负载为4c延迟

Jaguar:movzx/movsx负载为每个时钟1个,延迟4个周期。mov负载为每个时钟1个,32/64位的延迟为3c,mov r8/r16的延迟为4c,但仍然只是一个AGU端口,而不是像Haswell/Skylake那样的ALU合并

K7/K8/K10:movzx/movsx负载的吞吐量为每时钟2个,延迟比mov负载高1个周期。他们使用AGU和ALU

推土机系列:与K10相同,但movsx负载有5个周期延迟。movzx负载有4个周期延迟,mov负载有3个周期延迟。因此,从理论上讲,如果来自16位mov负载的错误依赖不需要额外的ALU合并,或者为循环创建循环承载的依赖,则mov cx、word[mem]和movsx eax、cx 1循环的延迟可能会更低

Ryzen:movzx/movsx加载仅在加载端口中运行,延迟与mov加载相同

通过

Via Nano 2000/3000:movzx仅在加载端口上运行,延迟与mov加载相同。movsx是LD+ALU,具有1c额外延迟

当我说以相同的方式执行时,我的意思是不计算来自更大负载的任何部分寄存器惩罚或缓存线拆分。e、 一个movzx-eax,word[rsi]避免了对Skylake上mov-ax,word[rsi]的合并惩罚,但我仍然要说mov的性能与movzx相同。我想我的意思是,没有任何缓存线分割的mov-eax,dword[rsi]和movzx-eax,word[rsi]一样快

在写入16位寄存器之前,可以避免英特尔P6系列上随后出现的部分寄存器合并暂停,以及中断错误的依赖关系

如果您也想在P5上运行良好,那么这可能会更好一些,而在任何现代CPU上都不会差很多,除了PPro到PIII,其中xor归零不会破坏dep,即使它仍然被认为是一种归零习惯用法,使得EAX在写入AL或AX后读取EAX时不会出现部分寄存器暂停

;; Probably not a good idea, maybe not faster on anything.

;mov  eax, 0             ; some code tuned for PIII used *both* this and xor-zeroing.
xor   eax, eax           ; *not* dep-breaking on early P6 (up to PIII)
mov    ax, word [src1]
cmp    ax, word [src2]

; safe to read EAX without partial-reg stalls

操作数大小前缀对于P5来说不是理想的,所以如果您确信它没有错误,跨越缓存行边界,或者导致最近的16位存储区的存储转发失败,可以考虑使用32位加载。 实际上,我认为奔腾上的16位mov加载可能比movzx/CMP2指令序列慢。对于像处理32位数据那样高效地处理16位数据,似乎真的没有一个好的选择!当然,除了包装好的MMX材料

有关Pentium的详细信息,请参阅Agner Fog的指南,但在P1原始P5和PMMX上解码操作数大小前缀需要额外的2个周期,因此此序列实际上可能比movzx负载更糟糕。在P1而不是PMMX上,movzx使用的0F转义字节也算作前缀,需要额外的一个周期来解码

显然movzx无论如何都是不可修复的。多周期movzx将隐藏cmp ax的解码延迟[src2],因此movzx/cmp可能仍是最佳选择。或者安排指令,以便movzx可以更早地完成,cmp可以与某些东西配对。无论如何,P1/PMMX的调度规则非常复杂

我在Core2 Conroe上对该循环计时,以证明xor归零避免了16位寄存器的部分寄存器暂停,以及setcc al的低8:

perf stat-r4./testloop在静态二进制文件中输出,在以下时间后进行sys\u exit系统调用:

 ;; Core2 (Conroe) with   XOR eax, eax
       469,277,071      cycles                    #    2.396 GHz
     1,400,878,601      instructions              #    2.98  insns per cycle
       100,156,594      branches                  #  511.462 M/sec
             9,624      branch-misses             #    0.01% of all branches

       0.196930345 seconds time elapsed                                          ( +-  0.23% )
每个周期有2.98条指令:3个ALU端口,所有指令都是ALU,并且没有宏融合,因此每个都是1个uop。因此,我们以前端容量的3/4运行。循环有3*4+2条指令/UOP

Core2上的情况非常不同,xor归零已被注释,并使用mov eax、imm32替代:

从3向下0.9 IPC与前端暂停2到3个周期一致,以便在每个mov edx、eax上插入合并uop

Skylake以相同的方式运行这两个循环,因为mov eax、imm32仍在破坏依赖关系。像大多数只写目的地的指令一样,但是要小心

实际上,uops_executed.thread perf计数器确实显示了一个区别:在SnB系列上,xor归零不采用执行单元,因为它是在发布/重命名阶段处理的。mov edx、eax在重命名时也被消除,因此uop计数实际上相当低。无论哪种方式,循环计数都在小于1%的范围内

 ;;; Skylake (i7-6700k) with xor-zeroing
 Performance counter stats for './testloop' (4 runs):

         84.257964      task-clock (msec)         #    0.998 CPUs utilized            ( +-  0.21% )
                 0      context-switches          #    0.006 K/sec                    ( +- 57.74% )
                 0      cpu-migrations            #    0.000 K/sec                  
                 3      page-faults               #    0.036 K/sec                  
       328,337,097      cycles                    #    3.897 GHz                      ( +-  0.21% )
       100,034,686      branches                  # 1187.243 M/sec                    ( +-  0.00% )
     1,400,195,109      instructions              #    4.26  insn per cycle           ( +-  0.00% )  ## dec/jg fuses into 1 uop
     1,300,325,848      uops_issued_any           # 15432.676 M/sec                   ( +-  0.00% )    ###   fused-domain
       500,323,306      uops_executed_thread      # 5937.994 M/sec                    ( +-  0.00% )    ### unfused-domain
                 0      lsd_uops                  #    0.000 K/sec                  

       0.084390201 seconds time elapsed                                          ( +-  0.22% )
lsd.uops为零,因为循环缓冲区被微码更新禁用。前端瓶颈:uops融合域/时钟=4个中的3.960个。最后的0.04可能部分是操作系统开销中断等等
,因为这只是计算用户空间UOP。

所以这是在P1或PMMX上,对吗?对于这样的问题,特定的微体系结构是非常重要的。如果您将问题限制在尝试优化3个周期以下,您不可能比MOVSE/ZE做得更好。你最好描述一下你想用16位数量做什么;与指令本身相比,更可能优化包含MOVZE/SE的代码块,特别是如果该代码块由于其他原因必须接触16位量旁边的数据。您的周期计时不准确。对于一个原子来说,movsx reg,r/m16需要1/1个周期。LCP停机位严重依赖于体系结构。英特尔的建议是加载32位,只使用16位寄存器。如果在没有立即操作数的指令上使用操作数大小前缀,则其长度不会改变。在某些情况下,16位版本的速度仍然稍慢,但它不会暂停解码器,除非操作码和modr/m字节之间存在16字节边界。你在拿什么做比较?如果它是常数,考虑把它放在登记册里。而且,在PMMX之后,movzx在任何事情上都很快,所以你可以直接使用它。说真的,只要根据需要使用movzx或movsx即可。他们对过去十年制造的任何东西都很快。所以这就是P1或PMMX,对吗?对于这样的问题,特定的微体系结构是非常重要的。如果您将问题限制在尝试优化3个周期以下,您不可能比MOVSE/ZE做得更好。你最好描述一下你想用16位数量做什么;与指令本身相比,更可能优化包含MOVZE/SE的代码块,特别是如果该代码块由于其他原因必须接触16位量旁边的数据。您的周期计时不准确。对于一个原子来说,movsx reg,r/m16需要1/1个周期。LCP停机位严重依赖于体系结构。英特尔的建议是加载32位,只使用16位寄存器。如果在没有立即操作数的指令上使用操作数大小前缀,则其长度不会改变。在某些情况下,16位版本的速度仍然稍慢,但它不会暂停解码器,除非操作码和modr/m字节之间存在16字节边界。你在拿什么做比较?如果它是常数,考虑把它放在登记册里。而且,在PMMX之后,movzx在任何事情上都很快,所以你可以直接使用它。说真的,只要根据需要使用movzx或movsx即可。它们对过去十年中制造的任何东西都很快。这样做会导致前缀指令模式异常。如果您实际汇编此代码,您将看到一个0x66前缀添加到CMP操作码中。这会导致处理器暂停和巨大的性能损失。在使用imm16立即数操作数而不是imm8的指令时,操作数大小前缀只会对英特尔CPU造成性能损失,因为这样会更改指令其余部分的长度,如add ax,0x1234。cmp-ax,bx是快的,movzx-eax,word[mem]也是快的。在Intel SnB系列mov ax上,0x1234没有LCP暂停。解码器专门处理16位mov。并不总是工作。如果您的地址不是4字节对齐的,您将支付罚款,并且由于被忽略的字节位于页面末尾,而下一页未映射,此代码可能会崩溃。mov eax,0是错误的选择。xor eax,eax是有意义的。但真正正确的答案是,movzx除了在P5上运行外并不慢。这样做会导致前缀指令模式异常。如果您实际汇编此代码,您将看到一个0x66前缀添加到CMP操作码中。这会导致处理器暂停和巨大的性能损失。在使用imm16立即数操作数而不是imm8的指令时,操作数大小前缀只会对英特尔CPU造成性能损失,因为这样会更改指令其余部分的长度,如add ax,0x1234。cmp-ax,bx是快的,movzx-eax,word[mem]也是快的。在Intel SnB系列mov ax上,0x1234没有LCP暂停。解码器专门处理16位mov。并不总是工作。如果您的地址不是4字节对齐的,您将支付罚款,并且由于被忽略的字节位于页面末尾,而下一页未映射,此代码可能会崩溃。mov eax,0是错误的选择。xor eax,eax是有意义的。但真正正确的答案是,movzx除了在P5上外并不慢。