【发布时间】:2013-06-12 21:01:34
【问题描述】:
让我用一个陈述来断言这个问题;此代码按预期工作,但它的速度非常慢。有没有办法让牛顿法收敛得更快,或者设置一个 __m256 var 等于单个浮点数而不弄乱浮点数数组等?
__m256 nthRoot(__m256 a, int root){
#define aligned __declspec(align(16)) float
// uses the calculation
// n_x+1 = (1/root)*(root * x + a / pow(x,root))
//initial numbers
aligned r[8];
aligned iN[8];
aligned mN[8];
//Function I made to fill arrays
/*
template<class T>
void FillArray(T a[],T b)
{
int n = sizeof(a)/sizeof(T);
for(int i = 0; i < n; a[i++] = b);
}*/
//fills the arrays
FillArray(iN,(1.0f/(float)root));
FillArray(mN,(float)(root-1));
FillArray(r,(float)root);
//loads the arrays into the sse componenets
__m256 R = _mm256_load_ps(r);
__m256 Ni = _mm256_load_ps(iN);
__m256 Nm = _mm256_load_ps(mN);
//sets initaial guess to 1 / (a * root)
__m256 x = _mm256_rcp_ps(_mm256_mul_ps(R,a));
for(int i = 0; i < 20 ; i ++){
__m256 tmpx = x;
for(int k = 0 ; k < root -2 ; k++){
tmpx = _mm256_mul_ps(x,tmpx);
}
//f over f'
__m256 tar = _mm256_mul_ps(a,_mm256_rcp_ps(tmpx));
//fmac with Ni*X+tar
tar = _mm256_fmadd_ps(Nm,x,tar);
//Multipled by Ni
x = _mm256_mul_ps(Ni,tar);
}
return x;
}
编辑#1
__m256 SSEnthRoot(__m256 a, int root){
__m256 R = _mm256_set1_ps((float)root);
__m256 Ni = _mm256_set1_ps((1.0f)/((float)root));
__m256 Nm = _mm256_set1_ps((float)(root -1));
__m256 x = _mm256_mul_ps(a,_mm256_rcp_ps(R));
for(int i = 0; i < 10 ; i ++){
__m256 tmpx = x;
for(int k = 0 ; k < root -2 ; k++){
tmpx = _mm256_mul_ps(x,tmpx);
}
//f over f'
__m256 tar = _mm256_mul_ps(a,_mm256_rcp_ps(tmpx));
//mult nm x then add tar because my compiler stoped thinking that fmadd is a valid instruction
tar = _mm256_add_ps(_mm256_mul_ps(Nm,x),tar);
//Multiplied by the inverse of power
x = _mm256_mul_ps(Ni,tar);
}
return x;
}
任何使牛顿方法收敛更快的提示或指针(不是内存类型)将不胜感激。
在使用 _mm256_rcp_ps() 调用 _mm256_set1_ps() 函数时删除了编辑 #2,因为我已经将所需内容的倒数加载到 R 中
__m256 SSEnthRoot(__m256 a, int root){
__m256 R = _mm256_set1_ps((float)root);
__m256 Ni = _mm256_rcp_ps(R);
__m256 Nm = _mm256_set1_ps((float)(root -1));
__m256 x = _mm256_mul_ps(a,Ni);
for(int i = 0; i < 20 ; i ++){
__m256 tmpx = x;
for(int k = 0 ; k < root -2 ; k++)
tmpx = _mm256_mul_ps(x,tmpx);
//f over f'
__m256 tar = _mm256_mul_ps(a,_mm256_rcp_ps(tmpx));
//fmac with Ni*X+tar
//my compiler believes in fmac again
tar = _mm256_fmadd_ps(Nm,x,tar);
//Multiplied by the inverse of power
x = _mm256_mul_ps(Ni,tar);
}
return x;
}
编辑#3
__m256 SSEnthRoot(__m256 a, int root){
__m256 Ni = _mm256_set1_ps(1.0f/(float)root);
__m256 Nm = _mm256_set1_ps((float)(root -1));
__m256 x = _mm256_mul_ps(a,Ni);
for(int i = 0; i < 20 ; i ++){
__m256 tmpx = x;
for(int k = 0 ; k < root -2 ; k++)
tmpx = _mm256_mul_ps(x,tmpx);
__m256 tar = _mm256_mul_ps(a,_mm256_rcp_ps(tmpx));
tar = _mm256_fmadd_ps(Nm,x,tar);
x = _mm256_mul_ps(Ni,tar);
}
return x;
}
【问题讨论】:
-
当您切换到使用
_mm256_set1_ps时,速度提高了多少,需要提高多少? -
每个函数的 1000000 只加速了 86 毫秒。改进功能的时间 = 2816。旧功能 2900 磨机的时间。我将 SSEnthRoot 函数的 for 循环迭代次数固定为与未改进的相同。
-
现在的速度是多少?你的目标是什么?不过,这些数字似乎非常错误,因为每个函数调用需要 3 µs,这太高了(除非
root是一个很大的数字?)。 -
我找到了第 29 个根。
-
您的 pow 函数效率低下。您使用 27 次乘法来计算 x^28。只需 6 次乘法即可完成。我在答案中添加了一个函数,可以更有效地使用 AVX。
标签: c simd intrinsics avx