编译器通过引用与按值优化返回

时间:2021-03-24 12:11:08

标签: c++ c++11

我想了解编译器可以执行的具体优化(如果有的话)。

两个 get 函数 get() 通过引用返回一个字符串,另一个通过值返回。地图本质上是全球性的。

我想理解这一点,因为它是常见的事情,所以如果我们有对象映射或映射映射或非常大的字符串,会发生什么(不将我的映射仅限于字符串)。它可以得到多少代价。我可以理解我们大多数人都知道它依赖于编译器,但是我们可以有一个未知数列表。真的会有帮助

std::map<std::string, std::string> value;

std::string& get(std::string& key) {
    return value[key];
}

std::string get2(std::string& key) {
    return value[key];
}

int main()
{ 
    value.insert(std::make_pair("name","XXXXXXX"));
    std::string keyaa = "name";
    auto new_val = get(keyaa);
    auto new_val2 = get2(keyaa);
}

1 个答案:

答案 0 :(得分:1)

从 C++ 语言的角度来看,不能保证 get2 中的副本将被省略。强制 return value optimization 仅涵盖 prvalue 操作数(即在函数调用本身中创建的值)。即使是“允许的”优化也不包括预先存在的对象。

所以我们只能希望今天的编译器足够聪明来优化副本,这意味着我们必须对其进行测试!

我稍微重写了示例,以使编译器最容易优化字符串:

#include <string>
#include <map>

struct Test {
    std::map<std::string, std::string> value = {{"name", "XXXXXX"}};

    std::string const& get(std::string const& key) {
        return value[key];
    }

    std::string get2(std::string const& key) {
        return value[key];
    }
};

static void TestReturnByReference(benchmark::State& state) {
  Test test;
  std::string key = "name";
  for (auto _ : state) {
    size_t n = test.get(key).size();
    benchmark::DoNotOptimize(n);
  }
}

BENCHMARK(TestReturnByReference);

static void TestReturnByValue(benchmark::State& state) {
  Test test;
  std::string key = "name";
  for (auto _ : state) {
    size_t n = test.get2(key).size();
    benchmark::DoNotOptimize(n);
  }
}

BENCHMARK(TestReturnByValue);

不,事实证明,无论 GCC 还是 Clang 都能够完全优化它:

GCC 10.2,-O3: (link to quick-bench) - 显着差异:

enter image description here

Clang 11 (libc++),-O3: - 更好,但仍然较慢:

enter image description here

结论:通过引用返回现有字符串更快。


注意:从 C++17 开始,您可以返回 std::string_view 以避免担心:

std::string_view get(std::string const& key) {
    return value[key];
}
相关问题