我试图对功能进行矢量化(SSE / AVX)。在我找到的所有实现中,只需使用log
和exp
进行矢量化:
pow(x, y) = exp(y * log(x))
它适用于正x,但由于负数的对数是一个复数,因此不会对负x
起作用。是否有可能有效地矢量化战队,同时保持处理负x
数字的能力?
答案 0 :(得分:4)
这是一个通用的答案,没有充分利用你如何实际矢量化pow()的任何细节。
您可以检查基矢量的任何元素是否为负数,并在其上进行分支以在快速路径和慢速路径之间进行选择。
返回两个实部和虚部的向量,因此快速路径可以为虚部返回_mm_setzero_ps()
。不想要虚构部分的来电者可以忽略它(而不是必须随机抽取以提取真实/虚构交替矢量的真实部分。)
因此,仅传递非负基础的呼叫者获得的行为几乎与矢量化真实版本一样快。
但是传递负面和非负面混合的来电者将获得慢速版本。如果你可以对慢速版本进行矢量化,那就完美了。
如果它不适用于正基础,那么当混合时你可以同时运行并混合(基于您检查的相同比较掩码,看你是否需要慢速版本)。 / p>
对于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
}
}
确保帮助函数内联,这样您就不会通过内存通过引用传递向量。
和/或将此包装内联到调用者中,这可以让检查针对常量向量进行优化。
我不认为Windows或System V ABI会在两个__m256
寄存器中返回两个ymm
向量的结构,因此第二个按引用arg可能是你最好的#&# 39;重新开始。
请注意,imag_result
是最后一个arg,所以即使在Windows x64 ABI中,此函数仍然可以将其args在相同的寄存器中转发到pow_nonegative(base, exp);
。虽然你想要它内联。
答案 1 :(得分:0)
好吧,如果你用负数做一些算术,你可以使用log()
函数的主分支,它会在结果中引入一个虚构的+i*b*pi
数字,然后在{{1因子.....只在你的指数exp(+i*b*pi)
是一个精确整数的情况下映射到实数.....这不是你的一般情况,因为函数b
声明为pow(3)
类型....您可以解决此问题,编写一个检查负第一个参数的包装函数,并且只有在第二个参数是奇数时才给出否定值。隐藏实现细节作为练习让您完成...很容易实现double
执行此检查并给出适当的结果。
但是如果指数double generalized_pow(double b, double e);
参数将是整数...那么你最好实现fast power algorithm,这将给你答案可能比通过{{1}更快}和b
函数。在Google中查看快速指数算法的实现。