【问题标题】:Why can't compilers optimize-out this std::string construction?为什么编译器不能优化这个 std::string 构造?
【发布时间】:2018-11-14 02:59:55
【问题描述】:

考虑以下代码:

#include <string>
#include <cstring>

size_t foo(const char* cptr)
{
    if (cptr == nullptr) { return 0; }
    return strlen(cptr);
}

size_t bar()
{
    static const char* cptr { "Hello world" };
    return std::string{cptr}.length();
}

size_t baz(const char* cptr)
{
    if (cptr == nullptr) { return 0; }
    return std::string{cptr}.length();
}

使用 GodBolt,我们 can see 认为 GCC 8.1 和 Clang++ 6.0 可以优化 std::stringbar() 中,但不能在 baz() 中。在baz() 中,虽然编译器不能返回一个固定值,但它绝对可以只运行检查字符串长度的代码,而无需构造任何东西,或者至少无需完成构造——即行为类似于foo()。为什么它会完全构造字符串?

【问题讨论】:

  • 只要你在任何地方使用baz,它就会出现。
  • @FrançoisAndrieux:如果它在不同的翻译单元中,则不会。
  • baz("Hello world") 将输出与bar() 相同的单条指令
  • @ricco19:不在其他翻译单元中不会。
  • 尝试使用更长的字符串(比 SSO 大小更长)的 bar,您可能会得到一些不同的东西。字符串的构造函数远非微不足道,如果大小未知,可能很难消除。

标签: c++ g++ compiler-optimization clang++ stdstring


【解决方案1】:

baz 中,编译器不知道cptr 指向什么,所以它必须构造一个字符串来获取它的大小。

bar 中,编译器知道cptr 指向"Hello world",因此它可以用字符串的大小替换字符串的创建和对size 的调用。

【讨论】:

  • 嗯,它“必须”,但是它可能注意到该字符串仅用于执行计算字符串长度的代码。它应该能够不构造字符串而只运行它——就像在foo()中那样。
  • @einpoklum我不明白你为什么期望baz得到优化。以第一个foo 为例,优化存在不起作用,因为您从不在foo 中要求std::string。引用bar 也不起作用,因为输入数据是已知的,并且可以看到生成的代码完全是编译时常量。在实践中,您会发现对 baz 的调用将被内联,并且在可能的情况下,整个事情将被优化为常量,就像在 bar 中一样。
  • @einpoklum “它应该能够......运行那个” “那个”是什么? string::length()?
  • @einpoklum 但是在编译时它不知道那个字符串是什么。如果你真的不希望它成为std::string,请使用std::string_viewgodbolt.org/g/wVp43H
  • @einpoklum length() 只是 return str_size;。那里没有对字符串的大小进行任何计算。这是在std::string 的构造函数中完成的。因此,编译器需要调用构造函数来获取大小。在bar 中,所有这些都可以避免,因为编译器知道字符串文字的长度,所以它可以直接返回。
猜你喜欢
  • 1970-01-01
  • 2015-05-13
  • 1970-01-01
  • 2011-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-15
  • 2018-12-02
相关资源
最近更新 更多