使用额外的变量使计算更容易理解——(多少)我减慢了我的代码?

Using extra variables to make calculations easier to understand - (how much) am I slowing down my code?

主要问题

假设我正在编写一个函数来计算两个水滴碰撞时生成的球体的半径:

double drop_size_adder(double r1, double r2) {
    return cbrt((4 / 3 * M_PI * r1*r1*r1 + 4 / 3 * M_PI * r2*r2*r2) * 3 / (4 * M_PI));
}

(我意识到很多因素都被排除了,但想象这不是为了这部分问题。)

我可以重写上面的计算,通过引入各种变量将计算分成几步来更容易理解发生了什么:

double drop_size_adder(double r1, double r2) {
    double vol1, vol2, vol3, r3;
    vol1 = 4 / 3 * M_PI * r1*r1*r1;
    vol2 = 4 / 3 * M_PI * r2*r2*r2;
    vol3 = vol1 + vol2;
    r3 = cbrt(3 * vol3 / (4 * M_PI));
    return r3;
}

这会显着增加函数 运行 所需的时间吗?

副题

此外,假设我重写了上面的函数,下面的两个代码块之间是否会有明显的运行时间差异:

double drop_size_adder(double r1, double r2) {
    return cbrt(r1*r1*r1 + r2*r2*r2);
}

int main()
{
    double water_r = 3.2;
    double oil_r = 5.4;
    combined_r = drop_size_adder(water_r, oil_r);
}

int main()
{
    double water_r = 3.2;
    double oil_r = 5.4;
    combined_r = cbrt(water_r*water_r*water_r + oil_r*oil_r*oil_r);
}

在调试版本中,在调试器中,该代码将更容易单步执行并查看发生了什么,是的。

在优化构建中,输出应该与 less 'steppable' 版本[s]相同。

您甚至可以使用 CompilerExplorer 之类的东西将编译后的输出与各种编译器标志进行比较。 -O2(大写 'o',不是零)是一个普遍支持的编译器标志。它还可以让您比较不同编译器的 output/behavior。

没有优化标志,程序集将会改变。有了它,就一样了。这正是您想要的。

这些天编译器优化非常好。优化你的执行速度更多的是 algorithms/containers ("big O") 和内存使用模式 ("cache coherency") 而不是调整程序集。

编写代码以提高可读性。不要自作聪明。

话虽如此,如果您仍然想知道有什么区别,那么您可以查看编译器的输出(例如 here)。这个:

#define M_PI 1

double cbrt(double);

double drop_size_adder(double r1, double r2) {
    return cbrt((4 / 3 * M_PI * r1*r1*r1 + 4 / 3 * M_PI * r2*r2*r2) * 3 / (4 * M_PI));
}

double drop_size_adder2(double r1, double r2) {
    double vol1, vol2, vol3, r3;
    vol1 = 4 / 3 * M_PI * r1*r1*r1;
    vol2 = 4 / 3 * M_PI * r2*r2*r2;
    vol3 = vol1 + vol2;
    r3 = cbrt(3 * vol3 / (4 * M_PI));
    return r3;
}

由 gcc (-O3) 翻译为:

_Z15drop_size_adderdd:
        movapd  xmm2, xmm0
        mulsd   xmm0, xmm0
        mulsd   xmm0, xmm2
        movapd  xmm2, xmm1
        mulsd   xmm2, xmm1
        mulsd   xmm2, xmm1
        addsd   xmm0, xmm2
        mulsd   xmm0, QWORD PTR .LC0[rip]
        mulsd   xmm0, QWORD PTR .LC1[rip]
        jmp     _Z4cbrtd
_Z16drop_size_adder2dd:
        movapd  xmm2, xmm0
        mulsd   xmm0, xmm0
        mulsd   xmm0, xmm2
        movapd  xmm2, xmm1
        mulsd   xmm2, xmm1
        mulsd   xmm2, xmm1
        addsd   xmm0, xmm2
        mulsd   xmm0, QWORD PTR .LC0[rip]
        mulsd   xmm0, QWORD PTR .LC1[rip]
        jmp     _Z4cbrtd
.LC0:
        .long   0
        .long   1074266112
.LC1:
        .long   0
        .long   1070596096

same example @ godbolt

我汇编不是很流利,但显然两者是相同的。结论:如果您确实关心效率,那么仍然编写代码以提高可读性。编译器很可能比你更了解如何优化代码。