这种优化依赖于编译器知道名为
printf
的函数只能是C标准定义的
printf
函数。如果程序将
printf
定义为其他含义,则程序会引发未定义行为。这使得编译器可以在适用“as if”标准
printf
函数的情况下替换为调用
puts
。它不必担心它是否适用于用户定义的
printf
函数。因此,这些类型的函数替换优化基本上仅限于C或C ++标准中定义的函数。(如果编译器以某种方式知道给定的标准正在生效,则可能还有其他标准。)
除了自己修改编译器源代码外,没有办法告诉编译器这些函数替换可以使用自己的函数。但是,有限制条件下,您可以使用内联函数类似地实现类似于
printf
/
puts
优化的功能。例如,您可以使用以下内容实现类似于:
printf
/
puts
优化的功能:
inline int myprintf(char const *fmt, char const *arg) {
if (strcmp(fmt, "%s\n") == 0) {
return myputs(args);
}
return _myprintf_impl(fmt, arg)
}
当启用优化后,编译器可以根据fmt
参数在编译时选择调用哪个函数,但前提是它能够确定它是一个常量字符串。如果不能确定,或者未启用优化,则编译器必须在每次调用时检查它,这可能会导致性能下降。请注意,此优化取决于编译器知道strcmp
的工作方式并完全删除调用,因此是编译器可以进行的另一种库函数调用替换的示例。
您可以使用GCC的__builtin_constant_p
函数来改进此问题:
inline int myprintf(char const *fmt, char const *arg) {
if (__builtin_constant_p(fmt[0])
&& strcmp(fmt, "%s\n") == 0) {
return myputs(arg);
}
return _myprintf_impl(fmt, arg);
}
在GCC下,这会导致在运行时永远不会检查格式字符串。如果可以在编译时确定
fmt
是
"%s\n"
,则生成调用
myputs
的代码,否则生成无条件调用
_myprintf_impl
的代码。因此,在启用优化的情况下,此函数永远不是一个性能劣化。不幸的是,尽管clang支持
__builtin_constant_p
函数,但我的版本的clang总是会生成无条件调用
_myprintf_impl
的代码。
memset
是另一个例子,编译器通常通过识别它是否适用于固定小尺寸并将其内联处理来进行特殊处理。 - ShadowRangerdouble x = sqrt(2.0)
的代码可能会被优化为double x = 1.4142135.....
。这取决于编译器和设置。 - chux - Reinstate Monicaclang
是否提供了库作者可以使用的机制来扩展优化,还是必须通过修改编译器本身来完成。 - Barmarmemset
、memcpy
、strlen
等)仍将内联其内置实现,或进行常量传播。例如,int foo(){return strlen("hello world!"); }
编译为return 12
,即使没有<string.h>
。 - Peter Cordes