C++ 迭代Kahan求和的优化实现
简介C++ 迭代Kahan求和的优化实现,c++,x86-64,sse,inline-assembly,fast-math,C++,X86 64,Sse,Inline Assembly,Fast Math,简介 Kahan求和/补偿求和是一种解决编译器无法尊重数字的关联特性的技术。截断误差导致(a+b)+c不完全等于a+(b+c),从而在较长的和序列上累积了不希望的相对误差,这是科学计算中的一个常见障碍 任务 我希望Kahan求和的最佳实现。我怀疑最好的性能可能是通过手工编制的汇编代码实现的 尝试 下面的代码使用三种方法计算[0,1]范围内1000个随机数的总和 标准求和:将均方根相对误差累加为O(sqrt(N))的简单实现 Kahan求和[g++]:使用c/c++函数“csum”进行补偿求和。评
Kahan求和/补偿求和是一种解决编译器无法尊重数字的关联特性的技术。截断误差导致(a+b)+c不完全等于a+(b+c),从而在较长的和序列上累积了不希望的相对误差,这是科学计算中的一个常见障碍 任务
我希望Kahan求和的最佳实现。我怀疑最好的性能可能是通过手工编制的汇编代码实现的 尝试
下面的代码使用三种方法计算[0,1]范围内1000个随机数的总和
#包括
#包括
#包括
外部“C”无效csumasm(双重、双重、双重);
__asm__(
“csumasm:\n”
“movsd(%rcx),%xmm0\n”//xmm0=a
“subsd(%r8),%xmm1\n”//xmm1-r8(c)| y=b-c
movapd%xmm0,%xmm2\n
“addsd%xmm1,%xmm2\n”//xmm2+xmm1(y)| b=a+y
movapd%xmm2,%xmm3\n
“subsd%xmm0,%xmm3\n”//xmm3-xmm0(a)| b-a
movapd%xmm3,%xmm0\n
“subsd%xmm1,%xmm0\n”//xmm0-xmm1(y)|-y
“movsd%xmm0,(%r8)\n”//xmm0到c
“movsd%xmm2,(%rcx)\n”//b到a
“ret\n”
);
void csum(double&a,double b,double&c){//此函数添加a和b,并将c作为补偿项传递
双y=b-c;//y是b参数的修正
b=a+y;//将已更正的b参数添加到参数中。当前总和的输出
c=(b-a)-y;//查找要作为补偿项传递的新错误
a=b;
}
双重乐趣(双重fMin,双重fMax){
双f=(双)rand()/rand_MAX;
返回fMin+f*(fMax-fMin);//返回随机值
}
int main(int argc,字符**argv){
int N=1000;
srand(0);//对每个方法使用0种子
双sum1=0;
对于(int n=0;n
带有-O3的输出为:
Standard summation:
5.1991955320902093e+002 (error: -3.4106051316484809e-013)
Kahan compensated summation [g++]:
5.1991955320902127e+002 (error: 0.0000000000000000e+000)
Kahan compensated summation [asm]:
5.1991955320902127e+002
使用-O3-ffast math进行输出
Standard summation:
5.1991955320902093e+002 (error: -3.4106051316484809e-013)
Kahan compensated summation [g++]:
5.1991955320902093e+002 (error: -3.4106051316484809e-013)
Kahan compensated summation [asm]:
5.1991955320902127e+002
很明显,-ffast math破坏了Kahan求和算法,这很不幸,因为我的程序需要使用-ffast math
问题
- “fun”的内容不能内联,但“csum”函数可以内联
- 总和必须作为一个迭代过程进行计算(校正项必须应用于每次加法)。这是因为预期的求和函数接受的输入取决于前一个求和李>
- 预期的求和函数被无限期地每秒调用数亿次,这促使我们追求高性能的低级别实现
- 由于性能原因,高精度算法(如long double、float128或任意精度库)不被视为高精度解决方案
您可以将不需要使用
-ffast-math
的函数(如csum循环)放入单独的文件中,该文件在编译时不使用-ffast-math
可能您也可以使用\uuuuuuuuuuuuuuu属性((优化(“无快速数学”))
,但遗憾的是,您说优化级别的杂注和属性“不适用于生产代码”
更新:问题的一部分显然是基于一种误解,即-O3
不安全,还是什么?它是;ISO C++指定FCC数学规则,就像GCC的代码> -FNO快速数学< /代码>。使用-O3
编译所有内容显然可以使OP的代码快速安全地运行。请参阅此答案的底部,了解类似OpenMP的变通方法,以便在不实际启用-ffast math
的情况下,为代码的某些部分获得快速数学的一些好处
ICC默认为fast path,因此您必须专门启用FP=strict,以便使用-O3时安全,但不管其他优化设置如何,gcc/clang默认为完全严格的FP。(除了-Ofast
=-O3-ffast数学
)
通过保持一个(或四个)总计向量和相等数量的补偿向量,您应该能够对Kahan求和进行向量化。您可以使用intrinsic实现这一点(只要不为该文件启用快速数学) e、 g.使用SSE2
\uuuu m128d
进行2次包装a
subsd xmm0, QWORD PTR [rsp+32]
movapd xmm1, xmm3
addsd xmm3, xmm0
movsd QWORD PTR [rsp+16], xmm3
subsd xmm3, xmm1
movapd xmm1, xmm3
subsd xmm1, xmm0
movsd QWORD PTR [rsp+32], xmm1