【问题标题】:Why the compiler does this division为什么编译器会做这个除法
【发布时间】:2019-12-18 12:55:02
【问题描述】:

我正在尝试优化 nbody 算法,这个操作很昂贵

real s = jMass / POW(distSqr,3.0/2.0);

所以我尝试转换:

s = jMass * POW(distSqr, -3.0/2.0);

但是带有 -Ofast 选项的编译器 gcc 还是会进行除法,为什么?

【问题讨论】:

  • 您是否检查了汇编程序的输出,或者这只是您的假设?我认为POW 函数比除法更昂贵。
  • 我用 perf 记录检查过,在汇编代码中除法占 50%,其他是乘法
  • 只是一个问题:你说这个部门正在吃掉你的表现。与其尝试优化部门本身的性能,您可能会问自己一个问题,为什么这种部门会发生这么多次。您是否有可能有一个例程重复执行此除法,而它可以只执行一次并保留结果,而不是一遍又一遍地执行?
  • 这看起来不像普通的 C 代码。 real 是什么?和POW?和distSqr?没有这样的上下文,问题是没有意义的。给我们minimal reproducible example,以便我们自己确认!
  • Here 编译器优化为平方根、乘法和除法。可能是编译器设计者认为平方根和除法比pow 快。如果您得到不同的代码,请显示 minimal reproducible example 以及编译器版本和用于编译的开关。

标签: c gcc


【解决方案1】:

我必须在这里做几个假设(变量都是double 类型,而POW 是扩展为pow 的宏):

#include <math.h>

double f0(double a, double b) {
    return a / pow(b, 1.5);
}

double f1(double a, double b) {
    return a * pow(b, -1.5);
}

这给了我(在 x86 上):

f0:
    .cfi_startproc
    movapd  %xmm1, %xmm2
    sqrtsd  %xmm2, %xmm2
    mulsd   %xmm1, %xmm2
    divsd   %xmm2, %xmm0
    ret
    .cfi_endproc
f1:
    .cfi_startproc
    movapd  %xmm1, %xmm2
    sqrtsd  %xmm2, %xmm2
    mulsd   %xmm1, %xmm2
    divsd   %xmm2, %xmm0
    ret
    .cfi_endproc

pow(x, 1.5) 转换为x * √x 是一个合理的优化,并且随后的除法仍然比调用昂贵的pow() 函数快得多。换句话说,当使用-Ofast 时,编译器相当合理地选择将pow(x, -1.5) 表示为1/(x*√x)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-02
    • 1970-01-01
    • 1970-01-01
    • 2013-05-16
    • 2013-03-26
    • 1970-01-01
    • 2021-08-15
    • 2020-04-26
    相关资源
    最近更新 更多