Latency / rThoughput
setup:
xor eax,eax ; na
vpxor xmm0,xmm0 ; na ;mask to use for the nand operation of ptest
work:
vptest xmm4,xmm0 ; 3 1 ;is xmm4 alive?
adc eax,eax ; 1 1 ;move first bit into eax
vptest xmm5,xmm0 ; 3 1 ;is N alive?
adc eax,eax ; 1 1 ;move consecutive bits into eax
因此,每个测试都有3+1=4个周期的延迟。
其中一些可以通过在EAX
、ECX
等之间交替并行运行。
但它仍然相当慢。
有没有更快的方法来实现这一点?
我需要在一行中测试8个XMM/YMM寄存器。一字节位图中每个寄存器1位。
实际上,你现有的方法是合理的,而不是“相当慢”。
当然,每个单独的测试都有4个循环1的延迟,但是如果您希望将结果保存在通用寄存器中,那么无论如何,您通常要为该移动支付3个循环的延迟(例如,movmskb
也有3个循环的延迟)。在任何情况下,您都希望测试8个寄存器,并且不能简单地添加延迟,因为每个寄存器基本上都是独立的,因此uop计数和端口使用最终可能比测试单个寄存器的延迟更重要,因为大多数延迟将与其他工作重叠。
在Intel硬件上,一种可能更快的方法是使用连续的pcmpeq
指令来测试几个向量,然后将结果折叠在一起(例如,如果使用PCMPEQQ,您实际上有4个四字结果,需要将它们折叠成1)。您可以在pcmpeq
之前或之后折叠,但更多地了解您希望如何/在哪里获得更好的结果将会有所帮助。以下是8个寄存器的未经测试的草图,xmm1-8
xmm0
假定为零,xmm14
为pblendvb
掩码,以选择上一条指令中使用的备用字节。
# test the 2 qwords in each vector against zero
vpcmpeqq xmm11, xmm1, xmm0
vpcmpeqq xmm12, xmm3, xmm0
vpcmpeqq xmm13, xmm5, xmm0
vpcmpeqq xmm14, xmm7, xmm0
# blend the results down into xmm10 word origin
vpblendw xmm10, xmm11, xmm12, 0xAA # 3131 3131
vpblendw xmm13, xmm13, xmm14, 0xAA # 7575 7575
vpblendw xmm10, xmm10, xmm13, 0xCC # 7531 7531
# test the 2 qwords in each vector against zero
vpcmpeqq xmm11, xmm2, xmm0
vpcmpeqq xmm12, xmm4, xmm0
vpcmpeqq xmm13, xmm6, xmm0
vpcmpeqq xmm14, xmm8, xmm0
# blend the results down into xmm11 word origin
vpblendw xmm11, xmm11, xmm12, 0xAA # 4242 4242
vpblendw xmm13, xmm13, xmm14, 0xAA # 8686 8686
vpblendw xmm11, xmm11, xmm13, 0xCC # 8642 8642
# blend xmm10 and xmm11 together int xmm100, byte-wise
# origin bytes
# xmm10 77553311 77553311
# xmm11 88664422 88664422
# res 87654321 87654321
vpblendvb xmm10, xmm10, xmm11, xmm15
# move the mask bits into eax
vpmovmskb eax, xmm10
and al, ah
根据直觉,您将每个XMM
中的每个Qword
测试为零,给出8个寄存器的16个结果,然后将这些结果混合到Xmm10
中,最终每个字节有一个结果(所有高qword结果在所有低qword结果之前)。然后将这些16位字节掩码作为16位移动到EAX
中,并使用MOVMSKB
,最后将EAX
中每个寄存器的高位和低位Qword
组合在一起。
在我看来,对于8个寄存器,总共有16个UOP,所以每个寄存器大约有2个UOP。总延迟是合理的,因为它在很大程度上是一个“reduce”类型的并行树。一个限制因素是6vpblendw
操作,这些操作在现代Intel上只转到5端口。最好用vpblendd
替换其中的4个,这是用于p015
中任何一个的“受祝福”混合。那应该很直截了当。
所有的操作都是简单和快速的。最后的和al,ah
是一个部分寄存器写入,但是如果MOV
它之后进入EAX
可能没有惩罚。如果这是个问题,你也可以用几种不同的方法来完成最后一行...
这种方法还可以自然地扩展到YMM
寄存器,在末尾的EAX
中的折叠略有不同。
编辑
稍快的结尾使用打包移位来避免两个昂贵的指令:
;combine bytes of xmm10 and xmm11 together into xmm10, byte wise
; xmm10 77553311 77553311
; xmm11 88664422 88664422 before shift
; xmm10 07050301 07050301
; xmm11 80604020 80604020 after shift
;result 87654321 87654321 combined
vpsrlw xmm10,xmm10,8
vpsllw xmm11,xmm11,8
vpor xmm10,xmm10,xmm11
;combine the low and high dqword to make sure both are zero.
vpsrldq xmm12,xmm10,64
vpand xmm10,xmm12
vpmovmskb eax,xmm10
Agner Fog的指令表没有列出这种特殊情况,他的microarch指南也没有提到UOP的数量。 这可能意味着是实现的更好方法。 对于AVX512,还通过在可能的情况下仅使用vex编码的零化习惯用法而不是EVEX来保存字节。(即对于zmm0-15.仍然需要EVEX)。GCC/CLANG目前使用所需寄存器宽度的XOR零化习惯用法,而不是总是使用AVX-128。 (256B向量指令在第一个256B指
我知道JE和JZ指令是相同的,而且使用OR给出了一个字节的大小改进。然而,我也关心代码速度。似乎逻辑运算符会比SUB或CMP更快,但我只是想确定一下。这可能是大小和速度之间的权衡,或者是双赢(当然代码会更加不透明)。
在x86-64中,如果某些通用寄存器比其他寄存器更受欢迎,某些指令会执行得更快吗? 例如,会比执行得更快吗?我可以想象后者需要一个REX前缀,这会使指令获取速度变慢? 使用代替怎么样?或呢?其他操作?更小的寄存器,如vs?vs? AMD vs Intel?更新的处理器?较旧的处理器?指令的组合? 澄清:某些通用登记册是否应该优先于其他登记册,它们是哪些?
考虑: 为什么,我怎么才能让它起作用? 我的CPU是i5-10210u(支持AVX-256)。在X64版本/调试中运行。
本文向大家介绍Intel x86 Assembly& Microarchitecture 测试寄存器为0,包括了Intel x86 Assembly& Microarchitecture 测试寄存器为0的使用技巧和注意事项,需要的朋友参考一下 示例 背景 要弄清寄存器是否为零,天真的方法是这样做: 但是,如果您查看此操作码,则会得到以下信息: 采用 test 检查您得到的操作码: 优点 只有两个字
在wikipedia x86调用约定中,它说对于Microsoft x64调用约定: 寄存器RBX、RBP、RDI、RSI、RSP、R12、R13、R14和R15被视为非易失性(被叫方保存)。 但对于System V AMD64 ABI: 如果被调用方希望使用寄存器RBX、RBP和R12-R15,则必须在将控制权返回给调用方之前恢复它们的原始值。 我的问题是,在不同的平台上调用约定是不是不同的?(