大多数移动设备使用 64 位 ARM 处理器。越来越多的服务器(亚马逊、微软)也使用 64 位 ARM 处理器。
这些处理器具有称为 ARM NEON 的特殊指令,提供称为单指令多数据 (SIMD) 的并行性。例如,您可以使用一条指令将 16 个值与其他 16 个值进行比较。
一些最新的 ARM 处理器还支持更高级的指令,称为 SVE 或可扩展向量扩展。随着时间的推移,他们添加了越来越多的扩展:SVE 2 和 SVE 2.1。
ARM NEON 的寄存器设置为 128 位,而 SVE 寄存器是 128 位的倍数。实际上,SVE 寄存器通常是 128 位,但也有例外。 Amazon Graviton 3 基于ARM Neoverse V1 内核,支持向量长度为 256 位(32 字节)的 SVE。对于Graviton 4来说,它基于ARM Neoverse V2核心,与NEON一样支持向量长度为16字节的SVE2。
- svset_neonq :将 NEON 128 位向量(uint8x16_t、int32x4_t 等)的内容设置为 SVE 可扩展向量(svuint8_t、svint32_t 等)。
- svget_neonq :提取 SVE 可扩展向量的前 128 位并将它们作为 NEON 128 位向量返回。
这些函数是“免费的”:它们可能不会被编译为任何指令。
让我们用一个例子来说明。在最近的一篇文章中,我讨论了检查 NEON 寄存器中是否有非零字节有点复杂。一个有竞争力的解决方案如下:
int veq_non_zero_max ( uint8x16_t v ) { 返回vmaxvq_u32 ( vreinterpretq_u32_u8 ( v ) ) ! = 0 ; }
实际上,我们计算寄存器中的最大 32 位整数,将其视为四个 32 位整数。该函数编译为三个基本指令:umaxv、fmov 和比较 (cmp)。
int sve_non_zero_max ( uint8x16_t nvec ) { svuint8_t 向量; vec = svset_neonq_u8 ( vec , nvec ) ; svbool_t 掩码= svwhilelt_b8 ( 0 , 16 ) ; svbool_t cmp = svcmpne_n_u8 (掩码, vec , 0 ) ; 返回svptest_any (掩码, cmp ) ; }
该函数除了mask的初始化外,由cmpne和cset两条指令组成。在某些 ARM 内核中,这两条指令可能会融合为一条指令。尽管混合 NEON 和 SVE 的代码看起来更复杂,但它应该更高效。
如果您知道您的目标处理器支持 SVE(或 SVE 2 或 SVE 2.1),并且您已经有 ARM NEON 代码,则可以尝试向其中添加 SVE 位。
原文: https://lemire.me/blog/2025/03/29/mixing-arm-neon-with-sve-code-for-fun-and-profit/