【问题标题】:Replacing extrordinarily slow pow() function替换非常慢的 pow() 函数
【发布时间】:2012-02-14 05:27:25
【问题描述】:

我们有一个 CFD 求解器,在运行模拟时,发现它在某些机器上运行异常缓慢,但在其他机器上却没有。使用 Intel 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 会发生什么?这会导致customPow.o 中的pow 和libc 中的pow 之间的命名冲突吗?

【问题讨论】:

  • 很好的 Fortran!有趣的问题虽然 +1

标签: c fortran libc pow


【解决方案1】:

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

【讨论】:

  • 确实如此。但是,至少在我们的例子中,我们的代码是多维的,所以我们只有在计算东西以进行可视化或某种后处理时才有一个基数,所以在正确答案的 1e-15 范围内并不是非常重要.我进行了比较,看看我们损失了多少,误差约为 1e-13,对于我们的二阶准确代码而言,无论如何它都小于我们的离散化误差,因此我们可以安全地将所有 pow() 替换为稍微不太准确的代码.
【解决方案2】:

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

【讨论】:

  • 这是否允许自定义 pow 函数在 libc 中调用 pow?这个自定义的pow 只会在需要时缩放基数,然后调用 libc pow,然后在需要时取消缩放。似乎会有一些命名冲突。
  • 如果您使用动态链接,您可以使用dlsym hack 来实现所需的行为,但它很脆弱。如果您只需要它在带有 GNU 链接器的系统上工作,更好的方法是 ld--wrap 选项(gcc 可以通过 -Wl,--wrap,pow 传递给 ld)。然后将__wrap_pow 放入libmypow.a,并在需要使用libc pow 的地方调用__real_pow,一切都会好起来的。
【解决方案3】:

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

【讨论】:

  • 这可能会规避调用的缓慢性,但我们正在寻找一种方法来基本上替换 Fortran 中 ** 运算符生成的函数调用,而无需更改我们拥有的实际代码库,如果可能的话。
  • 所以链接你自己的使用这个身份的 pow() 版本。
  • 这对 1.0000000000000020^1.5 给出了不同的结果:1.0000000000000031 与 call pow,1.0000000000000029 与 -ffast-math 和 1.500000000040029 与 -ffast-math 和 1.5000000000400261
【解决方案4】:

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

【讨论】:

    猜你喜欢
    • 2013-01-19
    • 2020-07-16
    • 2016-01-16
    • 1970-01-01
    • 2011-12-09
    • 2019-02-12
    • 1970-01-01
    • 1970-01-01
    • 2015-10-15
    相关资源
    最近更新 更多