gpt4 book ai didi

assembly - 测试 xmm/ymm 寄存器是否为零的更快方法?

转载 作者:行者123 更新时间:2023-12-02 12:21:50 26 4
gpt4 key购买 nike

幸运的是,PTEST 不会影响进位标志,而只会设置(相当尴尬的)ZF。还会影响 CF 和 ZF。

我提出了以下序列来测试大量值,但我对糟糕的运行时间感到不满意。

              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

我想要在eax中拥有所有非零寄存器的位图(显然我可以在多个寄存器中组合多个位图)。

因此每个测试都有 3+1 = 4 个周期的延迟。
其中一些可以通过在 eaxecx 等之间交替来并行运行。
但还是很慢。
有没有更快的方法来做到这一点?

我需要连续测试 8 个 xmm/ymm 寄存器。一字节位图中每个寄存器 1 位。

最佳答案

实际上,您现有的方法并不是“相当慢”,而是合理的。

当然,每个单独的测试都有 4 个周期的延迟1,但如果您希望将结果保存在通用寄存器中,您通常需要支付 3 个周期的费用无论如何,该移动的延迟(例如,movmskb 的延迟也为 3)。无论如何,您想要测试 8 个寄存器,并且不能简单地添加延迟,因为每个寄存器基本上都是独立的,因此 uop 计数和端口使用最终可能比测试单个寄存器的延迟更重要。的延迟将与其他工作重叠。

在英特尔硬件上可能更快一点的方法是使用连续的 PCMPEQ 指令来测试多个向量,然后将结果折叠在一起(例如,如果您使用 PCMPEQQ,您实际上可以得到4 个四字结果,需要将它们折叠成 1)。您可以在 PCMPEQ 之前或之后折叠,但这有助于更多地了解您希望如何/在何处获得更好的结果。这是 8 个寄存器的未经测试的草图,xmm1-8 中的 xmm0 假定为零,xmm14pblendvb 掩码选择最后一条指令中使用的替代字节。

# 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 结果之前)。然后,您使用 movmskb 将这些 16 字节掩码作为 16 位移动到 eax 中,最后将每个寄存器的高和低 QWORD 位结合起来 eax

在我看来,8 个寄存器总共有 16 个微指令,所以每个寄存器大约有 2 个微指令。总延迟是合理的,因为它主要是“减少”类型的并行树。一个限制因素是 6 个 vpblendw 操作,它们都只发送到现代 Intel 上的端口 5。最好用 VPBLENDD 替换其中的 4 个,这是一个适用于 p015 中任何一个的“祝福”混合物。这应该很简单。

所有操作都简单快速。最后的和al, ah是部分寄存器写入,但是如果你在eax之后mov它也许没有惩罚。如果这是一个问题,您也可以通过几种不同的方式来完成最后一行...

这种方法也可以自然地扩展到 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

这通过避免 2 个周期 vpblendvbor al,ah 的部分写入惩罚节省了 2 个周期,它还修复了对慢速 vpmovmskb 的依赖 如果不需要立即使用该指令的结果。

<小时/>

1实际上,PTEST 似乎仅在 Skylake 上有 3 个周期的延迟,在此之前似乎是 2 个周期。我也不确定您为 rcl eax, 1 列出的 1 个周期延迟:根据 Agner 的说法,在现代英特尔上,它似乎是 3 uops 和 2 个周期延迟/recip 吞吐量。

关于assembly - 测试 xmm/ymm 寄存器是否为零的更快方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42317528/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com