Ell*_*sky 72 c performance temporary
假设我有以下C代码:
int i = 5;
int j = 10;
int result = i + j;
Run Code Online (Sandbox Code Playgroud)
如果我多次循环,使用它会更快int result = 5 + 10吗?我经常创建临时变量以使我的代码更具可读性,例如,如果使用某个长表达式从某个数组中获取这两个变量来计算索引.这在C中是否表现不佳?其他语言怎么样?
Sha*_*our 83
现代优化编译器应该优化这些变量,例如,如果我们在godbolt中gcc使用以下示例并使用-std=c99 -O3标志(请参见实时):
#include <stdio.h>
void func()
{
int i = 5;
int j = 10;
int result = i + j;
printf( "%d\n", result ) ;
}
Run Code Online (Sandbox Code Playgroud)
它将导致以下程序集:
movl $15, %esi
Run Code Online (Sandbox Code Playgroud)
为了计算i + j,这是不断传播的形式.
注意,我添加了printf这样我们有副作用,否则func会被优化掉:
func:
rep ret
Run Code Online (Sandbox Code Playgroud)
这些优化在as-if规则下是允许的,只需要编译器模拟程序的可观察行为.这在C99标准部分的5.1.2.3 程序执行草案中有所说明:
在抽象机器中,所有表达式都按语义指定进行计算.实际实现不需要评估表达式的一部分,如果它可以推断出它的值未被使用并且不产生所需的副作用(包括由调用函数或访问易失性对象引起的任何副作用).
另请参阅:优化C++代码:常量折叠
ste*_*ith 12
您给出的示例很容易让编译器进行优化.使用局部变量来缓存从全局结构和数组中提取的值实际上可以加快代码的执行速度.例如,如果您从for循环内的复杂结构中获取某些内容,而编译器无法进行优化,并且您知道该值没有更改,则局部变量可以节省相当多的时间.
您也可以使用GCC(其他编译器)生成中间汇编代码,并查看编译器实际执行的操作.
有关如何打开汇编列表的讨论:使用GCC生成可读组件?
检查生成的代码并查看编译器实际执行的操作可能很有帮助.
R..*_*R.. 10
虽然代码中的各种微不足道的差异会以轻微改善或恶化性能的方式扰乱编译器的行为,但原则上它是否应该使用这样的临时变量,只要程序的含义不是这样,就不会产生任何性能差异.改变.一个好的编译器应该以任何一种方式生成相同或相当的代码,除非您有意建立优化关闭以获得尽可能接近源的机器代码(例如用于调试目的).
当我试图学习编译器的功能时,你遇到了同样的问题 - 你做了一个简单的程序来演示问题,并检查编译器的汇编输出,只是意识到编译器已经优化了一切你试图让它消失.你可能会发现main()中的一个相当复杂的操作基本上被简化为:
push "%i"
push 42
call printf
ret
Run Code Online (Sandbox Code Playgroud)
你原来的问题不是"会发生什么int i = 5; int j = 10...?" 但"临时变量通常会导致运行时间的惩罚吗?"
答案可能不是.但是你必须查看特定的非平凡代码的汇编输出.如果你的CPU有很多寄存器,比如ARM,那么i和j很可能在寄存器中,就像那些寄存器直接存储函数的返回值一样.例如:
int i = func1();
int j = func2();
int result = i + j;
Run Code Online (Sandbox Code Playgroud)
几乎可以肯定是完全相同的机器代码:
int result = func1() + func2();
Run Code Online (Sandbox Code Playgroud)
我建议你使用临时变量,如果它们使代码更容易理解和维护,如果你真的想收紧一个循环,你将会研究装配输出,以便弄清楚如何提高性能.可能.但是,如果没有必要,不要牺牲几秒的可读性和可维护性.