为什么某些类型的字符串拼接比其他类型快得多?

12

考虑以下四种情况:

#include <string>
int main()
{
    std::string s("Hi I'm Da");

 1. s += "n";
 2. s += 'n';
 3. s = s + "n";
 4. s = s + 'n';

    return 0;
}

使用以下方法调用运行此测试套件

g++ -std=c++11 -O3 -DVER=case -Wall -pedantic -pthread test.cpp -o test

使用 g++ 版本 4.8.3 20140624,我得到了以下结果:

2.16172ms
0.48296ms
510.202ms
510.455ms

现在我明白+=更快的原因是因为在赋值之前不需要使用+来复制,但是为什么情况1和2相比于情况3和4会表现出显著的差异呢?另外,使用双引号或单引号如何影响连接速度?


9
用字符串字面值,代码需要搜索空终止符。用字符字面值,只需执行一个操作,无需分支。赋值在右侧生成一个临时对象。 - Kerrek SB
1
他们可能真的需要一个针对字符串字面值的重载,以便在编译时捕获它们的长度... 这将加快速度。 - Deduplicator
@嗨,我是丹:你把单引号和双引号当作某种化妆特征来引用,实际上它们意味着完全不同的事情。"n"是C风格的字符串,而'n'根本不是字符串,而是一个单个字符。难怪它们被处理为完全不同的代码分支,针对处理不同的一般情况进行了优化。 - AnT stands with Russia
那是相当惊人的性能差异。-.- - Lightness Races in Orbit
@KerrekSB "使用字符串字面值,代码需要搜索空终止符" 这正是我不理解为什么 std::basic_string 没有一个适用于原始字符串字面值的合适构造函数 (template<std::size_t N> basic_string( const char (&str[N]) ))。字符串字面值的大小在编译时已知,但我们仍将它们视为愚蠢的以 null 结尾的 C 字符串。 - Manu343726
显示剩余3条评论
1个回答

5
s += "n";

这个方法是对字符串进行原地操作的。有可能不需要重新分配内存。但是字符串字面量是以0结尾的字符序列,所以代码需要在'n'后的内存中找到值为0的位置。

s += 'n';

这个和第一个类似,但是它使用字符字面量而不是字符串字面量。不需要搜索 0,所以可能更快。

s = s + "n";

搜索0是存在的,但更重要的是 - 要构建新的临时字符串对象,而且很可能意味着进行内存分配,这将成为数量级或更昂贵的操作。

s = s + 'n';

虽然新方法可能比以前的更快,但由于寻找0而引起的差异与堆上的临时对象创建和分配相比可以忽略不计。

请注意所有的“可能”和“也许”。我所描述的可能会在流行的编译器中发生,但各种优化可能会完全改变情况。


1
那最后一段非常关键,因为它们都具有完全相同的可观察行为,因此可以优化为完全相同的指令。(只是想强调一下。) - Deduplicator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接