为什么GCC不会优化printf的调用? [英] Why doesn't GCC optimize this call to printf?
问题描述
#include< stdio.h>
int main(void){
int i;
scanf(%d,& i);
if(i!= 30){return(0); }
printf(我等于%d \\\
,i);
}
看起来结果字符串总是我等于30 ,那么,为什么GCC不会调用printf来调用 puts()
或 write()
,例如?
(刚检查过生成的程序集,使用 gcc -O3
(版本5.3.1) ,或在 Godbolt编译器资源管理器)
首先,问题不在于如果
;正如你所看到的, gcc
如果通过,如果
并设法传递 30
直接到 printf
。
现在, gcc
确实有一些逻辑来处理 printf
的特殊情况(特别是,它优化了 printf(something \\\
甚至
) printf(%s \ n,something) to
保持原样。更糟糕的是,没有尾随的换行符的上述任何变体都不会被触及,即使它们可以转换为 puts(something)
),但它非常具体,不会进一步发展;例如, printf(Hello%s \ n,world) fputs(something,stdout)
我想这可归结为两个主要问题:
printf
的性能很关键,他不应该依赖于此
如果你问我,即使只是 puts $ c $> c>上面的优化已经为了风格点,你不会真的在任何人工测试用例上获得认真的性能。 当你开始超越
%s \ n
, printf
的领域是雷区,因为它具有强烈的依赖性在运行时环境中;特别是,许多的printf
说明符(不幸的是)的影响由区域设置,再加上有特定于实现的缺点和说明符(和 GCC <的卷扬机/ code>可以用
的printf
从glibc的工作,MUSL,MinGW的/ MSVCRT,... - 在编译时你不能调用目标C运行时 - 认为当你是交叉编译的)。
我同意这个简单的%d
情况可能是安全的,但我可以看看为什么他们可能决定避免过于聪明,只在这里执行最蠢和最安全的优化。 b
对于好奇的读者,这里 a>是实际实现该优化的地方;你可以看到,该功能非常简单的情况下(和GIMPLE有限数量的匹配之外,因为并没有改变很多的这篇很好的文章概述了他们写的)。顺便说一句,源代码实际解释了为什么他们不能为非新行情况实现 fputs
变体(没有简单的方法来引用 stdout
global在编译阶段)。
#include <stdio.h>
int main(void) {
int i;
scanf("%d", &i);
if(i != 30) { return(0); }
printf("i is equal to %d\n", i);
}
It appears that the resulting string will always be "i is equal to 30", so, why doesn't GCC optimize this call to printf with a call to puts()
, or write()
, for example?
(Just checked the generated assembly, with gcc -O3
(version 5.3.1), or on the Godbolt Compiler Explorer)
First of all, the problem is not the if
; as you saw, gcc
sees through the if
and manages to pass 30
straight to printf
.
Now, gcc
does have some logic to handle special cases of printf
(in particular, it does optimize printf("something\n")
and even printf("%s\n", "something")
to puts("something")
), but it is extremely specific and doesn't go much further; printf("Hello %s\n", "world")
, for example, is left as-is. Even worse, any of the variants above without a trailing newline are left untouched, even if they could be transformed to fputs("something", stdout)
.
I imagine that this comes down to two main problems:
the two cases above are extremely easy patterns to implement and happen quite frequently, but for the rest probably it's rarely worth the effort; if the string is constant and the performance is important, the programmer can take care of it easily - actually, if the performance of
printf
is critical he shouldn't be relying on this kind of optimization, which may break at the slightest change of format string.If you ask me, even just the
puts
optimizations above are already "going for the style points", you are not really going to gain serious performance in anything but artificial test cases.When you start to go outside the realm of
%s\n
,printf
is a minefield, because it has a strong dependency on the runtime environment; in particular, manyprintf
specifiers are (unfortunately) affected by the locale, plus there are a hoist of implementation-specific quirks and specifiers (andgcc
can work withprintf
from glibc, musl, mingw/msvcrt, ... - and at compile time you cannot invoke the target C runtime - think when you are cross-compiling).I agree that this simple
%d
case is probably safe, but I can see why they probably decided to avoid being overly smart and only perform the dumbest and safest optimizations here.
For the curious reader, here is where this optimization is actually implemented; as you can see, the function matches a restricted number of very simple cases (and GIMPLE aside, hasn't changed a lot since this nice article outlining them was written). Incidentally, the source actually explains why they couldn't implement the fputs
variant for the non-newline case (there's no easy way to reference the stdout
global at that compilation stage).
这篇关于为什么GCC不会优化printf的调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!