各种数学运算的简单基准

2020-07-28 13:45:48

2014年11月9日,各种基本浮点数学运算的计算成本有多高?这里有一个快速而肮脏的基准,虽然肯定相当天真,但似乎捕捉到了一些运算的粗略和相对成本。

这个季度我要上一门关于数值线性代数的课程。当然,我们涵盖了浮点算术基础、数值稳定性、矩阵分解和算法分析等主题。

在我们的一些任务中,我们的任务是确定执行某些特定算法所需的浮点操作(FLOP)的确切数量。分析算法的复杂性当然是一项有用的练习,但是给人留下这样的印象是愚蠢的,那就是我们可以产生准确的失败计数。我不是一个很喜欢硬件的人,但我至少知道现代的CPU非常复杂,我们在检查伪代码后可以写在纸上的任何失败计数都没有机会以任何有意义的方式“准确”。

除了这些练习的整体徒劳无益外,我们还奉命将以下每一种运算都视为一个“1翻转”的基准:加法、减法、乘法、除法、平方根。“正如我们很快就会看到的那样,这些操作在现实世界的计算费用方面甚至没有接近相等,所以把这些都算作相等的计算原子是有误导性的。

不管怎样,…。在最近的一次任务中,我们被要求查看使用旋转矩阵的一些技术的翻转计数。正如您可能对旋转y元素所期望的那样,矩阵条目基于各种三角函数(正弦、余弦、反正弦)。“我在做作业的时候很匆忙,所以我随随便便地给这些TRIG函数每个分配了1个FLUP。我觉得这样做在一定程度上是合理的,因为我刚刚了解到,在现代Intelx86中,这些TRIG函数实际上是作为单CPU指令提供的。考虑到我们已经粉饰了这么多,将理论上可以在一条CPU指令中完成的任何事情视为原子似乎是情有可原的。不管怎样,我被扣分了。哼。

好吧,好吧,我不是很担心分数,但现在我很好奇这些手术到底有多贵。如果“平方根”是1个翻转,那么把“正弦”也归为1个翻转真的是错的吗?

我清理了大脑的C++区域,编写了以下测试程序。在这里大量使用宏很有趣。

#include<;math.h>;#include<;chrono>;使用命名空间std::chrono;//从//https://gist.github.com/gongzhitaao/7062087class复制的计时器{PUBLIC:TIMER():BEG_(CLOCK_::NOW()){}VALID RESET(){BEG_=CLOCK_::NOW();}Double Lapsed()const{Return Duration_Cast<;Second_>;(CLOCK_::NOW()。}PRIVATE:tyfinf HIGH_RESOLUTION_CLOCK CLOCK_;tyfinf Duration<;Double,Ratio<;1>;>;Second_;time_point<;lock_>;beg_;};int main(char*argv){Double Total;Timer tmr;#Define randf()((Double)Rand())/((Double)(RAND_Max))#定义OP_TEST(name,expr)\total=0.0;\for(int i=0;i<;100000000;i++){\Double R1=Randf();\Double R2=Randf();\Total+=expr;\}\Double Name=tmr.elapsed();\printf(#name);\printf(";%.7f\n";,name-Baseline);//计时基线代码://for循环,没有额外的数学OP_TEST(Baseline,1.0)/。//减去基线时间,得到//更好的成本近似值//对于指定的操作op_test(加,R1+R2)op_test(减,R1-R2)op_test(mult,R1*R2)op_test(div,R1/R2)op_test(sqrt,sqrt(R1))op_test(sin,sin(R1))op_test(cos,cos(R1))op_test(tan,tan(R1))op_test

编译这个未经优化的基准测试(这样就不会擦除或重新排列任何内容),并在我的Intel Corei7机器上运行,我会得到以下结果。我已经正常化了,所以结果是相对于“加”的成本的。

因此,在“1翻转”运算中,除法和平方根的成本实际上分别是加法基线的4倍和6倍左右。Trig函数似乎落在15x-20x范围内的某个地方,而exp大约是10x。

所以,是的,触发器函数确实比+-*√昂贵得多(尽管将sqrt和除法算作1次FLUP似乎仍然有点武断)。