我想将一些
u8
扩展为
u64
,但我想要的不是直接支持的零扩展或符号扩展,而是 "复制扩展"。有什么好办法可以做到这一点(在使用 avx512 的 intel cpus 上)?示例代码使用的是 rust 语言,但宿主语言并不是最有趣的部分。
#![feature(portable_simd)]#!
使用 std::simd::*;
// 将每个输入字节扩展 8 次
pub fn batch_splat_scalar(x: [u8; 16]) -> [u64; 16] {
let mut ret = [0; 16];
for i in 0.16 {
ret[i] =
u64::from_le_bytes([x[i], x[i], x[i], x[i], x[i], x[i], x[i], x[i], x[i], x[i], x[i]]);
}
返回
}
pub fn batch_splat_simd(x: u8x16) -> u64x16 {
Simd::from_array(batch_splat_scalar(x.to_array()))
}
在使用 avx512 时,编译结果如下
所示
vpmovzxbq zmm0, qword ptr [rsi] vpbroadcastq zmm0.
vpbroadcastq zmm1,qword ptr [rip + .LCPI0_0] 代码
mov rax, rdi
vpmuludq zmm2、zmm0、zmm1
vpbroadcastq zmm3,qword ptr [rip + .LCPI0_1] vpmuludq zmm0,zmm0,zmm1
vpmuludq zmm0, zmm0, zmm3
vpsllq zmm0,zmm0,32
vporq zmm0,zmm2,zmm0
vmovdqu64 zmmword ptr [rdi],zmm0
VPMOVZXBQ ZMM0,QWORD Ptr [RSI + 8]
VPMULUDQ ZMM1,ZMM0,ZMM1
vpmuludq zmm0, zmm0, zmm3
VPSLLQ ZMM0,ZMM0,32
VPORQ ZMM0、ZMM1、ZMM0
vmovdqu64 zmmword ptr [rdi + 64],zmm0
vzeroupper
返回
https://godbolt.org/z/67cW5GnKf
是对的,生成的汇编代码对于执行复制扩展来说过于复杂。编译器没有选择最优的指令序列。
以下是一些使用 AVX-512 在 Rust 中对
u8
进行复制扩展到
u64
的更有效方法:
1. 使用
_mm512_broadcastb_epi64
intrinsics:
此 intrinsics 将一个字节广播到 64 位值的低 8 位。然后,可以使用移位和 OR 操作将广播的字节复制到 64 位值的所有字节。
#[target_feature(enable = "avx512f")]
unsafe fn batch_splat_simd_avx512(x: u8x16) -> u64x16 {
let broadcasted = _mm512_broadcastb_epi64(x.as_i32() as _);
// 创建一个掩码,其中每个字节都设置为 0x0101010101010101
let mask = _mm512_set1_epi64(0x0101010101010101);
// 将广播的值乘以掩码
let expanded = _mm512_mul_epu64(broadcasted, mask);
transmute(expanded)
}
2. 使用
vpbroadcastd
和 shuffle 指令:
此方法使用
vpbroadcastd
将每个字节广播到 32 位值。然后,使用
vpshufb
指令将 32 位值交错,生成所需的 64 位值。
#[target_feature(enable = "avx512f")]
unsafe fn batch_splat_simd_avx512_shuffle(x: u8x16) -> u64x16 {
let broadcasted = _mm256_broadcastd_epi32(x.as_i32() as _);
// 创建一个 shuffle 掩码,用于交错 32 位值
let shuffle_mask = _mm256_set_epi8(
15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
);
// 使用 shuffle 掩码交错广播的值
let shuffled = _mm256_shuffle_epi8(broadcasted, shuffle_mask);
transmute(_mm512_castps256_ps512(shuffled))
}
这些方法应该比最初的代码生成更有效的汇编代码。请注意,这些示例使用
unsafe
代码,因为它们使用 intrinsics 和假设对齐的内存访问。在生产代码中使用这些函数之前,请确保了解其含义。
此外,请务必使用
#[target_feature(enable = "avx512f")]
属性来启用 AVX-512 优化。