我们有一个CFD求解器,在运行模拟时,发现它在某些机器上运行速度非常慢,而在其他机器上却运行得非常慢。使用英特尔VTune,发现以下行是问题(在Fortran中):
RHOV= RHO_INF*((1.0_wp - COEFF*EXP(F0)))**(1.0_wp/(GAMM - 1.0_wp))
使用VTune进行钻取,问题跟踪到call pow
装配线,并且在跟踪堆栈时,它显示它正在使用__slowpow()
。经过一番搜索,this page出现抱怨同样的事情。
在具有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会怎么样?这是否会导致pow
中的customPow.o
与libc中的pow
之间出现命名冲突?
答案 0 :(得分:22)
好吧,等一下吧。图书馆没有打电话给__slowpow()
只是为了和你一起玩;它正在调用__slowpow()
,因为它认为必须提供额外的精确度才能为您提供的值提供准确的结果(在这种情况下,基数非常接近1,指数为1)。如果你关心这个计算的准确性,你应该理解为什么会这样,如果在尝试解决它之前它是否重要。可能是这样的情况,对于(比方说)大的负F0,整个事情可以安全地舍入到1;或者它可能不会,取决于之后用这个值做了什么。如果您需要1.d0减去此结果,您将需要更高的精度。
答案 1 :(得分:7)
只需编写自己的pow
函数,将.o
文件放在链接器库路径中某处的静态库归档libmypow.a
中,并在链接时传递-lmypow
。 / p>
答案 2 :(得分:3)
pow(a,b)
与exp(b*ln(a))
相同,也许替换会对您有用。
答案 3 :(得分:1)
我自己测试了这个,确实如果我从链接到它的页面编译测试程序,它在汇编代码中使用call pow
。但是,使用优化-ffast-math
进行编译时,没有调用pow,但结果略有不同。