使用 x64 SSE / AVX 寄存器进行字符串反转
Posted
技术标签:
【中文标题】使用 x64 SSE / AVX 寄存器进行字符串反转【英文标题】:String reverse with x64 SSE / AVX registers 【发布时间】:2019-08-08 00:52:55 【问题描述】:我正在尝试编写 SIMD 汇编指令来反转长度在 16 到 32 字节之间的字符串。下面反转一个正好 32 字节长的字符串,但不处理任何更短的字符串。是否有一种 AVX/SSE 方法可以以更简洁的方式更好地做到这一点?我实际上需要 xmm 或 ymm 的 bswap。
Rdx 指向内存中的某个位置,其中包含我想要反转的空终止字符串。反转后,我想用相同地址的反转版本覆盖字符串。
movdqu xmm0, [rdx]
pshufd xmm0,xmm0, 0x1B
pshuflw xmm0,xmm0, 0xB1
pshufhw xmm0, xmm0, 0xB1
movdqa xmm1,xmm0
psrlw xmm1, 8
psllw xmm0, 8
por xmm0,xmm1
movdqu xmm2, [rdx +0x10]
pshufd xmm2,xmm2, 0x1b
pshuflw xmm2,xmm2, 0xB1
pshufhw xmm2, xmm2, 0xB1
movdqa xmm3,xmm2
psrlw xmm3, 8
psllw xmm2, 8
por xmm2,xmm3
movdqu [rdx], xmm2
movdqu [rdx+0x10], xmm0
【问题讨论】:
为pshufb
加载一个控制向量以通过一次随机播放来反转整个向量。您在 Intel 上只能获得 1 次随机播放/时钟吞吐量,但 vpshufb ymm
仍然是单个 uop。所以加载 32 个字节,用vpshufb
对 128 位通道进行字节反转,然后用vextracti128
分别存储两半。 (或者做窄负载和宽存储。)
我之前尝试过,但没有成功。有没有比 x86 手册更好的文档可以指点我?我发现的一切都是 C++ 固有的做法,手册本身并不是最清楚的。我对使用这个扩展很陌生。
另外,我非常感谢您的回复。
您可以编写内在函数并查看编译器输出。或者Intel的x86手册很清楚(felixcloutier.com/x86/pshufb)。不幸的是,这些天来,AVX512 版本的所有内容都有些臃肿,因此请考虑查看 vol.2 的较旧 PDF。不过,Agner Fog 的优化组装指南很不错:agner.org/optimize
谢谢你。我非常感谢您提供的信息。
【参考方案1】:
为pshufb
加载一个控制向量,以通过一次随机播放来反转整个向量。 在 Intel 上您只能获得 1 次随机播放/时钟吞吐量,但vpshufb ymm
仍然是单个 uop。 (https://agner.org/optimize/)
所以加载 32 个字节,用 vpshufb
对 128 位通道进行字节反转,然后用 vextracti128
分别存储两半。或者进行窄负载和宽存储,这可能更适合避免存储转发停顿。
或者使用额外的 shuffle 在 32 字节加载/32 字节存储之间交换 YMM 的一半。 (例如vpermq
或vperm2i128
换道,在vpshufb
之前或之后)。
default rel
byte_rev_32:
...
vmovdqu xmm0, [rdx + 16] ; 1 uop
vinserti128 ymm0, ymm0, [rdx], 1 ; 2 uops: load + any vector-ALU port
; lane-swapping load that doesn't cost any port-5-only shuffle uops
; then in-lane byte reverse
vpshufb ymm0, ymm0, [byte_reverse] ; 1 uop (with micro-fused load)
vmovdqu [rdx], ymm0
...
section .rodata:
align 32
byte_reverse: db 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
db 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
或者,如果您在循环中执行此操作,您应该提升 shuffle-control 向量的负载。例如VBROADCASTI128 ymm1, [byte_reverse]
所以你只需要一个 16 字节的常量在内存中。在 Intel CPU 上,具有 dword 和更大粒度的广播负载与常规负载一样便宜。
AVX512VBMI (CannonLake / Ice Lake) 具有交叉车道vpermb
,可以在 1 条指令中对 32 或 64 字节向量进行字节反转。
或者对于pshufb
只使用 SSSE3,而不是 AVX2,只需加载两个 16 字节的一半,分别交换它们,然后分别存储。
【讨论】:
您的意思是 vinserti128 有 3 个参数吗?文档说我们需要 4 但不知道这是否是您在这种特殊情况下的意思的简写。 @kr1tzb1tz:哦,对,这不是故意的。但是 NASM 语法确实允许在与目标相同时省略第一个源,所以我认为如果你组装了我的原始版本,无论如何你都会得到这个。以上是关于使用 x64 SSE / AVX 寄存器进行字符串反转的主要内容,如果未能解决你的问题,请参考以下文章