为什么StringCopyFromLiteral比StringCopyFromString快?

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

因为 Quick C++ Benchmarks 例。

static void StringCopyFromLiteral(benchmark::State& state) {
  // Code inside this loop is measured repeatedly
  for (auto _ : state) {
    std::string from_literal("hello");
    // Make sure the variable is not optimized away by compiler
    benchmark::DoNotOptimize(from_literal);
  }
}
// Register the function as a benchmark
BENCHMARK(StringCopyFromLiteral);

static void StringCopyFromString(benchmark::State& state) {
  // Code before the loop is not measured
  std::string x = "hello";
  for (auto _ : state) {
    std::string from_string(x);
  }
}
// Register the function as a benchmark
BENCHMARK(StringCopyFromString);

http:/quick-bench.comIcZllt_14hTeMaB_sBZ0CQ8x2Ro。

如果我懂组装怎么办...


更多结果。

http:/quick-bench.com39fLTvRdpR5zdapKSj2ZzE3asCI的。

c++ string constructor copy-constructor stdstring
1个回答
4
投票

答案很简单。在你构造一个 std::string 在这种情况下,编译器通过使用汇编中的常量直接填充字符串对象的内容进行优化。 这避免了昂贵的循环以及测试是否可以应用小字符串优化(SSO)。 在这种情况下,它知道可以应用SSO,所以编译器生成的代码只需要将字符串直接写入SSO缓冲区。

注意StringCreation案例中的这段汇编代码。

// Populate SSO buffer (each set of 4 characters is backwards since
// x86 is little-endian)
19.63% movb   $0x6f,0x4(%r15)    // "o"
19.35% movl   $0x6c6c6568,(%r15) // "lleh"
// Set size
20.26% movq   $0x5,0x10(%rsp)    // size = 5
// Probably set heap pointer. 0 (nullptr) = use SSO buffer
20.07% movb   $0x0,0x1d(%rsp)

你看到的是那里的常量值。这段代码不多,也不需要循环。 事实上,在StringCreation中的 std::string 构造者 甚至不需要被调用! 编译器只是把东西放在内存里,放在与 std::string 构造函数会。

如果编译器不能应用这种优化,结果就会大不相同--特别是,如果我们通过先将字词复制到一个char数组中来 "隐藏 "源是字符串字词的事实,结果就会翻转。

char x[] = "hello";
for (auto _ : state) {
  std::string created_string(x);
  benchmark::DoNotOptimize(created_string);
}

现在,"from-char-pointer "的情况下,会采取 两倍! ! - 为什么?

I 怀疑 这是因为 "copy from char pointer "的情况下,不能简单地通过查看一个值来检查字符串有多长。 它需要知道是否可以进行小字符串优化。 它有几种方法可以去做。

  1. 先测量字符串的长度,进行分配(如果需要),然后把源文件复制到目的地。 在SSO确实适用的情况下(这里几乎可以肯定是这样),我预计这需要花费两倍的时间,因为它必须走两次源码--一次是测量,一次是复制。
  2. 从源码中逐字复制,追加到新的字符串。 这需要在每次append操作时测试字符串现在是否对SSO来说太长,需要复制到一个堆分配的char数组中。 如果字符串目前在一个堆分配的数组中,则需要改测试是否需要调整分配的大小。 这也会花费相当长的时间,因为对源字符串中的每个字符至少要进行一次测试。
  3. 从源码中分块复制,以减少需要执行的测试次数,并避免走两次源码。 这将比逐个字符的方法更快,因为测试的数量会更少,而且,由于源码没有被走过两次,CPU的内存缓存会更有效。 这只会对长字符串显示出显著的速度改进,而我们这里没有这种情况。 对于短字符串,它的效果会和第一种方法(测量,然后复制)差不多。

对比一下,当它从另一个字符串对象中复制时的情况:它可以简单地看一下 size() 的另一个字符串,并立即知道它是否可以进行SSO,如果不能进行SSO,那么它也知道到底要为新字符串分配多少内存。

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