Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么在一个大的std::列表上迭代如此缓慢?_C++_List_Runtime_Std_Deque - Fatal编程技术网

C++ 为什么在一个大的std::列表上迭代如此缓慢?

C++ 为什么在一个大的std::列表上迭代如此缓慢?,c++,list,runtime,std,deque,C++,List,Runtime,Std,Deque,正如标题所示,我在我的一个程序中遇到了问题,在这个程序中,我使用std::list作为堆栈,并对列表的所有元素进行迭代。当列表变得非常大时,这个项目花费的时间太长了 有人对此有很好的解释吗?这是某种堆栈/缓存行为吗 (通过将列表更改为std::vector和std::deque(顺便说一下,这是一个惊人的数据结构)解决了这个问题,然后一切突然变得更快了) 编辑:我不是傻瓜,我也不在列表中间访问元素。我对列表所做的唯一一件事就是删除/添加列表末尾/开头的元素,并遍历列表中的所有元素。 我总是使用迭

正如标题所示,我在我的一个程序中遇到了问题,在这个程序中,我使用std::list作为堆栈,并对列表的所有元素进行迭代。当列表变得非常大时,这个项目花费的时间太长了

有人对此有很好的解释吗?这是某种堆栈/缓存行为吗

(通过将列表更改为std::vector和std::deque(顺便说一下,这是一个惊人的数据结构)解决了这个问题,然后一切突然变得更快了)

编辑:我不是傻瓜,我也不在列表中间访问元素。我对列表所做的唯一一件事就是删除/添加列表末尾/开头的元素,并遍历列表中的所有元素。
我总是使用迭代器对列表进行迭代

这是由于使用列表时会出现大量缓存未命中。通过向量,周围的元素存储在处理器缓存中

这是由于使用列表时会出现大量缓存未命中。通过向量,周围的元素存储在处理器缓存中

查看以下内容。

查看以下内容。

[编辑:我已更正。std::list没有运算符[]。抱歉。]

很难从您的描述中分辨出来,但我怀疑您试图随机访问这些项目(即,通过索引):


“vector”和“deque”都擅长于随机访问,因此在这两种情况下,它们中的任何一个都可以对这些类型——O(1)充分执行。但“列表”不适合随机访问。通过索引访问列表需要O(n^2)个时间,而使用迭代器时需要O(1)个时间。

[编辑:我已更正。std::list没有运算符[]。抱歉。]

很难从您的描述中分辨出来,但我怀疑您试图随机访问这些项目(即,通过索引):

“vector”和“deque”都擅长于随机访问,因此在这两种情况下,它们中的任何一个都可以对这些类型——O(1)充分执行。但“列表”不适合随机访问。通过索引访问列表需要O(n^2)时间,而使用迭代器时需要O(1)时间。

存在一个缓存问题:vector中的所有数据都存储在一个连续的块中,每个列表元素都是单独分配的,并且可能恰好存储在一个随机的内存位置,这会导致更多的缓存未命中。但是,我敢打赌您会遇到其他答案中描述的问题之一。

存在一个缓存问题:vector中的所有数据都存储在一个连续的块中,每个列表元素都是单独分配的,并且可能碰巧存储在相当随机的内存位置,这会导致更多缓存未命中。但是,我敢打赌您会遇到其他答案中描述的问题之一。

列表的缓存位置非常糟糕(不存在)。每个节点都是一个新的内存分配,可以是任意位置。所以,每次你跟随一个指针从一个节点到下一个节点,你就会跳转到内存中一个新的、不相关的地方。是的,这对性能有很大影响。缓存未命中可能比缓存命中慢两个数量级。在vector或deque中,几乎每次访问都会被缓存命中。一个向量是一个连续的内存块,所以在它上面迭代的速度和你得到的速度一样快。deque是几个较小的内存块,因此它偶尔会引入缓存未命中,但它们仍然很少见,而且迭代速度仍然非常快,因为您获得的大部分缓存命中

一个列表将列出几乎所有的缓存未命中。而且表现会很糟糕

实际上,从性能的角度来看,链表从来都不是正确的选择

编辑: 有评论指出,列表的另一个问题是数据依赖性。现代CPU喜欢重叠操作。但是如果下一条指令依赖于这条指令的结果,它就不能这样做

如果你在向量上迭代,那没问题。您可以动态计算下一个要读取的地址,而无需检查内存。如果您现在正在读取地址
x
,那么下一个元素将位于地址
x+sizeof(T)
,其中T是元素类型。因此,这里没有依赖项,CPU可以立即开始加载下一个元素,或者加载下一个元素之后的元素,同时仍然处理先前的元素。这样,数据就可以在我们需要的时候为我们准备好,这进一步有助于掩盖在RAM中访问数据的成本

在列表中,我们需要跟踪从节点
i
到节点
i+1
的指针,直到
i+1
加载完毕,我们甚至不知道在哪里查找
i+2
。我们有一个数据依赖性,因此CPU被迫一次读取一个节点,并且它不能提前开始读取未来的节点,因为它还不知道它们在哪里

如果列表不是所有缓存未命中,这不会是一个大问题,但由于我们得到了大量缓存未命中,这些延迟代价高昂

列表的缓存位置非常糟糕(不存在)。每个节点都是一个新的内存分配,可以是任意位置。所以,每次你跟随一个指针从一个节点到下一个节点,你就会跳转到内存中一个新的、不相关的地方。是的,这对性能有很大影响。缓存未命中可能比缓存命中慢两个数量级。在vector或deque中,几乎每次访问都会被缓存命中。一个向量是一个连续的内存块,所以在它上面迭代的速度和你得到的速度一样快。deque是几个较小的内存块,因此它偶尔会引入缓存未命中,但它们仍然很少见,而且迭代速度仍然非常快,因为您获得的大部分缓存命中

一个列表将列出几乎所有的缓存未命中。而且表现会很糟糕

实际上,从性能的角度来看,链表从来都不是正确的选择

编辑: 有评论指出,列表的另一个问题是数据依赖性。一种现代的CPU-li
for(int i = 0; i < mylist.size(); ++i) { ... mylist[i] ... }
for(list::iterator i = mylist.begin(); i != mylist.end(); ++i) { ... (*i) ... }