为啥 int 在 Python 中需要三倍的内存?

Posted

技术标签:

【中文标题】为啥 int 在 Python 中需要三倍的内存?【英文标题】:Why do ints require three times as much memory in Python?为什么 int 在 Python 中需要三倍的内存? 【发布时间】:2014-05-25 20:18:39 【问题描述】:

在 64 位系统上,Python 中的整数占用 24 个字节。这是例如所需内存的 3 倍。 C 表示 64 位整数。现在,我知道这是因为 Python 整数是对象。但是额外的内存有什么用呢?我有我的猜测,但很高兴知道。

【问题讨论】:

查看这篇文章:laurentluce.com/posts/python-integer-objects-implementation 和 docs.python.org/2/c-api/structures.html 了解 Python 中的常见对象结构 @DNA:谈论 Python 2 基本的int 类型; Python 2 中的 long 类型(替换 Python 3 中的 int 类型)仍然有点复杂。 请注意,此问题及其答案特定于 CPython 参考实现。其他实现可能有完全不同的内存使用(不过,同样的一般原则适用:需要存储大小和其他对象元数据)。 【参考方案1】:

请记住,Python int 类型没有像 C int 那样的有限范围;唯一的限制是可用内存。

内存用于存储值、整数存储的当前大小(存储大小可变以支持任意大小)和标准 Python 对象簿记(对相关对象的引用和引用计数)。

您可以查找longintrepr.h source(Python 3 int 类型在 Python 2 中传统上称为 long 类型);它有效地利用了PyVarObject C type 来跟踪整数大小:

struct _longobject 
        PyObject_VAR_HEAD
        digit ob_digit[1];
;

ob_digit 数组存储 15 位或 30 位宽的“数字”(取决于您的平台);所以在我的 64 位 OS X 系统上,一个不超过 (2 ^ 30) - 1 的整数使用 1 个“数字”:

>>> sys.getsizeof((1 << 30) - 1)
28

但如果您在数字中使用 2 个 30 位数字,则需要额外的 4 个字节,等等:

>>> sys.getsizeof(1 << 30)
32
>>> sys.getsizeof(1 << 60)
36
>>> sys.getsizeof(1 << 90)
40

基本的 24 字节是 PyObject_VAR_HEAD 结构,保存对象大小、引用计数和类型指针(在我的 64 位 OS X 平台上每个 8 字节/64 位)。

在 Python 2 上,整数 sys.maxint 但 >= -sys.maxint - 1 使用 simpler structure 存储,仅存储单个值:

typedef struct 
    PyObject_HEAD
    long ob_ival;
 PyIntObject;

因为这使用PyObject 而不是PyVarObject,所以结构中没有ob_size 字段,并且内存大小仅限于24 字节; 8 表示long 值,8 表示引用计数,8 表示类型对象指针。

【讨论】:

如果将 int 作为数字序列给出,如何处理负值? python中有二进制补码的概念吗?如果我打印 hex(-1) 我得到 -0x1 或者类似地如果我打印 bin(-1) 我得到 -0b1 我知道这可能不是内部表示的但是 python 如何决定它是一个负值如果高位没有设置? @Har:对象大小设置为负值。参见linked header file:负数用 ob_size 表示。因此,需要 2 个 ob_digits 条目的整数表示,然后 ob_size2-2,后者表示它是一个负整数。 所以这意味着它不是一个二进制补码,它只是结构中的一个位,表示它是否为负数? @Har:完全正确;内部表示不使用 2s 补码。 每个“数字”中剩余的 1 或 2 位会发生什么? (因为 16 或 32 位用于存储仅 15 或 30 位的数字)【参考方案2】:

从 longintrepr.h 中,我们看到一个 Python 'int' 对象是用这个 C 结构定义的:

struct _longobject 
        PyObject_VAR_HEAD
        digit ob_digit[1];
;

数字是一个 32 位无符号值。大部分空间由可变大小的对象标头占用。从object.h中,我们可以找到它的定义:

typedef struct 
    PyObject ob_base;
    Py_ssize_t ob_size; /* Number of items in variable part */
 PyVarObject;

typedef struct _object 
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
 PyObject;

我们可以看到我们正在使用 Py_ssize_t,假设 64 位系统为 64 位,将“数字”的计数存储在值中。这可能是浪费。我们还可以看到,一般的对象头有一个 64 位的引用计数,以及一个指向对象类型的指针,这也将是一个 64 位的存储空间。引用计数对于 Python 知道何时释放对象是必要的,指向对象类型的指针对于知道我们有一个 int 而不是,比如说,一个字符串,因为 C 结构无法测试来自任意指针的对象。

_PyObject_HEAD_EXTRA 在大多数 Python 构建中被定义为空,但如果构建启用该选项,则可用于在堆上存储所有 Python 对象的链接列表,使用另外两个 64 位指针。

【讨论】:

以上是关于为啥 int 在 Python 中需要三倍的内存?的主要内容,如果未能解决你的问题,请参考以下文章

PHP:内存优化级联?

为啥我的 C++ 代码比 LeetCode 上的 C 代码慢三倍? [关闭]

比Redis快5倍的中间件,为啥这么快?

数据结构专题

为啥级联 ManualResetEvents 的多次等待会使执行时间增加三倍?

ubuntu 为啥cpu占用率超过100