代码优化什么时候发生?
昨天,我去面试了。他们问我代码优化什么时候发生? 说,
int abc;//Global variable
abc = 3;
if(abc == 3)
{
printf("abc will be always 3");
}
else
{
printf("This will never executed");
}
现在的问题是优化何时发生? A...在运行时 B...在编译时。 我在编译时回答...对我来说,我认为编译器在编译时检查 volatile 关键字。如果变量未声明为 易失性,那么它会优化代码。但是,当编译器知道这一点时,这个变量永远不会是 3 以外的值? 如果是在运行时,那么编译器什么时候知道变量永远不会是 3 以外的值?因为如果这部分代码执行后变量要改变。请解答我的疑惑
Yesterday, I had an interview. There they asked me when the code optimization happens?
Say,
int abc;//Global variable
abc = 3;
if(abc == 3)
{
printf("abc will be always 3");
}
else
{
printf("This will never executed");
}
Now the question is when the optimization happens?
A... At Run time
B... At compile time.
I answered at compile time... To me I thought, compiler checks for volatile keyword at compile time. If the variable is not declared as volatile then it optimizes the code. But, when the compiler comes to know that, this variable is never ever going to be other than 3?
If it is at run-time, then when compiler comes to know that variable is never ever going to be other than 3? Because if the variable is going to be changed after this part of the code executed. Please clear my doubt
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
C 代码通常使用静态(又称提前)编译进行编译。代码一旦离开编译器就被固定下来;它不能在运行时改变。
这与使用即时编译的语言形成对比,例如爪哇。在那里,优化几乎可以在程序运行时随时发生。
C code is usually compiled using static (aka ahead-of-time) compilation. The code is set in stone once it leaves the compiler; it cannot change at runtime.
This is in contrast to languages that use just-in-time compilation, such as Java. There, optimizations can happen pretty much at any time while then program is running.
大多数代码优化不能在运行时发生,至少不是你的意思。代码在执行过程中无法更改以反映一组新的或不同的变量,这只会造成绝对的混乱。
在运行时可以做的是通过代码选择最佳路径,但这主要必须手动完成,以便创建单独的路径、早期退出、分支等以允许优化。
像这样的代码,正如所写的,允许编译时优化,因为编译器可以检查 abc 的任何可能的替代值,如果没有找到,则优化调用。然而,搜索的范围极大地影响了这种情况是否会发生,而编译器设置也会影响这一点。
如果编译器只是天真地优化单个目标文件,并且您的第二行位于打印部分的另一个文件中,那么它可能无法保证
abc
不会更改,因此不会完全能够优化这一点。无论变量的使用如何,这都取决于编译器设置的激进程度以及是否允许它们丢弃死分支,或者是否会考虑这样做。优化大小可能比速度更有可能删除分支,但中/高设置可能会以任何方式实现(如果可能)。大多数现代编译器都有一个整体程序优化选项,它将大部分优化延迟到链接器阶段。这允许它搜索整个程序,可能会发现
abc
从未在其他任何地方更改或使用,并从条件中删除变量和失败分支。整个程序优化比针对每个对象单独优化要有效得多,因为它可以允许更准确的搜索。在编译器无法修剪死代码的情况下,您可以提示它(最近的构造,例如
constexpr
可以帮助实现这一点)或自己添加优化。它可以很简单,只需将最有可能的路径放在前面,并在 else 之前包含一个 return,从而避免 CPU 进行跳转。这种微优化不太可能是必要的,尤其是在像这样的简单示例中,其中if
本身就已经进行了大量优化。Most code optimization can't happen at runtime, at least not how you mean. The code can't change during execution to reflect a new or different set of variables, that would just make an absolute mess.
What can be done at runtime is choosing the best path through the code, but that has to be done mostly manually in order to create the separate paths, early outs, branches and so on to allow optimization.
Code like this is, as written, allows compile-time optimization because the compiler can check for any possible alternate values of
abc
and, if none are found, optimize out the call. The scope of the search greatly influences whether that can happen, however, and the compiler settings influence that.If the compiler is simply naively optimizing single object files and your second line is in another file from the print section, then it may not be able to guarantee
abc
doesn't change, and so won't be able to optimize this at all. Regardless of variable use, it depends on how aggressive your compiler settings are and whether they are allowed to discard dead branches, or will consider doing so. Optimizing for size may be more likely to remove the branch than for speed, but medium/high settings will likely do it either way (if possible).Most modern compilers have a whole-program-optimization option, which delays much of the optimization until the linker stage. This allows it to search the whole program, potentially discover that
abc
is never changed or used anywhere else, and remove the variable and failing branch from the condition. Whole program optimization can be far more effective than separate optimization for each object, because it can allow more accurate searching.In the case where it's not possible for the compiler to trim dead code, you can either hint it (recent constructs such as
constexpr
can help with that) or add optimizations yourself. It could be as simple as putting the most-likely path first and including a return before the else, saving the CPU from doing a jump. That sort of micro-optimization is unlikely to be necessary, certainly not in a simple example like this, where theif
is plenty optimization by itself.不回答问题,但给出编译时优化的示例。当要求时,gcc 会优化代码。 -O(优化)选项可以实现不同级别的优化。它可以用作-O1、-O2 和-O3。 gcc 手册页准确地描述了每个级别的含义。
-S 选项将 C 转换为汇编并保存在 .s 文件中。
test.c
在没有 gcc 优化的情况下,两个字符串出现在汇编代码中。
$ gcc -S test.c;cat test.s
在没有 gcc 1 级优化的情况下,只有一个字符串被翻译成汇编< /strong>
$ gcc -O1 -S test.c;cat test.s
Not replying the question but giving an example of compile time optimization. gcc optimizes the code when asked to do so. The -O (Optimize) option enables optimization in different leves. It can be used as -O1, -O2 and -O3. The gcc man page describes precisely the meaning of each level.
The -S option translates C into assembly and saves on .s file.
test.c
Whitout gcc optimization the two strings appear on the assembly code.
$ gcc -S test.c;cat test.s
Whit gcc level 1 optimization only one string is translated into assembly
$ gcc -O1 -S test.c;cat test.s
在编译时:)
(原则上这取决于编译器等)
At compile-time :)
(Well, in principle it depends on the compiler, etc.)
我不太熟悉编译器如何优化代码,但是我知道从您那里的代码中,编译器可以推断出
abc
永远不会被更改。这意味着它可以完全删除if
语句,只调用第一个printf
函数。然后在运行时,就没有太多优化了,因为代码非常简单。如果
abc
要改变,那么显然if
不能被省略,但是,在运行时,CPU上的分支预测之类的东西会尝试预测正在采取的代码路径。这也可以被认为是一种优化形式。注意:我没有声称这就是发生的情况,但我可以看到编译器可能会在积极的优化设置中以这种方式进行优化。
I'm not overly familiar with how a compiler optimizes code, however I know that from the code you have there, the compiler can deduce that
abc
is never being changed. This means it could take out theif
statement entirely and just call the firstprintf
function.Then at runtime, there isn't much optimization left, as the code is pretty straightforward. If
abc
were to change, then obviously theif
couldn't be elided, however, then at runtime, there is things like branch prediction on the CPU that will try predict the code path being taken. Which could also be considered to be a form of optimization.Note: I make no claims that this is what happens, but I could see that a compiler might optimize that way, in an aggressive optimization setting.
我想知道他们是在寻找一个答案还是几个可能的答案。
通常,编译器在运行时不会执行任何操作。编译器二进制文件及其组件不需要出现在运行时环境中(对于 C 语言)。
消除这种死分支的优化类型将使用某些技术来编写优化编译器。请参阅 Muchnik 的有关编译器的书。使用的技术可能涉及创建基本块的有向图,然后使用以下之一:
随着
然后
然后
另一方面,某些编译器可能无法在优化期间进行足够的计算来删除它。
在这种情况下,如果您在具有分支预测的芯片上运行代码,芯片将“学习”预测第一个分支,并有利地缓存(获取)该分支。但这不是编译器机制,通常不称为优化。
I wonder if they were looking for one answer or a few possible answers.
The compiler does not do anything at run time, usually. The compiler binary and its components are not required to be present (for the language C) at the run-time environment.
The type of optimization which would eliminate this dead branch would use certain techniques for writing optimizing compilers. See Muchnik's book on compilers. The techniques used might involve creating a directed graph of basic blocks, then using one of:
along with
and then
then
On the other hand, some compiler might not calculate enough to remove this during optimization.
In that case, if you ran the code on a chip with branch prediction, the chip would "learn" that the first branch is predicted, and cache (fetch) that branch favorably. But this is not a compiler mechanism, not usually called optimization.
最简单的答案:
代码优化发生在撰写本文时。
简单回答:
也许,考虑到范围,它取决于编译器。
硬答案:
考虑到全局范围,编译器应该保留它,并假设它可以在文件的其他地方访问。多次通过可以优化它。如果编译器不认为它对文件是静态的(想想具有平面内存模型的系统),那么全局就是真正的全局,并且假设应该是任何可能改变该值的东西。这就是为什么要避免全局变量,除非意图确实是全局访问。
根据处理器的不同,分支预测参数将适用,但主要是编译时间或根本不适用。
ps:我真的很不喜欢这样的面试问题。
Simplest answer:
Code optimization occurs at the time of writing.
Simple answer:
maybe, it's compiler dependent given the scope.
Hard answer:
Given the global scope, the compiler should leave that alone with the assumption that it could be accessed elsewhere in the file. Multiple passes could optimize it out. If it is not considered static to the file by the compiler (think systems with flat memory models), then global is truly global and the assumption should be anything could change the value. This is why you avoid globals unless the intent really is global access.
Depending on the processor, the branch prediction argument would apply, but mostly it's compile time or not at all.
ps: I really dislike interview questions like this.