【问题标题】:How is tuple implemented in CPython?元组在 Python 中是如何实现的?
【发布时间】:2022-01-06 19:06:54
【问题描述】:

我一直在尝试了解 CPython 是如何在幕后实现的。 Python 是高级别的很好,但我不喜欢把它当作一个黑盒子。

考虑到这一点,如何实现元组?我看过the source (tupleobject.c),但它超出了我的想象。

我看到PyTuple_MAXSAVESIZE = 20PyTuple_MAXFREELIST = 2000,什么是保存和“空闲列表”? (长度为 20/21 或 2000/2001 的元组之间会有性能差异吗?是什么强制执行最大元组长度?)

【问题讨论】:

    标签: python data-structures tuples cpython python-internals


    【解决方案1】:

    需要注意的是,此答​​案中的所有内容均基于我从查看您链接的实现中收集到的内容。

    元组的标准实现似乎只是一个数组。但是,有很多优化可以加快速度。

    首先,如果您尝试创建一个空元组,CPython 将返回一个代表空元组的规范对象。因此,它可以节省大量仅分配单个对象的分配。

    接下来,为了避免分配一堆小对象,CPython 为许多小列表回收内存。有一个固定常数 (PyTuple_MAXSAVESIZE) 使得所有小于此长度的元组都有资格回收它们的空间。每当一个长度小于该常量的对象被释放时,与其关联的内存有可能不会被释放,而是将根据其大小存储在“空闲列表”中(下一段中将详细介绍) .这样,如果您需要分配一个大小为 n 的元组,而其中一个已被分配且不再使用,CPython 可以回收旧数组。

    空闲列表本身被实现为一个大小为PyTuple_MAXSAVESIZE的数组,存储指向未使用元组的指针,其中数组的第n个元素指向NULL(如果没有额外的大小为n的元组可用)或一个回收的元组大小为 n。如果有多个不同的大小为 n 的元组可以重用,则它们通过将每个元组的第零个入口点指向下一个可以重用的元组,以一种链表的形式链接在一起。 (由于只分配了一个长度为零的元组,因此永远不会有读取不存在的第零元素的风险)。通过这种方式,分配器可以存储一定数量的每个大小的元组以供重用。为了确保这不会使用太多内存,还有第二个常量PyTuple_MAXFREELIST 控制任何存储桶中任何这些链表的最大长度。然后有一个长度为PyTuple_MAXSAVESIZE 的辅助数组,它存储每个给定长度的元组的链表长度,这样就不会超过这个上限。

    总而言之,这是一个非常聪明的实现!

    【讨论】:

      【解决方案2】:

      因为在正常操作过程中 Python 会创建和销毁 很多 个小元组,因此 Python 会为此目的保留一个小元组的内部缓存。这有助于减少大量的内存分配和释放流失。出于同样的原因,从 -5 到 255 的小整数被保留(制成单例)。

      PyTuple_MAXSAVESIZE 定义控制符合此优化条件的元组的最大大小,PyTuple_MAXFREELIST 定义控制有多少这些元组保留在内存中。当一个长度 PyTuple_MAXSAVESIZE 的元组被丢弃时,如果仍有空间(tupledealloc)将其添加到空闲列表中,以便在 Python 创建新的小元组时重新使用(PyTuple_New )。

      Python 在如何存储这些方面有点聪明;对于每个长度 > 0 的元组,它将重用每个缓存元组的第一个元素,将最多 PyTuple_MAXFREELIST 个元组链接到一个链表中。所以free_list数组中的每个元素都是一个Python元组对象的链表,并且这样一个链表中的所有元组都是相同大小的。唯一的例外是空元组(长度为 0);这些中只需要一个,它是一个单例。

      所以,是的,对于长度超过 PyTuple_MAXSAVESIZE 的元组,python 保证必须为新的 C 结构单独分配内存,如果您创建 大量丢弃此类元组,这可能会影响性能.

      如果你想了解 Python C 的内部结构,我建议你学习Python C API;它将更容易理解 Python 用于在 C 中定义对象、函数和方法的各种结构。

      【讨论】:

      • 为了清楚起见,它是缓存包含内容的元组对象,还是“只是”维护一个适当大小的内存块的空闲列表?我的印象是后者,但你的第一段读起来有点像第一段。
      • @delnan:元组对象本身的缓存,它们只是对象引用的固定列表。内容本身不会被缓存。元组引用的对象有自己的内存块。
      • 这听起来像是函数式语言如何实现其不可变列表。 (例如,Haskell 中的 Data.List。)很高兴看到 Python 也有它!
      猜你喜欢
      • 2012-12-17
      • 1970-01-01
      • 2020-04-14
      • 2010-10-15
      • 2011-06-30
      • 2019-02-11
      • 2013-07-28
      • 1970-01-01
      相关资源
      最近更新 更多