有没有办法在 AVX 上模拟 _m256 类型的整数按位运算?

Posted

技术标签:

【中文标题】有没有办法在 AVX 上模拟 _m256 类型的整数按位运算?【英文标题】:Is there a way to simulate integer bitwise operations for _m256 types on AVX? 【发布时间】:2013-12-11 19:03:14 【问题描述】:

我已经设法在 SSE2 中实现了一个布尔表达式。现在我想尝试在 AVX 中实现它,利用额外的因子 2 增加并行度(从 128 位 SIMD 类型到 256)。但是,AVX 不支持整数运算(AVX2 支持,但我正在使用 Sandy Bridge 处理器,因此目前不是一个选项)。但是,由于有AVX intrinsics for bitwise operations。我想我可以尝试将我的整数类型转换为浮点类型,看看它是否有效。

第一次测试成功:

__m256 ones = _mm256_set_ps(1,1,1,1,1,1,1,1);
__m256 twos = _mm256_set_ps(2,2,2,2,2,2,2,2); 
__m256 result = _mm256_and_ps(ones, twos);

我按我应该做的那样把所有的 0 都弄掉了。同时将两者相加,我得到的结果为 2。但是当相应地尝试 11 XOR 4 时:

__m256 elevens = _mm256_set_ps(11,11,11,11,11,11,11,11); 
__m256 fours = _mm256_set_ps(4,4,4,4,4,4,4,4); 
__m256 result2 = _mm256_xor_ps(elevens, fours); 

结果是 6.46e-46(即接近 0)而不是 15。同时执行 11 OR 4 给我的值是 22,而不是应该的 15。我不明白为什么会这样。是我缺少的错误还是某些配置?

我实际上期待我的假设与浮点数一起工作,就好像它们是整数一样,因为初始化为浮点值的整数实际上可能不是精确值,而是一个接近的近似值。但即便如此,我对我得到的结果感到惊讶。

有没有人能解决这个问题,或者我必须升级我的 CPU 以获得 AVX2 支持来启用这个?

【问题讨论】:

听起来您将整数打印为浮点数以获得 6.46e-46。您确定您的 printf() 格式说明符正确吗? 我没有打印。我刚刚检查了 Visual Studio 调试器中的值。 【参考方案1】:

第一次测试是偶然的。

1 作为浮点数是 0x3f800000,2 是 0x40000000。一般来说,它不会那样工作。

但是您绝对可以做到,您只需确保使用正确的位模式即可。不要将整数转换为浮点数 - 重新解释转换它们。这对应于诸如_mm256_castsi256_ps 之类的内在函数,或者将您的整数存储到内存中并将它们作为浮点数读取(这不会改变它们,通常只有数学运算关心浮点数的含义,其余的使用原始位模式,检查指令可以确保的异常列表)。

【讨论】:

啊哈。谢谢。这就说得通了。我试一试,如果可行,将您的答案标记为正确。 @Toby999 但请注意,在所有当前的英特尔处理器上,按位逻辑指令的浮点版本的吞吐量只有整数版本的 1/3。因此,如果您这样做是为了提高性能,您可能需要三思而后行。除非您受到解码器带宽的限制,否则它甚至可能适得其反。 在 Sandy 和 Ivy Bridge 上,整数 SSE 按位逻辑可以一个周期进入端口 0、1 或 5 中的任何一个。每个周期 3 个。但浮点 SSE 按位逻辑只能以一个/周期进入端口 5。因此,每个周期限制为 1 个。在 Haswell 上,它是相同的,但它具有 AVX2 - 这使得这一点没有实际意义。 您可以将 AVX 整数加载和存储操作(例如 _mm256_loadu_si256)与 AVX 一起使用,但您不能对 AVX 执行整数操作(例如 _mm256_add_epi32)。所以你应该能够加载整数然后使用_mm256_and_ps 感谢您的额外投入。在确实成功实现了完整数学表达式的初始版本后,AVX 版本的吞吐量低于 SSE2 版本。我猜是因为你的解释神秘。无论如何,我并没有期待太多额外的东西,因为无论如何我认为我非常接近内存读取最大带宽。不过令人失望。 ;(【参考方案2】:

您不需要 AVX2 来使用 AVX 整数加载和存储操作:请参阅intel intrinsic guide。因此,您可以使用 AVX 加载整数,重新解释转换为浮点数,使用浮点按位运算,然后重新解释转换回 int。 reinterpret-casts 不会生成任何指令,它们只会让编译器满意。试试这个:

//compiled and ran on an Ivy Bridge system with AVX but without AVX2
#include <stdio.h>
#include <immintrin.h>
int main() 
    int a[8] = 0, 2, 4, 6, 8, 10, 12, 14;
    int b[8] = 1, 1, 1, 1, 1,  1,  1,  1;
    int c[8];

    __m256i a8 = _mm256_loadu_si256((__m256i*)a);
    __m256i b8 = _mm256_loadu_si256((__m256i*)b);
    __m256i c8 = _mm256_castps_si256(
        _mm256_or_ps(_mm256_castsi256_ps(a8), _mm256_castsi256_ps(b8)));
    _mm256_storeu_si256((__m256i*)c, c8);
    for(int i=0; i<8; i++) printf("%d ", c[i]); printf("\n");
    //output: 1 3 5 7 9 11 13 15

当然,正如 Mystical 指出的那样,这可能不值得这样做,但这并不意味着你不能这样做。

【讨论】:

感谢您的意见。这很有帮助,因为挖掘正确的内在方法非常耗时。 有对齐变量的选项,因此您不需要处理未对齐的负载 @LưuVĩnhPhúc,我一直假设它不再重要。对齐和非对齐加载/存储指令的吞吐量和延迟在对齐内存上是相同的。这就是理论。但在实践中我仍然看到了差异,所以我同意你应该使用对齐的加载指令。

以上是关于有没有办法在 AVX 上模拟 _m256 类型的整数按位运算?的主要内容,如果未能解决你的问题,请参考以下文章

加载指令与 AVX 中的 AVX2 __m256i const* mem_addr [关闭]

AVX2 的汇编错误

为 AVX、m256 实现 ln(x) [关闭]

有符号 32 位元素的 AVX __m256i 整数除法

有没有办法用 AVX2 编写 _mm256_shldi_epi8(a,b,1) ? (向量之间每 8 位元素移位一位)

如何在 AVX 寄存器上打包 16 个 16 位寄存器/变量