这是一个通用的答案,没有利用您如何实际矢量化 pow() 的任何细节。
您可以检查基向量的任何元素是否为负,并在此基础上进行分支以在快速路径和慢速路径之间进行选择。
返回实部和虚部的两个向量,因此快速路径可以为虚部返回_mm_setzero_ps()。不想要虚部的调用者可以忽略它(而不必洗牌以提取实部/虚部交替向量的实部)。
因此,仅传递非负基数的调用者获得的行为几乎与矢量化纯实数版本一样快。
但同时传递负数和非负数的调用者将获得慢版本。如果你可以对慢版本进行矢量化,那就完美了。
如果它不适用于正碱基,当有混合时,您可以同时运行并混合(基于您检查是否需要慢版本的相同比较掩码)。
对于 AVX 版本,在内部名称中键入额外的 256。 (并将检查更改为== 0xff,因为移动掩码结果中还有 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
}
}
确保帮助函数内联,这样您就不会真正通过内存通过引用传递向量。
和/或将此包装器内联到调用者中,这可能会让检查针对常量向量进行优化。
我认为 Windows 或 System V ABI 都不会在两个 ymm 寄存器中返回两个 __m256 向量的结构,因此第二个引用 arg 可能是您将获得的最好的。
注意imag_result 是最后一个arg,所以即使在Windows x64 ABI 中,这个函数仍然可以将相同寄存器中的args 转发给pow_nonegative(base, exp);。尽管您仍然希望它内联。