替换极其缓慢的pow()函数

我们有一个CFD求解器,在运行模拟时,发现它在某些机器上运行速度非常慢,而在其他机器上却运行得非常慢。 使用英特尔VTune,发现以下行是问题(在Fortran中):

RHOV= RHO_INF*((1.0_wp - COEFF*EXP(F0)))**(1.0_wp/(GAMM - 1.0_wp)) 

使用VTune进行钻取,问题可以追溯到call pow组装线,并且在跟踪堆栈时,它显示它正在使用__slowpow() 。 经过一番搜索, 这个页面出现了抱怨同样的事情。

在具有libc版本2.12的机器上,模拟花了18秒。 在具有libc版本2.14的机器上,模拟花了0秒。

基于上述页面的信息,当pow()的基数接近1.0时出现问题。 所以我们做了另一个简单的测试,我们在pow()之前用任意数字缩放基数,然后除以在pow()调用之后提升到指数的数字。 使用libc 2.12,运行时间从18秒减少到0秒。

但是,将这些全部放在我们执行a**b的代码上是不切实际的。 如何更换libc中的pow()函数? 例如,我希望Fortran编译器生成的组装线call pow调用我们编写的自定义pow()函数来执行缩放,调用libc pow()然后除以缩放。 如何创建一个对编译器透明的中间层?

编辑

为了澄清,我们正在寻找类似(伪代码)的东西:

 double pow(a,b) { a *= 5.0 tmp = pow_from_libc(a,b) return tmp/pow_from_libc(5.0, b) } 

是否可以从libc加载pow并在我们的自定义函数中重命名以避免命名冲突? 如果customPow.o文件可以从libc重命名pow ,如果其他东西仍然需要libc会发生什么? 这会导致customPow.o pow和libc中的pow之间的命名冲突吗?

只需编写自己的pow函数,将.o文件放在链接器库路径中某处的静态库归档文件libmypow.a中,并在链接时传递-lmypow

好吧,坚持吧。 图书馆不是为了和你__slowpow()而调用__slowpow() ; 它正在调用__slowpow()因为它认为需要额外的精度才能给出你给它的值的准确结果(在这种情况下,基数非常接近1,指数为1)。 如果你关心这个计算的准确性,你应该理解为什么会这样,如果在尝试解决它之前它是否重要。 可能的情况是,对于(比方说)大的负F0,整个事情可以安全地舍入到1; 或者可能不会,取决于稍后对此值的处理方式。 如果您需要1.d0减去此结果,您将需要额外的精度。

pow(a,b)exp(b*ln(a)) ,也许替换对你有用。

我自己测试了这个,事实上如果我从链接到它的页面编译测试程序,它在汇编代码中使用call pow 。 但是,使用优化-ffast-math编译时没有调用pow,但结果略有不同。