程序员有时需要修剪或删除字符,例如字符串中的空格。这可能是一项令人惊讶的昂贵任务。在 C 中,以下函数是有效的:
size_t trimspaces ( const char * s , size_t len , char * out ) { char * init_out {输出} ; 对于( size_t i = 0 ; i < len ; i + + ) { *输出= s [ i ] ; 输出+ = ( s [ i ] ! = ' ' ) ; } 返回- init_out ; }
基本上,我们写入输入中的所有字符,但如果输入不是空格,我们只会增加指针。
Amazon 依靠其 Graviton 3 处理器提供新的基于 ARM 的系统。这些处理器支持称为“SVE”的高级指令。一个非常好的指令集,用于“压缩”值(有效地删除不需要的值)。 当从数组中过滤出整数值时,我很好地利用了它。
不幸的是,SVE 紧凑指令不能直接应用于字符串中修剪空间的问题,因为它们只对较大的字(例如,32 位字)进行操作。但是,幸运的是,可以使用svld1sb_u32等内部函数将字节直接加载到 32 位值中,以便每个字节值在内存中占用 32 位。正如您所期望的,您也可以反过来,获取一个 32 位值的数组,并自动将其转换为字节数组(例如,使用svst1b_u32 )。
因此,我可以获取我的字节数组(一个字符串),将其加载到临时 32 位向量中,修剪这些向量,然后将结果作为字节数组存储回字符串。下面的C代码是这个想法的合理实现:
size_t sve_trimspaces ( const char * s , size_t len , char * out ) { uint8_t * out8 = reinterpret_cast < uint8_t * > ( out ) ; 尺寸_t我= 0 ; 对于( ; i + svcntw ( ) < = len ; i + = svcntw ( ) ) { svuint32_t 输入= svld1sb_u32 ( svptrue_b32 ( ) , ( const int8_t * ) s + i ) ; svbool_t 匹配= svcmpne_n_u32 ( svptrue_b32 ( ) ,输入, 32 ) ; svuint32_t compressed = svcompact_u32 (匹配,输入) ; svst1b_u32 ( svptrue_b32 ( ) , out8 ,压缩) ; out8 + = svcntp_b32 ( svptrue_b32 ( ) ,匹配) ; } 如果(我<长度) { svbool_t read_mask = svwhilelt_b32 ( i , len ) ; svuint32_t input = svld1sb_u32 ( read_mask , ( const int8_t * ) s + i ) ; svbool_t matches = svcmpeq_n_u32 ( read_mask , input , 32 ) ; svuint32_t compressed = svcompact_u32 (匹配,输入) ; svst1b_u32 (读掩码, out8 ,压缩) ; out8 + = svcntp_b32 ( read_mask , matches ) ; } 返回out8 - reinterpret_cast < uint8_t * > ( out ) ; }
它更快吗?在 Graviton 3 上使用 GCC 12,我发现 SVE 方法的速度提高了 3.6 倍,并且使用的指令减少了 6 倍。 SVE 代码并没有快六倍,因为它每个周期退出的指令更少。我的代码可用。
约定代码 | 1.8 周期/字节 | 7条指令/字节 |
SVE代码 | 0.5 个周期/字节 | 1.1 指令/字节 |