C++ 是否/可以优化对函数参数的调用?

Does/can C++ optimize away call to a function argument?

提问人:Petr 提问时间:10/8/2023 最后编辑:Peter CordesPetr 更新时间:10/9/2023 访问量:101

问:

让我们假设这个函数模板:

template <typename F>
void foo(F&& f) {
  f("foo");
}

void to_optimize() {
  foo([](std::string_view s) {
      std::cout << s << std::endl;
    });
}

在这种情况下,编译器是否可以优化内联函数?也就是说,用

void to_optimize() {
  std::cout << "foo" << std::endl;
}

?

C++ 编译器-优化 函数 调用 内联

评论

3赞 Peter Cordes 10/8/2023
请参阅如何从 GCC/clang 汇编输出中消除“噪音”? - 您可以自己查看编译器生成的 ASM,以查看函数内联的工作情况。
4赞 Jesper Juhl 10/8/2023
当然可以。请参阅假设规则。
2赞 Pepijn Kramer 10/8/2023
如果你对编译器可以做什么感兴趣,可以看看编译器资源管理器,你可以在其中看到这些类型的操作(如查看生成的汇编代码)。看看我的编译器最近为我做了什么,这是该网站的作者,这也很有趣。(马特·戈德博尔特)
1赞 Petr 10/8/2023
@PepijnKramer 这真的很有趣,谢谢!
2赞 user17732522 10/8/2023
编译器很可能会内联调用。由于您是通过引用而不是指向函数的指针传递 lambda,因此调用本身已经是对特定函数(lambda 的调用运算符)的直接调用,而不是对可能任何函数的间接调用。然后,编译器将执行其通常的内联分析,如果满足内联条件,则内联调用运算符。它甚至不需要做任何更复杂的操作,比如确定调用的位置。(尽管它可能也会内联自身。f("foo");foofoo

答:

3赞 Sam Varshavchik 10/8/2023 #1

C++ 标准允许任何没有可观察效果的优化。如前所述,优化函数调用不会产生可观察到的效果。C++ 程序无法确定此处是否发生了其他函数调用。

所以,是的,C++ 编译器可以优化函数调用。它是否这样做取决于编译器和编译器配置参数。

评论

1赞 Jesper Juhl 10/8/2023
提及“假设规则”或指向 en.cppreference.com/w/cpp/language/as_if 的链接可能会改善您的答案。
1赞 Petr 10/8/2023 #2

@PepijnKramer的评论为我指出了我可以自己探索的地方(有一个稍微简化的例子) - Clang with : https://godbolt.org/z/YEb8vjYev.事实上,编译器完全优化了函数调用。-O2

enter image description here

#include <iostream>

#ifdef AS_FUNCTOR
template <typename F>
void foo(F&& f) {
  f(42);
}

void to_optimize() {
  foo([](int s) {
      std::cout << s;
    });
}

#else  // AS_FUNCTOR

void to_optimize() {
  std::cout << 42;
}
#endif  // AS_FUNCTOR

评论

2赞 Peter Cordes 10/8/2023
Compiler-explorer 有一个内置的差异窗格。您可以使用 和 vs. 使相同的源代码编译为排列的不同 ASM。#ifdef-Dfoo-Ufoo
2赞 Peter Cordes 10/8/2023
您的 lambda 不使用它的参数,它还使用硬编码的 ,因此编译器在内联后甚至没有任何常量传播。顺便说一句,如果您删除 或至少将其替换为 或 ,asm 会更简单。输出换行符后显式刷新的 asm 只是额外的噪音,并且会使程序运行速度变慢,因此当您不需要刷新流时停止使用,即使它被重定向到文件。42std::endl<< '\n'<< "\n"std::endl
0赞 Paul Sanders 10/8/2023
@PeterCordes Compiler-explorer 有一个内置的差异窗格 噢噢噢,不错
0赞 Petr 10/9/2023
@PeterCordes 感谢您发现我的错误,确实 lambda 应该使用该参数。让我尝试解决这个问题并重新发布结果。