在一行中进行多步计算是否更好?
Is it better to do multi-step calculations in one line?
这个问题专门针对嵌入式系统的 C 编译器,但我相信它可以应用于一般编程。
在一行中编写多步数学表达式而不是将它们分解为等效的单个操作有什么好处,反之亦然?
例如是:
int convert(int in){
return (int)(((long)in * 727) / 12) + 9;
}
好于或差于:
int convert(int in){
long out = (long)in;
out *= 727;
out /= 12;
out += 9;
return (int)out;
}
一般来说?
我的直觉是单行表达式可能更容易让编译器优化,但我不编写编译器所以我不确定。
对于编译器来说,这两种方式没有区别。
不过,对于 reader 试图理解或调试代码的开发人员来说,具有重要中间值的公式很有帮助。
如今编译器的性能如此之好,以至于费心进行这种微优化几乎毫无意义。优化的应该是算法,而不是代码编排。做其他人更容易阅读和理解的事情。
用 GCC(第一级优化)编译这两个代码会得到相同的汇编输出,因此它们是严格等价的:
https://godbolt.org/z/91rrc4E69
如果您在启用优化的情况下进行编译,则生成的代码没有区别。我更喜欢循序渐进,因为这样不容易出错,几个月后回到项目时更容易理解。
int convert0(int in){
return (int)(((long)in * 727) / 12) + 9;
}
int convert1(int in){
long out = (long)in;
out *= 727;
out /= 12;
out += 9;
return (int)out;
}
int convert2(int in){
long out1 = (long)in;
long out2 = out1 * 727;
long out3 = out2 / 12;
long out4 = out3 + 9;
return (int)out4;
}
最好的办法就是自己检查一下。只需查看发出的汇编代码:
Is there any benefit to writing multi-step mathematical expressions in one line rather than breaking them up into equivalent individual operations, or vice-versa?
是的。一是,如果您使用字符或 short
类型编写操作,它们将在整个过程中使用 int
算术执行,从而允许更大的中间结果。相反,如果每个中间结果都分配给一个字符或 short
变量,则每个中间结果都将缩减为目标类型(以 C 标准为无符号类型和有符号类型的实现定义的方式).1 在中间值中有更多的“余量”通常是一个好处,但赋值或强制转换可用于有意减少。
第二个是 C 标准允许比标称类型更精确地执行浮点运算。例如,两个 float
操作数相乘后跟另一个 float
操作数的相加可以像操作数是 double
一样执行,甚至可以像在乘法中有无限精度一样执行。相反,赋值(和强制转换)需要将值转换为标称类型。这可能是一个好处(因为它往往会减少舍入误差)或一个损害(在为精确的 IEEE-754 语义编程时)。
第三个是单个表达式通常使编译器更灵活地组织操作。对多个语句的中断操作在它们之间引入了序列点,从而限制了编译器的优化。与其他表达式一样,使用单个表达式允许编译器优化或使用多个语句引入必要的顺序可能是一个好处。
从评论和其他答案来看,none 这些在您的示例代码的检查中表现出来,但它们确实存在。
请注意,所有这些都会影响语义:好处或坏处在于有效地告诉编译器它是否可以自始至终使用更广泛的类型,是否可以使用额外的精度,以及它是否可以必须对某些事情进行排序。就优化而言,使用一条语句或单独的语句并不是帮助现代编译器优化的有用方法。优秀的现代编译器以相当全面的方式分析程序语义,因此这种简单的表达式重构对它们的帮助不大。
脚注
1 本回答仅讨论 int
比 char
和 short
宽的常见情况。虽然这在现代 C 实现中很典型,但 C 标准并不要求它。
这个问题专门针对嵌入式系统的 C 编译器,但我相信它可以应用于一般编程。
在一行中编写多步数学表达式而不是将它们分解为等效的单个操作有什么好处,反之亦然?
例如是:
int convert(int in){
return (int)(((long)in * 727) / 12) + 9;
}
好于或差于:
int convert(int in){
long out = (long)in;
out *= 727;
out /= 12;
out += 9;
return (int)out;
}
一般来说?
我的直觉是单行表达式可能更容易让编译器优化,但我不编写编译器所以我不确定。
对于编译器来说,这两种方式没有区别。
不过,对于 reader 试图理解或调试代码的开发人员来说,具有重要中间值的公式很有帮助。
如今编译器的性能如此之好,以至于费心进行这种微优化几乎毫无意义。优化的应该是算法,而不是代码编排。做其他人更容易阅读和理解的事情。
用 GCC(第一级优化)编译这两个代码会得到相同的汇编输出,因此它们是严格等价的: https://godbolt.org/z/91rrc4E69
如果您在启用优化的情况下进行编译,则生成的代码没有区别。我更喜欢循序渐进,因为这样不容易出错,几个月后回到项目时更容易理解。
int convert0(int in){
return (int)(((long)in * 727) / 12) + 9;
}
int convert1(int in){
long out = (long)in;
out *= 727;
out /= 12;
out += 9;
return (int)out;
}
int convert2(int in){
long out1 = (long)in;
long out2 = out1 * 727;
long out3 = out2 / 12;
long out4 = out3 + 9;
return (int)out4;
}
最好的办法就是自己检查一下。只需查看发出的汇编代码:
Is there any benefit to writing multi-step mathematical expressions in one line rather than breaking them up into equivalent individual operations, or vice-versa?
是的。一是,如果您使用字符或 short
类型编写操作,它们将在整个过程中使用 int
算术执行,从而允许更大的中间结果。相反,如果每个中间结果都分配给一个字符或 short
变量,则每个中间结果都将缩减为目标类型(以 C 标准为无符号类型和有符号类型的实现定义的方式).1 在中间值中有更多的“余量”通常是一个好处,但赋值或强制转换可用于有意减少。
第二个是 C 标准允许比标称类型更精确地执行浮点运算。例如,两个 float
操作数相乘后跟另一个 float
操作数的相加可以像操作数是 double
一样执行,甚至可以像在乘法中有无限精度一样执行。相反,赋值(和强制转换)需要将值转换为标称类型。这可能是一个好处(因为它往往会减少舍入误差)或一个损害(在为精确的 IEEE-754 语义编程时)。
第三个是单个表达式通常使编译器更灵活地组织操作。对多个语句的中断操作在它们之间引入了序列点,从而限制了编译器的优化。与其他表达式一样,使用单个表达式允许编译器优化或使用多个语句引入必要的顺序可能是一个好处。
从评论和其他答案来看,none 这些在您的示例代码的检查中表现出来,但它们确实存在。
请注意,所有这些都会影响语义:好处或坏处在于有效地告诉编译器它是否可以自始至终使用更广泛的类型,是否可以使用额外的精度,以及它是否可以必须对某些事情进行排序。就优化而言,使用一条语句或单独的语句并不是帮助现代编译器优化的有用方法。优秀的现代编译器以相当全面的方式分析程序语义,因此这种简单的表达式重构对它们的帮助不大。
脚注
1 本回答仅讨论 int
比 char
和 short
宽的常见情况。虽然这在现代 C 实现中很典型,但 C 标准并不要求它。