的std ::矢量<a> vs std::vector<a*> difference for CPU</a*></a>

时间:2014-05-04 17:00:56

标签: c++ performance cpu ram

当我有一个巨大的std :: vector时,让我们讨论一个案例。我需要迭代所有元素并调用print函数。有两种情况。如果我将对象存储在向量中,并且对象将在内存中彼此相邻,或者我分配对象是堆,并将对象的指针存储在向量中。在这种情况下,对象将分布在整个RAM中。

如果对象的副本存储在std::vector<A>中,当CPU将数据从RAM带到CPU高速缓存时,它会带来一块内存,其中包含向量的多个元素。在这种情况下,当您迭代每个元素并调用一个函数时,您就会知道将处理多个元素,然后CPU才会进入RAM以请求剩余的数据部分进行处理。这很好,因为CPU没有很多空闲周期。

std::vector<A*>的案例怎么样?当它带来一大块指针时,CPU是否很容易通过指针获取对象?或者它应该从RAM请求您调用某些函数的对象,并且会有缓存未命中和空闲的CPU周期?与上述情况相比,它在性能方面是否不好?

3 个答案:

答案 0 :(得分:2)

至少在典型的情况下,当CPU从内存中获取指针(或多个指针)时,会自动获取这些指针所引用的数据。

因此,在指针向量的情况下,当您加载每个指针引用的项时,您通常会获得缓存未命中,并且访问将比连续存储的速度慢得多。当/如果每个项目相对较小时,尤其如此,因此其中一些项目可以适合单个缓存行(对于某种级别的缓存 - 请记住,当前处理器通常具有两个或三个级别的缓存,每个可能有不同的行大小。)

但是,可以在某种程度上缓解这种情况。您可以为类重载operator new以控制该类对象的分配。使用它,您至少可以将该类的对象保存在内存中。这并不能保证特定向量中的项目是连续的,但可以改善局部性,足以显着提高速度。

另请注意,向量通过Allocator对象(默认为std::allocator<T>,然后使用new)分配其数据。虽然界面有点乱,所以它比你通常喜欢的更难,你可以定义一个分配器,如果你愿意,可以采取不同的行动。这通常不会对单个向量产生太大影响,但是如果(例如)你有多个向量(每个都是固定大小)并希望它们使用彼此相邻的内存,那么你可以通过分配器对象。

答案 1 :(得分:2)

  

如果我将对象存储在向量中,并且对象将在内存中彼此相邻,或者我分配的对象是堆

无论使用std::vector<A>还是std::vector<A *>,向量的内部缓冲区都将在堆中分配。但是,您可以使用有效的内存池来管理分配和删除,但您仍然可以使用堆上的数据。

  

与上述情况相比,在性能方面是否不好?

如果在没有专门的内存管理的情况下使用std::vector<A *>,您可能会很幸运地进行分配并始终在内存中很好地对齐数据,但通常最好由{执行连续分配{1}}。在前一种情况下,重新分配整个向量可能需要更长的时间(因为指针通常小于常规结构),但它会受到局部性的影响(考虑内存访问)。

答案 2 :(得分:1)

  

当它带来一大块指针时,CPU很容易获得   指针对象?

不,不是。 CPU并不知道它们的指针(CPU看到的所有内容只是一堆,不涉及语义),直到它取出&#34;解除引用&#34;指令。

  

或者它应该从RAM请求您调用它的对象   功能,将有缓存未命中和空闲CPU周期?

那是对的。 CPU将尝试加载与缓存指针相对应的数据,但这些数据可能位于远离最近访问的内存的某个位置,因此它可能是缓存未命中。

  

与上述情况相比,在性能方面是否不好?

如果您唯一关心的是访问元素,那么是的,它很糟糕。但在某些情况下,指针的矢量是更可取的。也就是说,如果你的对象不支持移动(C ++ 11还不是主流),那么矢量复制会变得更加昂贵。即使不复制您的矢量,也可能是您事先不知道存储元素数量的情况,因此您无法事先调用reverse(n)。然后,当vector将耗尽其容量并且将被强制调整大小时,将复制所有对象。

但最终它取决于具体类型。如果你的对象很小(微小的结构,整数或浮点数),那么通过复制工作显然会更好,因为指针的开销会太大。

相关问题