26

我们有一个 CFD 求解器,在运行模拟时,发现它在某些机器上运行异常缓慢,但在其他机器上却没有。使用 Intel 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 秒。

根据上述页面上的信息,当 base topow()接近 1.0 时会出现问题。所以我们做了另一个简单的测试,我们在之前用任意数字缩放基数,pow()然后除以pow()调用之后的指数。这也将 libc 2.12 的运行时间从 18 秒降至 0 秒。

然而,把它放在我们做的所有代码中是不切实际的a**b。如何替换pow()libc 中的函数?例如,我希望call powFortran 编译器生成的装配线调用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文件可以pow从 libc 重命名,如果其他事情仍然需要 libc 会发生什么?这会导致libcpowcustomPow.o的命名冲突吗?pow

4

4 回答 4

24

嗯,现在等一下。图书馆打电话__slowpow()来不仅仅是为了玩弄你。它之所以调用__slowpow(),是因为它认为需要额外的精度才能为您提供的值提供准确的结果(在这种情况下,基数非常接近 1,1 阶的指数)。如果您关心此计算的准确性,您应该在尝试解决它之前了解为什么会这样以及它是否重要。可能的情况是,对于(比如说)大的负 F0,整个事情可以安全地四舍五入为 1;或者它可能不会,这取决于稍后对这个值做了什么。如果您需要 1.d0 减去此结果,您将需要额外的精度。

于 2012-02-14T17:12:42.450 回答
9

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

于 2012-02-14T05:38:33.927 回答
4

pow(a,b)与 相同exp(b*ln(a)),也许该替换对您有用。

于 2012-02-14T05:31:56.293 回答
1

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

于 2012-02-14T10:00:46.167 回答