为什么 std::copy 比 std::string 构造函数更快?

Jas*_*.Pu 7 c++ performance std stdstring

我尝试了这些代码来比较std::copyandstd::string的构造函数。

#include <chrono>
#include <iostream>
#include <vector>

void construct_test() {
  std::vector<uint8_t> raw_data;
  for (int i = 0; i < 1000 * 1024; i++) {
    raw_data.push_back(i % 256);
  }

  auto start = std::chrono::high_resolution_clock::now();
  std::string target_data;
  target_data = std::string(raw_data.begin(), raw_data.end());
  auto finish = std::chrono::high_resolution_clock::now();
  std::cout << "construct: " << std::chrono::duration_cast<std::chrono::microseconds>(finish -
                                                                     start)
                   .count()
            << "us" << std::endl;
}

void copy_test() {
  std::vector<uint8_t> raw_data;
  for (int i = 0; i < 1000 * 1024; i++) {
    raw_data.push_back(i % 256);
  }

  auto start = std::chrono::high_resolution_clock::now();
  std::string target_data;
  target_data.resize(raw_data.size());
  std::copy(raw_data.begin(), raw_data.end(), target_data.begin());
  auto finish = std::chrono::high_resolution_clock::now();
  std::cout << "copy: " << std::chrono::duration_cast<std::chrono::microseconds>(finish -
                                                                     start)
                   .count()
            << "us" << std::endl;
}

int main() {
  construct_test();
  copy_test();

  return 0;
}

Run Code Online (Sandbox Code Playgroud)

我得到了结果:

construct: 6245us
copy: 1087us
Run Code Online (Sandbox Code Playgroud)

std::copy速度快 6 倍!

这符合预期吗?如果有,原因是什么?
我搜索了很多将向量转换为字符串的方法,但没有人提到std::copy方法。我应该使用这种方式吗?有什么缺点吗?

Jan*_*tke 8

正如评论者指出的那样,您的测试方法存在严重缺陷。一般来说,您必须多次运行操作(可能数百万或数十亿次)才能获得有意义的结果。否则,运行基准测试的顺序和日程安排等可能会给您带来截然不同的结果。

  • @463035818_is_not_an_ai 指出您应该使用steady_clockoverhigh_resolution_clock进行基准测试(尽管在这种情况下这不太可能产生重大影响)
  • @VLL 指出,只需更改 和 的顺序construct_test()即可copy_test()使一个函数比另一个函数运行得更快

您可以使用google/benchmark (由QuickBench使用)来获得更有意义的结果。

除了您使用的两种方法之外,至少还有两种创建/覆盖字符串的方法:

// BenchmarkInit
std::string target_data = std::string(raw_data.begin(), raw_data.end());

// BenchmarkAssignmentOp
std::string target_data;
target_data = std::string(raw_data.begin(), raw_data.end());

// BenchmarkAssign
std::string target_data;
target_data.assign(raw_data.begin(), raw_data.end());

// BenchmarkCopy
std::string target_data;
target_data.resize(raw_data.size());
std::copy(raw_data.begin(), raw_data.end(), target_data.begin());
Run Code Online (Sandbox Code Playgroud)

我们得到 clang 15、libstdc++、-O3 的以下基准测试结果在此输入图像描述

  • std::string最好使用构造函数,无论是否首先进行不必要的默认初始化。前两种方法std::memcpy内部使用,这应该是复制内存最快的方法。
  • std::copy速度较慢,可能是因为.resize()需要首先将内存归零,并且没有很好地针对 进行优化memcpy,而是针对向量化内存操作进行优化。
  • .assign速度要慢得多,可能是因为与 相比,部分循环展开较少std::copy,因此除了复制内存之外还有很多开销。

即使使用适当的基准测试,您也可以看到意想不到的巨大差异,并且只有在查看装配时才能理解事物的意义。

更新

我已经打开了一份错误报告,并且这个性能问题已由@JonathanWakely修复。