为什么将 char[256] 结构推送到 std::queue 比推送 std::string 结构慢?

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

我在STL队列推送中遇到了一个我不太理解的行为。

基本上,我有两个结构

struct A {
  string a;
};

struct B {
 char b[256];
};

struct A st1;
struct B st2;

/* ...assign a 256 characters string to both st1 and st2... */

queue<struct A> q1;
queue<struct B> q2;
for(int i=0; i< 10000; i++) {
  q1.push(st1);
}

 for(int i=0; i< 10000; i++) {
  q2.push(st2);
}

我意识到,与字符串结构相比,使用 char 结构的队列在推送该结构时会使用更长的时间(例如 5 倍)。在检查各个推送后,我意识到 char struct 推送性能到处都有相当多的峰值(范围从 2 倍到 10 倍)。这是什么原因呢?

c++ string performance stl
5个回答
7
投票

每次将 st1 或 st2 推送到队列中时,您实际上推送的是它的副本(不是引用或指针)。成本差异在于数据的复制。在

structB
中,每次都必须复制完整的 256 个字节。在
structA
中,您只复制字符串实例,该实例很可能具有写时复制语义,因此在修改其中之一之前,它们都将共享对底层字符串数据的相同引用。


1
投票

您的 C++ 实现可能使用写时复制字符串实现,这意味着字符串副本并不真正复制字符串(而是链接回副本),并且仅在写入时“真正”复制字符串.

要测试是否是这种情况,请将其放入循环中,在

q1.push(st1)
行之后:

++st1.a[0];

然后再来一次。

显然,字符数组不具有写时复制行为,并且每次您要求复制时都会“真正”复制。


0
投票

字符数组比空字符串大 - 随着向量因其使用的内存量的增加而增长,峰值可能与必要的重新分配有关。

如果字符串不为空,则无论如何都会启动写时复制,因此您需要牺牲一些锁定时间/引用计数器增量等来对抗内存使用:更快的是系统相关的。


0
投票

原因很可能是:

1)动态分配内存来保存每个字符串内的字符数据
2) 有可能,但可能性很小,调整支持队列的双端队列页面缓冲区的大小。


0
投票

std::queue 是另一个容器的适配器(实现了 front、back、push_back 和 pop_front),除非您指定要适配哪个容器,否则它将使用 std::deque。 Deque 在后台执行一些块分配魔法,应该提供类似于向量的大小调整,但性能更好,因为它管理多个不连续的块,并且每次调整大小时不必复制所有内容。无论如何,这是一个猜测,但我想说这就是原因。

由于为所有这些数组腾出空间,字节数组结构的命中率更高,我敢打赌,在更长的范围内,字符串结构也会产生峰值,它现在更小,因为字符串可能维护对底层字符存储的引用直到有什么改变。

现在您有机会熟悉您选择的分析器并确定答案!启动 valgrind (--callgrind) 或您的平台支持的任何分析器,并准确查看哪些调用正在使用时间和地点。

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