为什么 `printf("%s", "foo")` 没有被优化为 `fputs("foo", stdout)`?
Why is `printf("%s", "foo")` not being optimized to `fputs("foo", stdout)`?
所以 GCC 和 Clang 都足够聪明,可以将 printf("%s\n", "foo")
优化为 puts("foo")
(GCC, Clang)。太好了。
但是当我运行这个函数通过Compiler Explorer:
#include <stdio.h>
void foo(void) {
printf("%s", "foo");
}
GCC 和 Clang 都没有将 printf("%s", "foo")
优化为 fputs("foo", stdout)
,我认为这应该是相同的(因为 fputs
不会像 puts
那样放置换行符) printf
更快。
x86-64 GCC 11.1 (link):
.LC0:
.string "foo"
.LC1:
.string "%s"
foo:
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:.LC1
xor eax, eax
jmp printf
x86-64 Clang 12.0.0 (link):
foo: # @foo
mov edi, offset .L.str
mov esi, offset .L.str.1
xor eax, eax
jmp printf # TAILCALL
.L.str:
.asciz "%s"
.L.str.1:
.asciz "foo"
有什么具体原因没有对 fputs
进行优化,还是编译器不够智能?
一些非常具体的情况得到了优化,就像你展示的那样,但它非常肤浅,如果你在你的格式字符串中添加一些东西,即使是 space,它 immediately discards the puts
and goes back to printf
.
我想没有什么可以阻止更广泛的优化,我的推测是,由于性能提升不是那么大,进一步添加更多 special cases 被认为因为不值得。
根据我的推测,缺少 fputs
优化属于 不值得 类别。
这个旧的 gcc printf
optimization document 阐明了这些优化,我怀疑今天它会大不相同。
具体来说:
2.3 %s\n
A printf call with the format string %s\n
[line 4679-4687] is converted to a puts()
call.
printf("%s\n", "hello world"); // converted to puts("hello world");
2.8 The string not ending with \n
No optimization if the string is not ending with \n
[line 4721-4739].
2.9 The string ending with \n
A printf
call with a simple format string ending with \n
[line 4721-4739] is converted to a puts()
call.
printf("hello world\n"); // converted to puts("hello world");
所以 GCC 和 Clang 都足够聪明,可以将 printf("%s\n", "foo")
优化为 puts("foo")
(GCC, Clang)。太好了。
但是当我运行这个函数通过Compiler Explorer:
#include <stdio.h>
void foo(void) {
printf("%s", "foo");
}
GCC 和 Clang 都没有将 printf("%s", "foo")
优化为 fputs("foo", stdout)
,我认为这应该是相同的(因为 fputs
不会像 puts
那样放置换行符) printf
更快。
x86-64 GCC 11.1 (link):
.LC0:
.string "foo"
.LC1:
.string "%s"
foo:
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:.LC1
xor eax, eax
jmp printf
x86-64 Clang 12.0.0 (link):
foo: # @foo
mov edi, offset .L.str
mov esi, offset .L.str.1
xor eax, eax
jmp printf # TAILCALL
.L.str:
.asciz "%s"
.L.str.1:
.asciz "foo"
有什么具体原因没有对 fputs
进行优化,还是编译器不够智能?
一些非常具体的情况得到了优化,就像你展示的那样,但它非常肤浅,如果你在你的格式字符串中添加一些东西,即使是 space,它 immediately discards the puts
and goes back to printf
.
我想没有什么可以阻止更广泛的优化,我的推测是,由于性能提升不是那么大,进一步添加更多 special cases 被认为因为不值得。
根据我的推测,缺少 fputs
优化属于 不值得 类别。
这个旧的 gcc printf
optimization document 阐明了这些优化,我怀疑今天它会大不相同。
具体来说:
2.3
%s\n
A printf call with the format string
%s\n
[line 4679-4687] is converted to aputs()
call.printf("%s\n", "hello world"); // converted to puts("hello world");
2.8 The string not ending with
\n
No optimization if the string is not ending with
\n
[line 4721-4739].
2.9 The string ending with
\n
A
printf
call with a simple format string ending with\n
[line 4721-4739] is converted to aputs()
call.printf("hello world\n"); // converted to puts("hello world");