c++ - 如何使用_mm_extract_epi8函数?

标签 c++ visual-studio vectorization sse simd

<分区>

我正在使用 _mm_extract_epi8 (__m128i a, const int imm8) 函数,它有 const int 参数。当我编译此 c++ 代码时,收到以下错误消息:

Error C2057 expected constant expression

__m128i a;

for (int i=0; i<16; i++)
{
    _mm_extract_epi8(a, i); // compilation error
}

如何在循环中使用这个函数?

最佳答案

首先,即使可能,您也不会希望在循环中使用它,并且您不会希望使用 16x pextrb 完全展开循环。该指令在 Intel 和 AMD CPU 上花费 2 微指令,并且会在随机端口(以及用于 vec->int 数据传输的端口 0)上出现瓶颈。

_mm_extract_epi8 内在函数需要编译时常量索引,因为 the pextrb r32/m8, xmm, imm8 instruction仅适用于作为立即数的索引(嵌入到指令的机器代码中)。


如果您想放弃 SIMD 并在 vector 元素上编写标量循环,您应该存储/重新加载这么多元素。所以你应该用 C++ 这样写:

alignas(16) int8_t bytes[16];  // or uint8_t
_mm_store_si128((__m128i*)bytes, vec);
for(int i=0 ; i<16 ; i++) {
    foo(bytes[i]);
}

一次存储的成本(以及存储转发延迟)分摊到 16 次重新加载中,每次重新加载仅花费 1 movsx eax, byte ptr [rsp+16] 或任何其他内容。 (在 Intel 和 Ryzen 上为 1 uop)。或者在重新加载时使用 uint8_tmovzx 零扩展到 32 位。现代 CPU 每个时钟可以运行 2 个加载微指令, vector 存储 -> 标量重新加载存储转发是高效的(~6 或 7 个周期延迟)。


对于 64 位元素,movq + pextrq 几乎肯定是您的最佳选择。存储 + 重新加载的前端成本相当,延迟比提取更差。

对于 32 位元素,它更接近收支平衡,具体取决于您的循环。如果循环体很小,展开的 ALU 提取可能会很好。或者您可以存储/重新加载但确实使用 _mm_cvtsi128_si32 (movd) 执行第一个元素以在第一个元素上实现低延迟,以便 CPU 可以在存储时处理它-发生高元素的转发延迟。

对于 16 位或 8 位元素,如果您需要遍历所有 8 位或 16 位元素,几乎可以肯定存储/重新加载更好。

如果您的循环对每个元素进行非内联函数调用,Windows x64 调用约定有一些调用保留的 XMM 寄存器,但 x86-64 System V 没有。因此,如果您的 XMM reg 需要围绕函数调用进行溢出/重新加载,最好只进行标量加载,因为编译器无论如何都会将其保存在内存中。 (希望它可以优化它的第二个拷贝,或者您可以声明一个 union 。)

print a __m128i variable用于所有元素大小的工作存储 + 标量循环。


如果您真的想要一个水平总和,或者最小值或最大值,您可以在 O(log n) 步中随机播放,而不是 n 次标量循环迭代。 Fastest way to do horizontal float vector sum on x86 (还提到了 32 位整数)。

对于字节元素求和,SSE2 有一个特例 _mm_sad_epu8(vec, _mm_setzero_si128())Sum reduction of unsigned bytes without overflow, using SSE2 on Intel .

您还可以使用它来处理有符号字节,方法是将范围转换为无符号字节,然后从总和中减去 16*0x80https://github.com/pcordes/vectorclass/commit/630ca802bb1abefd096907f8457d090c28c8327b

关于c++ - 如何使用_mm_extract_epi8函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54492956/

相关文章:

c# - C# 中的不良 C++ 程序员行为

c++ - 在 C++ 中正确使用 assert()

c++ - 可以从文件列表创建项目的 C/C++ 编辑器

c++ - 访问冲突(未处理的异常)

visual-studio - 使用 Visual Studio 调试 TypeScript 代码

c# - VS2017 无法启动调试。无法加载文件或程序集

matlab - 向量化Matlab - 如何在没有循环的情况下向量化高斯函数(代码)

c++ - 使用 std::equal 和命名空间时出现 "No match operator=="错误

python - 如何理解字节对编码?

python - 如何加速 pandas 字符串函数?