为什么std :: string :: append在本基准测试中比push_back快得多?

问题描述 投票:2回答:1

给出以下基准:

const char* payload = "abcdefghijk";
const std::size_t payload_len = 11;
const std::size_t payload_count = 1000;

static void StringAppend(benchmark::State& state) {
  for (auto _ : state) {
    std::string created_string;
    created_string.reserve(payload_len * payload_count + 1);

    for(int i = 0 ; i < payload_count; ++i) {
      created_string.append(payload, payload_len);
    }

    benchmark::DoNotOptimize(created_string);
  }
}

BENCHMARK(StringAppend);

static void StringBackInsert(benchmark::State& state) {
  for (auto _ : state) {
    std::string created_string;
    created_string.reserve(payload_len * payload_count + 1);
    auto inserter = std::back_inserter(created_string);

    for(int i = 0 ; i < payload_count; ++i) {
      for(std::size_t i = 0; i < payload_len; ++i) {
        *inserter = payload[i];
        ++inserter;
      }
    }

    benchmark::DoNotOptimize(created_string);
  }
}
BENCHMARK(StringBackInsert);

static void StringPushBack(benchmark::State& state) {
  for (auto _ : state) {
    std::string created_string;
    created_string.reserve(payload_len * payload_count + 1);

    for(int i = 0 ; i < payload_count; ++i) {
      for(std::size_t i = 0; i < payload_len; ++i) {
        created_string.push_back(payload[i]);
      }
    }

    benchmark::DoNotOptimize(created_string);
  }
}
BENCHMARK(StringPushBack);

我在quickbench上获得了以下内容,它们显示出非常显着的差异:enter image description here

考虑到所有必需的内存是提前分配的,我很难接受这样的想法,即仅进行大小与容量检查实际上就代表了这里的所有成本,除非可能有大量的负载-hit-store或分支的错误预测。

http://quick-bench.com/XQ9kepYFE1_dZD8vVaQwOUSSVoE

我想了解的是:

  • 编译器使用的特定于此设置的东西不一定适用于实际情况吗?
  • 如果是这样,是否有办法重新安排此基准以更具代表性?
c++ string performance benchmarking compiler-optimization
1个回答
2
投票

编译器并非总能成功地将一个字节的循环优化成一个不可怕的东西。您正在将已知长度的append与调用push_back的整个内部循环进行比较。

[push_back包括大小检查,因此以这种方式使用它检查并在压入任何字节之后有条件地重新分配+副本,这可能会破坏编译器对其进行优化的能力。

但是append只需要在整个块之间进行检查,我认为clang可以内联11字节memcpy以使用几个加载/存储而不是11字节加载/字节存储。

© www.soinside.com 2019 - 2024. All rights reserved.