我正在尝试对 pow 函数进行矢量化(SSE/AVX)。在我发现的所有实现中,它只是使用 logexp 进行矢量化:

pow(x, y) = exp(y * log(x))

它适用于正 x,但不适用于负 x,因为负数的对数是复数。是否可以在保持处理负 x 数字的能力的同时有效地矢量化 pow?

最佳答案

这是一个通用答案,没有利用您可能如何实际矢量化 pow() 的任何细节。

您可以检查基 vector 的任何元素是否为负,并在其上进行分支以在快速路径和慢速路径之间进行选择。

返回实部和虚部的两个 vector ,因此快速路径可以返回虚部的 _mm_setzero_ps()。不想要虚部的调用者可以忽略它(而不是必须洗牌以提取实部/虚部交替 vector 的实部)。

因此,仅传递非负基数的调用者获得的行为几乎与矢量化的仅实数版本一样快。

但是传递否定和非否定混合的调用者将获得慢速版本。如果您可以对慢速版本进行矢量化,那就完美了。

如果它不适用于正基数,则当存在混合时,您可以同时运行和混合(基于您检查过的相同比较掩码以查看是否需要慢速版本)。

对于 AVX 版本,在内部名称中输入额外的 256。 (并将检查更改为 == 0xff ,因为在 movemask 结果中还有 4 位)。

// SSE4.1 for BLENDVPS
__m128  pow_complexresult(__m128 base, __m128 exp, __m128 &imag_result)
{
    __m128 negbase_vec = _mm_cmplt_ps(base, _mm_setzero_ps());
    unsigned negbase_mask = _mm_movemask_ps(negbase_vec);

    if (negbase_mask == 0) {               // all elements false
        imag_result = _mm_setzero_ps();
        return pow_nonegative(base, exp);   // fast path
    } else if (negbase_mask == 0xf) {      // all elements true
        return pow_negative(base, exp, imag_result);
    } else {
        // Only needed if pow_negative doesn't work for non-negative inputs.
        __m128 negpow = pow_negative(base, exp, imag_result);
        __m128 pospow = pow_simple(base, exp);
        imag_result = _mm_andn_ps(negbase_mask, imag_result);  // blend imaginary part
        return _mm_blendv_ps(pospow, negpow, negbase_vec);  // blend real part
    }
}

确保辅助函数内联,这样您就不会真正通过内存通过引用传递 vector 。

和/或将此包装器内联到调用者中,这可能会让检查优化为常量 vector 。

我认为 Windows 或 System V ABI 都不会在两个 __m256 寄存器中返回两个 ymm vector 的结构,因此第二个引用 arg 可能是您将要获得的最佳选择。

请注意, imag_result 是最后一个 arg,因此即使在 Windows x64 ABI 中,此函数仍可以将其在相同寄存器中的 args 转发到 pow_nonegative(base, exp); 。尽管无论如何您都希望它内联。

关于c++ - 如何矢量化 pow 函数(具有负基数)?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46390186/

10-11 16:00