【发布时间】:2020-05-31 23:31:36
【问题描述】:
在 C/C++ 中,当您分配动态内存时,操作系统会为您提供一个无法预测的地址,指向某个遥远的内存区域。如果您分配了大量动态内存,并且需要在它们之间移动(例如,链表、树),那么您必须浪费时间等待 CPU 查询 RAM,因为很可能,您的内存块想要的记忆与你的记忆并不接近。
最近,对于我的一门大学课程中的一个项目,我必须实现一棵树,以便节点可以有任意数量的子节点,并且我必须能够在恒定时间内将节点合并为单个树。所以,我有一堆内部 Node 对象和一堆指向周围节点的指针。
因为这是 C++,所以它当然很快,但随着时间和内存的测试,它的性能还可以。
因此,我决定尝试实现指针替换,这实际上是静态向量的索引,以及在需要新对象时增长向量的函数,以及可以重复使用的“已删除”索引堆栈。这样一来,所有内容都将在内存中紧密相连,并且取消引用不太可能导致缓存未命中。另外,因为我知道树不会在千兆字节的数据规模上进行测试,所以我可以将这些索引限制为 32 位,从而显着降低树结构的内存开销。
结果令人惊叹——在大多数情况下,我的课程的员工实施时间和记忆力只有三分之一。
我的问题是,如果缓存的好处如此极端,我们为什么要直接使用new 和delete 呢?为什么不把所有东西都放在向量中,每种类型一个呢?
【问题讨论】:
-
当然,如果您的程序可以提前准确预测每个对象需要多少,无论程序在运行时最终会做什么。
-
@SamVarshavchik 我的意思是动态分配的数组
-
向量存在一些限制,例如在向量的开头或中间插入元素,时间复杂度为 O(n )。这是您需要选择插入和删除元素的频率与搜索元素的频率之间的权衡。如果你不想使用 new/delete,你可以使用 std::map。
-
这实际上是静态向量的索引 -- 听起来这在没有适当同步的多线程程序中是个问题。
-
这就是我们有分配器的原因。此外,当性能满足您的需求时,这些性能提升带来的好处将被优化成本抵消。
标签: c++ caching optimization memory heap-memory