为啥 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_size
是 2
或 -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 中需要三倍的内存?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我的 C++ 代码比 LeetCode 上的 C 代码慢三倍? [关闭]