通常,列表既可以作为链接列表实现,也可以作为数组列表实现,这些列表在插入元素时很慢。
我想知道是否可以使用处理器的MMU更有效地实现列表,通过重新映射而不是在插入或删除元素时复制内存。这意味着数组中任何位置的索引和插入/删除都具有O(1),better than any other list implementation的速度。
我的问题是:
答案 0 :(得分:19)
首先回答一些问题:
mmap
和Windows上的similar APIs。特别是Linux最近添加了several methods以允许从内核高级操作用户可见的缓冲区而无需复制 - 但其中一个有趣的是no longer for this world(至少在性能方面) 。 使用MMU技巧的核心问题是(a)你只能“零复制”整个页面,这几乎意味着4K粒度或更大,以及类似的限制性对齐(b)即使{{1} } -type调用很快,高效的内存复制例程也是如此!
让我们首先看一下(a)。如果我理解正确,您希望通过使用MMU技巧来加速插入mmap
之类的内容,以便在插入发生时移动需要移动的元素。问题是你只能在典型系统上移动0,4096,8192等字节!因此,如果您将一个4字节std::vector
插入int
,这有何帮助?您可以将vector<int>
的底层存储分解为插入点处的两个部分并跟踪它,希望再次合并它们(例如,如果您插入4096字节的东西) - 但是你最终会得到一个不同的数据结构,具有不同的属性,而且无论如何MMU技巧都不是真正的基础。
这将我们带到(b)。理所当然地认为,在我的机器上,页面可以在~120 ns内重新映射(通过vector
)。这看起来很快(当你考虑它涉及采用各种内核锁定,弄乱页面表,添加VMA等等时,它还不错) - 但复制内存也非常快。在同一个盒子上,我可以以大约12 GB / s的速度复制内存中(即,往/来自任何缓存级别的RAM),而L1或L2中的副本可能以80-100 GB / s的速度复制。因此,复制4K页面需要介于41 ns(缓存)和340 ns(未缓存,RAM)之间。所以搞乱页面表并不是一个明确的胜利,即使它可能可能,特别是在缓存的情况下(缓存的情况可能是主导的情况,平均大多数工作负载)。
所以这些类型的技巧可能有意义,但仅限于特定情况,例如:
MMU技巧的最常见且最有用的示例可能是mmap
。在Linux和Windows(it seems?)上,realloc
可以通过重新映射和扩展内存中的映射页面(也称为MMU技巧)来实现,这既避免了物理复制又需要暂时同时使用旧的分配区域和新区域立即“实时”(如果它们的总和接近物理内存的大小,则可能很难)。
特别是,最新版本的Linux可能会首先使用mremap
到realloc
堆realloc
的堆区域(默认情况下,这种情况发生在大于128K的分配请求中,但当mmap
的可用空间用尽时,也可能发生这种情况。