对象容器的性能与指针容器的性能相比如何?

3
class C { ... };
std::vector<C> vc;
std::vector<C*> pvc;
std::vector<std::unique_ptr<C>> upvc;

根据C的大小,存储值的方法和存储指针的方法中哪种更有效率是不同的。

在32位和64位平台上,是否有可能大致知道它的大小?


6
高效用于什么?这实际上取决于你如何使用这些向量。如果你关注性能,你需要进行测量。 - juanchopanza
1
可能还取决于您对容器的使用方式。在我能够证明这是不好的之前,我总是按值存储,而不是指针。尽管到目前为止从未发生过这种情况。 - Baum mit Augen
1
你是在谈论内存开销吗?你是在谈论对向量进行操作的运行时成本(例如排序)吗?你想要最大化哪种效率? - jschultz410
1
制作一个模板,在处理大对象时分配指针,而在处理小对象时不分配指针,这样可以解决这个问题吗? - codekiddy
1
我可以确定这个大小的下限。对于POD类型,你应该优先选择至少是该类型的向量,直到sizeof(POD)> sizeof(POD*)。不那么明确地说,由于内存局部性和较低的内存使用(特别是如果您正在动态分配要指向的对象),您应该至少优先选择该类型的向量,直到sizeof(POD)> 2 * sizeof(POD*)。 - jschultz410
显示剩余6条评论
3个回答

8

8
在得出任何结论之前,让我们先查看每个示例的细节。

对象向量

对象向量有一个初始化性能问题。当一个对象被添加到向量中时,会进行一次复制。当需要扩展保留的内存时,向量也会进行复制。较大的对象将需要更多复制时间,复杂或复合对象也是如此。
访问对象非常高效 - 只需要一次取消引用。如果您的向量可以适应处理器的数据缓存,那么这将非常高效。

原指针向量

这可能会有一个初始化性能问题。如果对象在动态内存中,则必须首先初始化(分配)内存。
将指针复制到向量中不取决于对象大小。根据对象大小,这可能是一种性能优化。
访问对象会有性能损失。在获取对象之前,存在两个取消引用过程。大多数处理器在加载其数据缓存时不遵循指针。因为处理器需要取消引用指向对象的指针时,这可能会影响性能,导致重新加载数据缓存。

智能指针向量

性能方面比原始指针稍微昂贵一点。但是,在销毁向量时,这些项将自动删除。必须在销毁向量之前删除原始指针;否则会创建内存泄漏。

总结

最安全的版本是在向量中进行复制,但这取决于对象的大小和重新分配保留内存区域的频率,存在性能影响。指针向量会有性能影响,因为需要取消引用两次,但在复制时不会产生额外的性能影响,因为指针具有一致的大小。与原始指针向量相比,智能指针向量可能需要额外的性能影响。
真正的情况可以通过对代码进行分析来找到。一个数据结构与另一个数据结构相比的性能节省可能会消失,当等待I/O操作,如网络或文件I/O时。
需要对数据结构执行大量操作才能使节省显着。例如,如果最��表现数据结构和最佳表现之间的差异为10纳秒,那么您将需要执行至少1E + 6次才能使节省显着。如果一秒钟很重要,请预计要访问数据结构更多次(1E + 9)。
我建议选择一个数据结构并继续进行开发。您开发代码的时间比程序运行时间更重要。安全性和健壮性也更重要。不安全的程序将消耗更多的时间来修复问题,而安全和健壮的版本则不会。

我说“可能”,这意味着您需要进行分析才能找出。这取决于unique指针的实现方式。还有其他智能指针,而不仅仅是unique_ptr。您还可以查看编译器生成的汇编语言清单。 - Thomas Matthews
不,unique_ptr 不会比手动内存管理的裸指针慢。 - D Drmmr

-1

对于一个普通数据类型(Plain Old Data,POD),该类型的向量总是比指针向量更有效,至少在 sizeof(POD) > sizeof(POD*) 之前。

几乎总是如此,即使到 sizeof(POD) > 2 * sizeof(POD*),也是如此,因为它具有优越的内存局部性和更低的总内存使用量,与动态分配要指向的对象时相比。

这种分析将保持真实,直到sizeof(POD)越过您架构、编译器和用法的某个阈值,您需要通过基准测试来发现。以上仅对POD类型的大小设置下限。

关于所有非POD类型,很难说出任何明确的结论,因为它们的操作(例如默认构造函数,复制构造函数,赋值等)可能与POD相同或者任意昂贵。


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