C++ 编译器是否自动优化对数学函数的重复调用?

C++ 编译器是否自动优化对数学函数的重复调用?,c++,optimization,compiler-optimization,C++,Optimization,Compiler Optimization,假设我有一段代码: #include <cmath> // ... float f = rand(); std::cout << sin(f) << " " << sin(f); 这是一个优化,期望一个现代的C++编译器自己做是合理的吗?或者编译器没有办法确定sin(f)对于f的相等值总是返回相同的值吗?我很确定GCC标记sin带有非标准的纯属性,即\uuuuuuuuuu属性((纯)) 这具有以下效果: 许多函数除了返回值之外没有任何效果,它

假设我有一段代码:

#include <cmath>

// ...

float f = rand();
std::cout << sin(f) << " " << sin(f);

这是一个优化,期望一个现代的C++编译器自己做是合理的吗?或者编译器没有办法确定

sin(f)
对于
f
的相等值总是返回相同的值吗?

我很确定GCC标记
sin
带有非标准的纯属性,即
\uuuuuuuuuu属性((纯))

这具有以下效果:

许多函数除了返回值之外没有任何效果,它们的返回值仅取决于参数和/或全局变量。像算术运算符一样,这样的函数可以进行公共子表达式消除和循环优化

因此,这种纯调用很有可能通过消除公共子表达式来优化


(更新:实际上cmath正在使用constexpr,这意味着相同的优化)

使用带有默认优化标志的g++构建:

float f = rand();
40117e: e8 75 01 00 00          call   4012f8 <_rand>
401183: 89 44 24 1c             mov    %eax,0x1c(%esp)
401187: db 44 24 1c             fildl  0x1c(%esp)
40118b: d9 5c 24 2c             fstps  0x2c(%esp)
std::cout << sin(f) << " " << sin(f);
40118f: d9 44 24 2c             flds   0x2c(%esp)
401193: dd 1c 24                fstpl  (%esp)
401196: e8 65 01 00 00          call   401300 <_sin>  <----- 1st call
40119b: dd 5c 24 10             fstpl  0x10(%esp)
40119f: d9 44 24 2c             flds   0x2c(%esp)
4011a3: dd 1c 24                fstpl  (%esp)
4011a6: e8 55 01 00 00          call   401300 <_sin>  <----- 2nd call
4011ab: dd 5c 24 04             fstpl  0x4(%esp)
4011af: c7 04 24 e8 60 40 00    movl   $0x4060e8,(%esp)

从这一点我们可以看出,没有优化,编译器使用2个调用,只有1个进行优化,根据经验,我猜,我们可以说编译器确实优化了调用。

除非在同一个编译单元中定义了
sin
,否则编译器不知道如何实现
sin
,所以,这充其量只能在链接时发生。@Thomas不一定。一些编译器专门处理特定名称的函数,因为它们知道这些函数是在标准库中定义的,并且标准对它们提供了一些保证。还有一些特定于编译器的函数属性,用户定义的头可以用来声明它们自己是纯的,尽管我不知道这些知识是否用于优化。优化确实是用gcc完成的。如果我是一名编译器编写者,我当然会。部分问题是知道
std::ostream::operator只是为了增加这一点,MSC知道一些这样的函数,并可以生成相应的内联代码。
constexpr
并不意味着纯。可以实现一个一致的
constexpr
,它会随机为某些输入抛出异常。如果只包含没有属性的内容,gcc也会进行优化,它会在内部了解这些内容,并将sin标记为“const”,除非你通过了仅为“pure”的复杂数学运算。值得注意的是uu attribute_uuu((pure))是GCC扩展,不是C或C++标准的一部分。其他编译器可能也有类似的扩展,但在没有检查的情况下不要指望它。
float f = rand();
40117e: e8 75 01 00 00          call   4012f8 <_rand>
401183: 89 44 24 1c             mov    %eax,0x1c(%esp)
401187: db 44 24 1c             fildl  0x1c(%esp)
40118b: d9 5c 24 2c             fstps  0x2c(%esp)
std::cout << sin(f) << " " << sin(f);
40118f: d9 44 24 2c             flds   0x2c(%esp)
401193: dd 1c 24                fstpl  (%esp)
401196: e8 65 01 00 00          call   401300 <_sin>  <----- 1st call
40119b: dd 5c 24 10             fstpl  0x10(%esp)
40119f: d9 44 24 2c             flds   0x2c(%esp)
4011a3: dd 1c 24                fstpl  (%esp)
4011a6: e8 55 01 00 00          call   401300 <_sin>  <----- 2nd call
4011ab: dd 5c 24 04             fstpl  0x4(%esp)
4011af: c7 04 24 e8 60 40 00    movl   $0x4060e8,(%esp)
float f = rand();
4011af: e8 24 01 00 00          call   4012d8 <_rand>
4011b4: 89 44 24 1c             mov    %eax,0x1c(%esp)
4011b8: db 44 24 1c             fildl  0x1c(%esp)
std::cout << sin(f) << " " << sin(f);
4011bc: dd 1c 24                fstpl  (%esp)
4011bf: e8 1c 01 00 00          call   4012e0 <_sin>  <----- 1 call