【发布时间】:2021-12-12 01:08:32
【问题描述】:
我知道这里回答了一个类似的问题:How is Python's List Implemented? 但我想询问更多关于细节的信息。我想了解更多关于 CPython 如何实现列表大小调整的信息。我对C不太熟悉,所以看源代码有困难。
我想我理解的是列表Py_ssize_t ob_size的大小和分配给列表Py_ssize_t allocated的数量,当ob_size达到allocated时,需要分配更多的内存。我假设如果系统允许,内存将被分配到位,否则列表将被复制到内存中的另一个位置。特别是,我问的是选择将allocated 更改多少。从listobject.c,新分配的内存如下:
new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);
基本上,我们使分配的对象大小比所需的对象大小多 1/8(忽略常量)。我想知道为什么选择这个 1/8?在我的入门编码课程中,我记得学习过 ArrayLists,它在满时会翻倍。也许也可以选择增加 1/2 或 1/4。 增加越小,从一长串追加中分摊的时间就越差(仍然是常数,但系数更大),所以 1/8 似乎是一个糟糕的选择。我的猜测是每次分配少量将增加能够重新分配到位的机会。这是正确的推理吗?这个 CPython 实现在实践中运行良好吗?
注意:在删除元素后减少分配的列表内存时,当列表下降到原始大小的一半时会发生这种情况,从这部分代码可以看出:
/* 当先前的过度分配大到足以容纳 newsize 时,绕过 realloc()。如果 newsize 低于分配大小的一半,则继续执行 realloc() 以缩小列表。 */
if (allocated >= newsize && newsize >= (allocated >> 1)) {
【问题讨论】:
-
从 Big-O 的角度来看,每次加倍分配可能是最佳的 - 但在现实世界中,这可能会浪费字面 GB 的内存。
-
这是一个有趣的,最近关于该策略的讨论:bugs.python.org/issue38373
-
那次讨论中的一句话:“我们绝对应该重新审视过度分配策略。我上一次研究现有策略是在 2004 年。从那时起,速度/空间权衡考虑的权重变了。”
标签: python list cpython python-internals