c++ - 乘以 32 位整数的向量,只取高 32 位
问题描述
我想将 16 个无符号 32 位整数的两个 512 位__m512i
向量相乘,并且只取 64 位乘法结果的高 32 位。尽管英特尔内在函数指南说_mm512_mulhi_epu32
存在,但它不会在我的机器上编译。
这里的答案声称这_mm512_srli_epi64(_mm512_mul_epu32(a,b),32)
会起作用,但它不起作用 - 问题似乎是_mm512_mul_epu32
只考虑位 0...31、64...95 等,而忽略奇数位置的值。
如何最快地从 32 位向量乘法的结果中获取高 32 位?
解决方案
vpmuludq
aka_mm512_mul_epu32
采用偶数源 32 位元素 (0, 2, 4, etc) 1。这使它可以有效地执行,在每个 64 位块内将输入的低 32 位馈送到 FP 尾数乘法器。这是一个扩展的全乘法,而不是高半乘法,所以它当然必须忽略一些输入(因为没有 SIMD 数学指令有两个向量目标。)
因此,您需要使用它两次才能获得所需的所有高半结果:一次使用偶数元素,一次使用偶数位置的奇数元素(右移两个输入向量)。然后,您需要将这些 64 位元素的高半部分交错。
诀窍是有效地做到这一点:AVX-512vpermt2d
从 2 个源向量中挑选 32 位元素可以在单个 uop 中完成工作。所以这很好,特别是在一个让编译器提升随机控制向量常量的负载的循环中。其他选项包括_mm512_mask_shuffle_epi32
(vpshufd
使用合并屏蔽)将高半部分复制到 1 个向量中,并合并到结果的另一个向量中,给定k
寄存器中的合并控制。(其中一个vpmuludq
结果有你想要的高半部分,因为输入是右移的)。 vmovshdup
( _mm512_mask_movehdup_ps
) 在少 1 个字节的机器代码中执行相同的洗牌,不需要立即进行。使用内在函数很不方便,因为您需要转换__m512i
为__m512
with _mm512_castsi512_ps
,但应该具有相同的性能。
甚至存储两次,对第二个存储进行屏蔽,但这可能很糟糕,因为其中一个存储必须未对齐(因此 64 字节存储的缓存行交叉)。尽管如此,它确实避免了更多的 ALU 微指令。
更“明显”的选项(就像您对 AVX2 所做的那样)是vpsrld
( _mm512_srli_epi64(v,32)
) 其中一个,然后是vpblendd
. 但这需要 2 个单独的 ALU 微指令,并且在当前 CPU 上使用 512 位向量意味着只有 2 个向量 ALU 执行端口可以处理它们。此外,vpblendd
没有 AVX-512 版本;只有混合将控制操作数放在k
寄存器中。(使用 shift / AND 和 OR 来合并会更糟,仍然需要一个向量常数)
__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 将合并掩码的 shuffle 优化为vpermi2d
带有来自内存的向量常量的 a,而不是mov eax, 0x5555
/kmovw k1, eax
或其他。包含设置时的微指令更少,但可能缓存未命中。GCC 将其编译为书面形式。 https://godbolt.org/z/v4M7PK显示两者。对于循环体(已提升设置),任何一种方式都是单个 uop,但合并屏蔽vpshufd
只有 1 个延迟周期,而车道交叉vpermi2d
/则为 3 个vpermt2d
。(https://uops.info/和https://agner.org/optimize/)
脚注 1:您链接的问答要么没有完全描述问题和/或解决方案,要么真的只需要 2 个数字(在向量的底部?),而不是 2个数字向量。
推荐阅读
- mongodb - 在 M1 Apple 芯片上安装 mongodb 时无法点击 mongodb/brew:tap 中的语法无效
- python - python XLRD库如何扫描excel中的所有工作表?
- java - 覆盖具有相同类策略与策略实例的 equals 方法
- sql - 在 SQL Server 数据库中查找以小写字母开头的列并将它们列为 SELECT 语句
- php - PHP Elastic Search - 在执行搜索之前获取所有文档(以节省时间/性能)
- c - 如何实现“malloc”和“fgets”来读取和打印输入流
- python - 如何在数据着色器热图中填充或插入稀疏数据空白空间(欠采样)?
- amazon-s3 - EMR Spark 无法将小型 PySpark 数据帧写入 S3
- flutter - Flutter:带有下划线的电话号码的预屏蔽输入
- html - 使用 node.js 按表单上传文件