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

Posted

技术标签:

【中文标题】有符号 32 位元素的 AVX __m256i 整数除法【英文标题】:AVX __m256i integer division for signed 32-bit elements 【发布时间】:2019-02-26 16:19:56 【问题描述】:

我正在尝试在 AVX 机器中进行 SIMD 划分并遇到编译错误。

这是我的代码:

    __m256i  help;
    int arr[8];
    int arr2[8];
    help = _mm256_load_si256((__m256i*)arr);
    __m256i temp;
    temp = _mm256_load_si256((__m256i*)arr2);
    __m256i result;
    _mm256_div_ps(temp,help);

这是错误:

错误:无法将参数“1”的“__m256i aka __vector(4) long long int”转换为“__m256 aka __vector(8) float”到“__m256 _mm256_div_ps(__m256, __m256)” _mm256_div_ps(温度,帮助);

【问题讨论】:

_mm256_div_ps -- 正如名称中的 ps 所暗示的 -- 除以 Packet S 单精度浮点数,但不是整数。如果要近似除整数,请将它们转换为浮点数,除以它们并将结果转换回来。 (为了获得更好的结果转换为双精度,当然您需要将数组分成两半)。 如果你的编译器支持SVML,你可以使用_mm256_div_epi32 遗憾的是它不支持 SVML,但如果我想要整数除法,我可以只进行浮点除法并将其转换为整数吗?这会给我在所有情况下的正确结果吗? 如果你做float(即单精度)除法,你只会得到一个近似值。使用double,您应该得到准确的结果,但计算时间大约是原来的两倍。 似乎没有人明确提及它,但英特尔不支持任何本机 simd 整数除法。它需要以某种方式进行模拟,要么一直返回到 idiv,要么通过转换为浮点类型 【参考方案1】:

我建议将Vc: portable, zero-overhead C++ types for explicitly data-parallel programming 库用于 simd,我听说它的目标是包含在 C++ 标准中。它更容易写,更容易阅读。

例子:

#include <iostream>
#include <Vc/Vc>

int main() 
    using A = Vc::SimdArray<int, 8>;
    A arr1 = A::Random();
    A arr2 = A::Random();
    std::cout << arr1 << '\n';
    std::cout << arr2 << '\n';
    std::cout << arr1 / arr2 << '\n';

输出:

<1513634383 -963914658 1763536262 -1285037745 | -695608406 -35372374 1025922083 444041308>
<824703811 1962744590 1568022524 -293901648 | 549806324 248334095 1663905340 641164273>
[1, 0, 1, 4, -1, 0, 0, 0]

下面的函数

using A = Vc::SimdArray<int, 8>;

__attribute__((noinline)) A f(A a0, A a1) 
    return a0 / a1;

g++-8.2 -O3 -march=skylake 转换为以下程序集:

f(Vc_1::SimdArray<int, 8ul, Vc_1::Vector<int, Vc_1::VectorAbi::Avx>, 8ul>, Vc_1::SimdArray<int, 8ul, Vc_1::Vector<int, Vc_1::VectorAbi::Avx>, 8ul>):
    vcvtdq2pd   ymm3, xmm1
    vcvtdq2pd   ymm2, xmm0
    vextracti128    xmm1, ymm1, 0x1
    vextracti128    xmm0, ymm0, 0x1
    vcvtdq2pd   ymm1, xmm1
    vdivpd  ymm2, ymm2, ymm3
    vcvtdq2pd   ymm0, xmm0
    vdivpd  ymm0, ymm0, ymm1
    vcvttpd2dq  xmm2, ymm2
    vcvttpd2dq  xmm0, ymm0
    vinserti128 ymm0, ymm2, xmm0, 0x1
    ret

请注意,x86 指令集中没有用于整数除法的 simd 指令。

【讨论】:

简单又好用。很高兴看到(在答案中)这会产生什么指令。此外,一个 256 位的寄存器可以保存 8 个 int32(但如果 IO 格式取决于目标架构,那会令人困惑,IMO) 非常感谢,以后会尝试使用这个。但是上面的问题是针对家庭作业的,老师要我使用“正常”的方式。你知道我该如何解决上述情况吗? @OgiciBumKacar 如果您需要“手动”编写此代码,只需查找每条指令的内在函数即可。如果这是家庭作业,请务必正确引用此答案。 @Maxim 生成的代码看起来不错。感谢您展示该库! 我已经制作了另一个具有不同设计目标的零开销库:github.com/Const-me/IntelIntrinsics 这个库并没有尝试将这些向量封装到类中,仅提供内在函数的包装器。通过这种方式与其他库集成更容易,例如我经常将它与 Microsoft 的 DirectXMath 结合使用。

以上是关于有符号 32 位元素的 AVX __m256i 整数除法的主要内容,如果未能解决你的问题,请参考以下文章

AVX2 1x mm256i 32bit 到 2x mm256i 64bit

使用 AVX512 或 AVX2 计算所有压缩 32 位整数之和的最快方法

AVX2 上的 256 位 CRC 计算

AVX2 64位无符号整数比较

从填充为 0 的数组加载到 256 位 AVX2 寄存器

将 16 位值的 __m256i 打包(饱和)到 8 位值的 __m128i?