使用额外的变量使计算更容易理解——(多少)我减慢了我的代码?
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
我汇编不是很流利,但显然两者是相同的。结论:如果您确实关心效率,那么仍然编写代码以提高可读性。编译器很可能比你更了解如何优化代码。
主要问题
假设我正在编写一个函数来计算两个水滴碰撞时生成的球体的半径:
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
我汇编不是很流利,但显然两者是相同的。结论:如果您确实关心效率,那么仍然编写代码以提高可读性。编译器很可能比你更了解如何优化代码。