存储指向对象的指针时,提高引用的数据局部性

时间:2014-05-31 15:24:15

标签: c++ pointers caching containers

我理解的是,将对象直接存储到向量会产生比由于预取而存储指针更好的性能。

std::vector<Object> container1; // The objects are stored sequentially. 
std::vector<Object*> container2; //The pointed to objects are all over the place.

有什么方法可以解决这个问题吗?

我在考虑使用顺序容器作为指向对象。 这样,指针和对象在内存中都是顺序的,但我不是缓存方面的专家,我甚至不确定这是否有助于提高性能。

3 个答案:

答案 0 :(得分:2)

图表中的一些测量结果,来自陷阱演示文稿中的示例,显示标准OO的影响,具有连续存储的位置,然后进一步改进数据结构的进一步改进,进一步展平它们并预取 - http://seven-degrees-of-freedom.blogspot.co.uk/2009/12/pitfalls-of-object-oriented-programming.html?view=flipcard

为我认为非常清楚的游戏程序员编写的OO编程演示文稿的缺陷,解释了现代系统中使用许多指针的传统对象的问题&#34; OO&#34; - http://research.scee.net/files/presentations/gcapaustralia09/Pitfalls_of_Object_Oriented_Programming_GCAP_09.pdf

此外,它不只是关于缓存预取&amp;服务器/桌面系统上的位置,但也有页面错误的可能性(可能是来自/来自磁盘),这可能使分散的碎片对象分配非常低效。其次,您可能想要使用多个核心,更新对象的问题是访问争用和锁定,因为每个对象都无法知道对对象的其他访问是否安全&#34;。

因此,为了增加局部性,打包对象有所收获,尽可能紧密并且在内存中的同一页面上有块(特别是对于在高速缓存行上重叠的小项),这就是为什么在C中预分配nmemb结构的数组使用malloc void *calloc(size_t nmemb, size_t size)

,使用void *malloc(size_t size);可以比使用{{1}}更高效

现在假设大多数时候,你在大量对象上重复计算,比如对象中的特定值进行求和,或者以某种方式对某些字段进行转换,然后你想要将它组织成将数据打包在一起,以便尽可能在一个缓存行上,并且你不是从RAM(或磁盘)加载你很少需要或使用的所有东西,比如可能是识别字符串,链接指针或其他值你不希望有一段时间需要。这些字段实际上可以是独立的,因此可以与不同字段的变换同时进行求和。或者,可以通过简单地将其分成一半或四分之一来在CPU之间处理大量项目,而不会争用安全读取或更新每个对象所需的锁定。您知道您感兴趣的数据并不是争用的。

在这种情况下,您宁愿拥有并行数组,您可以使用可预测的预取顺序扫描,CPU可以检测并预测,从而自动启动加载。没有指针链,项的顺序是隐含的,因此您可以通过避免浪费的4/8字节指针来最大化数据密度。并且您可以减少对实际需要来自多个线程的同时更新的字段的锁争用。为什么它重要Herb Sutters机器架构谈话很有意思(但长视频) - http://www.youtube.com/watch?feature=player_detailpage&v=L7zSU9HI-6I#t=897

当你开始这么想时,你正在考虑面向数据的设计 - What is data oriented design?

如果它适合您的问题,它可以大大提高性能,并由游戏程序员使用 - http://gamedevelopment.tutsplus.com/articles/what-is-data-oriented-game-engine-design--cms-21052

这里有另一个先前的答案,其中有更详细的解释和一些解释现代记忆问题的非常详细的论文的链接 - What is "cache-friendly" code?

答案 1 :(得分:0)

我认为您的选择应该取决于容器所有者和包含对象之间的关系类型。如果容器所有者管理他们的生命周期,那么您可以免费使用对象容器。如果没有直接的管理关系,例如在监听器容器的情况下,你应该使用指针容器。我并不认为这对性能问题有好处。

答案 2 :(得分:0)

您无法以任何合理的方式使用std::vector执行此操作。容器不适合这个目的。考虑一下我想到的一些问题:

container2.push_back(nullptr);
Object o;
container2.push_back(&o);
Object *ptr = new Object;
container2.push_back(ptr);
container2.push_back(ptr); // added twice

如果你以某种方式安排动态分配的对象在堆上顺序存在,那么这与在某个容器中将指针存储到它们几乎无关。

您的性能问题可能只是想象中的。你有没有真正衡量过表现?否则,您可能应该阅读有关堆碎片的更多信息。