如果我想把2个无符号字节从内存移到32位寄存器中,我可以用mov
指令而不是模式切换吗?
我注意到,您可以使用movse
和movze
指令来完成此操作。例如,使用movse
,编码0f b7
将16位移动到32位寄存器。不过,这是一个3个循环的指令。
或者,我想我可以将4个字节移动到寄存器中,然后以某种方式CMP其中的两个字节。
在32位x86上检索和比较16位数据的最快策略是什么?请注意,我主要是在执行32位操作,所以我不能切换到16位模式并停留在那里。
这里的问题是,32位Intel x86处理器可以MOV
8位数据和16位或32位数据,这取决于它们处于何种模式。这种模式被称为“D位”设置。可以使用特殊前缀0x66和0x67来使用非默认模式。例如,如果处于32位模式,并且在指令前缀0x66,这将导致操作数被视为16位。唯一的问题是,这样做会导致性能大受影响。
使用movzx
在现代CPU上加载狭义数据。(或者movsx
,如果使用符号扩展而不是零扩展是有用的,但是movzx
有时更快,而不是更慢。)
MOVZX
只在古老的P5(原始奔腾)微架构上慢,而不是本世纪制造的任何东西。基于最新微架构的奔腾品牌CPU,如奔腾G3258(Haswell,原始奔腾20周年纪念版)是完全不同的野兽,性能类似于同等的i3,但没有AVX、BMI1/2或超线程。
不要根据P5的指导方针/数字来调整现代代码。然而,Knight's Corner(Xeon Phi)基于修改后的P54C微架构,因此它可能也有慢速movzx
。Agner Fog和Instlatx64都没有针对KNC的每指令吞吐量/延迟数。
使用16位操作数大小的指令不会将整个流水线切换到16位模式,也不会导致大的perf命中。请参阅Agner Fog的microarch pdf,以了解各种x86 CPU微架构(包括像Intel P5(原始奔腾)这样古老的架构,您似乎出于某种原因正在谈论这些架构)上到底什么是慢的,什么不是慢的。
在某些CPU上,写入16位寄存器,然后读取完整的32/64位寄存器是很慢的(在Intel P6系列上合并时,部分寄存器会停顿)。在其他情况下,写入16位寄存器会合并到旧值中,因此在写入时,即使从未读取完整寄存器,也会对完整寄存器的旧值产生错误依赖。看看哪个CPU做什么。(注意,Haswell/Skylake只单独重命名AH,不像Sandybridge(像Core2/Nehalem一样)也单独重命名AL/AX和RAX,但不会延迟合并。)
除非您特别关心顺序P5(或者可能是骑士角Xeon Phi,基于相同的核心,但是IDK如果movzx
在那里也很慢),否则请使用以下命令:
movzx eax, word [src1] ; as efficient as a 32-bit MOV load on most CPUs
cmp ax, word [src2]
来自Agner Fog指令集表的数据:注意,它们可能不包括每个角落的情况,例如mov
-加载数可能只适用于32/64位加载。还要注意,Agner Fog的加载延迟数不是L1D缓存中的加载使用延迟;它们只作为存储/重新加载(存储-转发)延迟的一部分才有意义,但是相对数字将告诉我们movzx
在mov
的基础上增加了多少周期(通常没有额外的周期)。
(update:https://uops.info/有更好的测试结果,实际上反映了负载使用延迟,而且这些结果是自动化的,所以更新电子表格时的错别字和文书错误不是问题。但是uops.info只追溯到Intel的Conroe(第一代核心2),而AMD只追溯到Zen。)
>
P5 Pentium(按顺序执行):MOVZX
-LOAD是一个3周期指令(加上来自0F
前缀的解码瓶颈),而MOV
-LOAD是单周期吞吐量。(不过,它们仍然有潜伏期)。
PPro/Pentium II/III:MOVZX
/MOVSX
只在一个装载端口上运行,吞吐量与普通MOV
相同。
Core2/Nehalem:相同,包括64位movsxd
,但在Core2上,movsxd r64、M32
加载需要一个加载+ALU uop,不进行微熔断。
SandyBridge系列(SnB通过Skylake和更高版本):MOVZX
/MOVSX
加载是单UOP(只是一个加载端口),执行与MOV
加载相同的操作。
Atom(按顺序):Agner的表不清楚内存-sourcemovzx
/movsx
需要一个ALU,但它们肯定很快。延迟数仅用于reg,reg。
西尔弗蒙特:和原子一样:速度快,但不清楚需要一个端口。
KNL(基于Silvermont):Agner将具有内存源的MOVZX
/MOVSX
列为使用IP0(ALU),但是延迟与MOV r,M
相同,因此没有任何惩罚。(执行单元压力不是一个问题,因为KNL的解码器只能勉强维持它的2个ALUs。)
AMD:
山猫:movzx
/movsx
负载为每时钟1,5个周期延迟。mov
-load是4C延迟。
Jaguar:movzx
/movsx
加载为每时钟1个,4个周期延迟。MOV
负载为每时钟1,32/64位时为3C延迟,MOV R8/R16,M
时为4C(但仍然只有一个AGU端口,而不是像Haswell/Skylake那样的ALU合并)。
XOR
-在写入16位寄存器之前将完整寄存器归零,可以避免以后在Intel P6-family上部分寄存器合并停顿,并破坏错误的依赖关系。
如果您也想在P5上运行良好,这可能会更好一些,而在任何现代CPU上都不会更差,除了PPro到PIII,在PIII中XOR
-zeroing不破坏DEP,尽管它仍然被认为是一种归零习惯用法,使EAX等同于AX(在写入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来说并不理想,因此如果您确信32位加载不会出错、跨越缓存行边界或导致最近16位存储的存储转发失败,则可以考虑使用32位加载。
实际上,我认为在Pentium上16位MOV
加载可能比MOVZX
/CMP
2指令序列慢。对于处理16位数据来说,似乎真的没有一个像32位数据那样有效的好选择!(当然,除了包装好的MMX材料)。
有关奔腾的详细信息,请参见Agner Fog的指南,但是操作数大小的前缀需要额外的2个周期才能在P1(原始P5)和PMMX上解码,因此这个序列实际上可能比MOVZX
加载更糟糕。在P1上(但不是PMMX),0F
转义字节(MOVZX
)也作为前缀计算,需要额外的周期进行解码。
显然movzx
是不可配对的。多循环MOVZX
将隐藏CMP ax,[src2]
的解码延迟,因此MOVZX
/CMP
可能仍然是最佳选择。或者调度指令,这样movzx
就可以更早地完成,cmp
就可以与某些东西配对。总之,P1/PMMX的调度规则相当复杂。
mov ebp, 100000000
ALIGN 32
.loop:
%rep 4
xor eax, eax
; mov eax, 1234 ; just break dep on the old value, not a zeroing idiom
mov ax, cx ; write AX
mov edx, eax ; read EAX
%endrep
dec ebp ; Core2 can't fuse dec / jcc even in 32-bit mode
jg .loop ; but SnB does
perf stat-r4./testloop
在静态二进制文件中输出,该文件在以下情况下进行sys_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
指令/UOPS。
在Core2上,XOR
-注释为零,并使用MOV eax,IMM32
:
;; Core2 (Conroe) with MOV eax, 1234
1,553,478,677 cycles # 2.392 GHz
1,401,444,906 instructions # 0.90 insns per cycle
100,263,580 branches # 154.364 M/sec
15,769 branch-misses # 0.02% of all branches
0.653634874 seconds time elapsed ( +- 0.19% )
;;; 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% )
我在Windows上使用Oracle JRE,在Linux上使用OpenJDK6。 我想知道Windows的调度程序是否随机抢占线程而Linux的没有?
问题内容: 我用Java创建了一个简单的程序: 如果我在Linux机器上运行此程序,它会显示100%的CPU使用率,但不会导致操作系统显示缓慢。但是,如果我在Windows上运行完全相同的代码,则仅显示约20%的CPU使用率。 我在Windows上使用Oracle JRE,在Linux上使用OpenJDK 6。 我想知道Windows的调度程序是否会随机抢占线程,而Linux的不是吗? 问题答案:
在使用java程序(带有Eclipse IDE)将leap motion listener从32位windows应用到64位windows后,该程序似乎运行正常。 问题是,现在与控制器的连接已初始化,已连接,然后在我不做任何操作的情况下立即退出。 我试着把手放在控制器上,结果出错了 Java运行时环境检测到一个致命错误: pc=0x000007fee8b4a975,pid=10516时的异常访问(
问题内容: 当我使用CDLL在32位python中调用32位dll时,它运行良好。但是不幸的是,在我的64位win7操作系统中,它只能安装64位python,调用时会变成:这不是有效的win32应用程序! 我可以在64位python中使用32位dll或exe吗?还是我必须安装32位python? 问题答案: 64位EXE无法加载32位DLL。(反之亦然:32位EXE无法加载64位DLL。)毕竟,它
我有一个立方体,我只在x轴上的3个点(浮动位置)之间移动它。所以立方体将从0.00开始,我按下右键,它在x轴上向右移动到2.0f。然后我按下左键,它会回到0.0f。然后我再次按下左键,它会移动到-2.0f。按下右键应该会将其返回到0.0f,但会超出0。误差的大小取决于我移动的速度。 如果我从左键开始,结果也是一样的。 帧时间是
但我会得到另一个例外 java.lang.UnsatisfiedLinkError:dlopen失败:“/data/app/com.example.user.project/lib/x86/libtracker.so”是64位而不是32位 我可以看到我的库已经成功构建,这是它在构建时显示的跟踪消息 这是我的CMakeLists 这是我的母语-lib.cpp 那么,Android studio无法从