c++ - 将 32 位整数的 vector 相乘,仅取高 32 位

标签 c++ intrinsics low-level avx512

我想将两个 512 位 __m512i 相乘16 个无符号 32 位整数组成的 vector 在一起,并且仅从乘法的 64 位结果中取出高 32 位。尽管英特尔内在函数指南说 _mm512_mulhi_epu32存在,它无法在我的机器上编译。

答案here声称_mm512_srli_epi64(_mm512_mul_epu32(a,b),32)可以,但不行 - 问题似乎是 _mm512_mul_epu32仅考虑位 0...31、64...95 等,忽略奇数位置的值。

如何最快地从 32 位 vector 乘法的结果中取出高 32 位?

最佳答案

vpmuludq 又名 _mm512_mul_epu32采用偶数源 32 位元素(0、2、4 等)1。这使其能够在每个 64 位 block 内高效执行,将输入的低 32 位馈送到 FP 尾数乘法器。这是一个加宽,又称为全乘法,而不是高半乘法,因此它当然必须忽略一些输入(因为没有 SIMD 数学指令有两个 vector 目标。)

因此,您需要使用它两次才能获得所需的所有高半结果:一次使用偶数元素,一次使用偶数位置的奇数元素(右移两个输入 vector ) 。然后您需要交错这些 64 位元素的高半部分。

诀窍在于高效地做到这一点:AVX-512 vpermt2d从 2 个源 vector 中选取 32 位元素可以在单个微指令中完成工作。所以这很棒,特别是在允许编译器提升洗牌控制 vector 常量负载的循环中。其他选项包括_mm512_mask_shuffle_epi32 ( vpshufd 带有合并掩码)将高半部分复制到 1 个 vector 中,并合并到结果的另一个 vector 中,给定 k 中的合并控制登记。 (vpmuludq 结果之一具有您想要的高半部分,因为输入是右移的)。 vmovshdup ( _mm512_mask_movehdup_ps ) 在少 1 个字节的机器代码中执行相同的洗牌操作,无需立即执行。内在函数很不方便,因为你需要强制转换 __m512i__m512_mm512_castsi512_ps ,但应该具有相同的性能。

或者甚至存储两次,并对第二次存储进行屏蔽,但这可能很糟糕,因为其中一个存储必须未对齐(因此 64 字节存储的缓存行交叉)。尽管如此,它确实避免了更多的 ALU uops。

更“明显”的选项(就像您对 AVX2 所做的那样)是 vpsrld ( _mm512_srli_epi64(v,32) ) 其中之一,然后 vpblendd 。但这需要 2 个独立的 ALU 微指令,并且在当前 CPU 上使用 512 位 vector 意味着只有 2 个 vector ALU 执行端口可以处理它们。另外,vpblendd没有 AVX-512 版本;仅存在采用 k 中的控制操作数的混合。登记。 (使用shift/AND和OR合并会更糟,并且仍然需要 vector 常量)

__m512i mulhi_epu32_512(__m512i a, __m512i b)
{
    __m512i evens = _mm512_mul_epu32(a,b);
    __m512i odds = _mm512_mul_epu32(_mm512_srli_epi64(a,32), _mm512_srli_epi64(b,32));
    return _mm512_mask_shuffle_epi32(odds, 0x5555, evens, _MM_SHUFFLE(3,3,1,1)); 

    // _mm512_mask_movehdup_ps may be slightly more efficient, saving 1 byte of code size
}

对于独立函数,clang 将合并屏蔽的随机播放优化为 vpermi2d使用内存中的 vector 常量,而不是 mov eax, 0x5555/kmovw k1, eax管他呢。当包含设置时,微指令会更少,但可能会丢失缓存。 GCC 按编写方式编译它。 https://godbolt.org/z/v4M7PK两者都显示。对于循环体(设置提升),任一方式都是单个 uop,但合并屏蔽 vpshufd只有 1 个延迟周期,而穿越车道则有 3 个延迟周期 vpermi2d/vpermt2d 。 ( https://uops.info/https://agner.org/optimize/ )


脚注 1:您链接的问答要么没有完全描述问题和/或解决方案,要么确实只需要 2 个数字(在 vector 的底部?),而不是 2 数字 vector

关于c++ - 将 32 位整数的 vector 相乘,仅取高 32 位,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64852344/

相关文章:

rust - 将结构复制到 Rust 中的特定内存位置

c++ - 模拟游戏机的内存映射,根据提供的地址访问不同的位置

c++ - 初始化 vector 时出现令人讨厌的错误

一组的 C++ mmap

具有隐藏依赖库的公共(public) api 的 C++ 动态库

c++ - C++子数组与现有数组的偏移量

c++ - _mm_mul_epu32 与 _mm_mul_epi32

c++ - 了解 `_mm_prefetch`

c - _mm_stream_load_si128 (movntdqa) 是否修改其参数指向的内存?

embedded - 在哪里可以了解有关低级编程的更多信息?例如设备驱动程序