了解 Python 中大整数的内存分配

Posted

技术标签:

【中文标题】了解 Python 中大整数的内存分配【英文标题】:Understanding memory allocation for large integers in Python 【发布时间】:2017-03-13 15:40:19 【问题描述】:

Python 如何为大整数分配内存?

int 类型的大小为28 bytes,随着我不断增加int 的值,大小以4 bytes 为增量增加。

    为什么 28 bytes 最初用于任何低至 1 的值?

    为什么要增加4 bytes

PS:我在 x86_64(64 位机器)上运行 Python 3.5.2。我正在寻找关于(3.0+)解释器如何处理如此庞大的数字的任何指针/资源/PEP。

说明尺寸的代码:

>>> a=1
>>> print(a.__sizeof__())
28
>>> a=1024
>>> print(a.__sizeof__())
28
>>> a=1024*1024*1024
>>> print(a.__sizeof__())
32
>>> a=1024*1024*1024*1024
>>> print(a.__sizeof__())
32
>>> a=1024*1024*1024*1024*1024*1024
>>> a
1152921504606846976
>>> print(a.__sizeof__())
36

【问题讨论】:

【参考方案1】:

为什么28 字节最初用于任何低至1 的值?

我完全相信@bgusach answered that; Python 使用C 结构体来表示Python 世界中的对象,任何对象including ints:

struct _longobject 
    PyObject_VAR_HEAD
    digit ob_digit[1];
;

PyObject_VAR_HEAD 是一个宏,在展开时会在结构中添加另一个字段(字段PyVarObject,专门用于具有某种长度概念的对象),ob_digits 是一个包含数字值的数组.大小的样板来自该结构,用于小 大 Python 数字。

为什么要增加4 字节?

因为,当创建更大的数字时,大小(以字节为单位)是sizeof(digit) 的倍数;你可以看到在_PyLong_New 中为新的longobject 分配内存是用PyObject_MALLOC 执行的:

/* Number of bytes needed is: offsetof(PyLongObject, ob_digit) +
   sizeof(digit)*size.  Previous incarnations of this code used
   sizeof(PyVarObject) instead of the offsetof, but this risks being
   incorrect in the presence of padding between the PyVarObject header
   and the digits. */
if (size > (Py_ssize_t)MAX_LONG_DIGITS) 
    PyErr_SetString(PyExc_OverflowError,
                    "too many digits in integer");
    return NULL;

result = PyObject_MALLOC(offsetof(PyLongObject, ob_digit) +
                         size*sizeof(digit));

offsetof(PyLongObject, ob_digit) 是与保存其值无关的长对象的“样板”(以字节为单位)。

digit 定义在将struct _longobject 作为typedef 用于uint32 的头文件中:

typedef uint32_t digit;

sizeof(uint32_t)4 字节。这就是当 _PyLong_Newsize 参数增加时,您会看到字节大小增加的数量。


当然,这正是CPython 选择实现它的方式。这是一个实现细节,因此您不会在 PEP 中找到太多信息。如果您能找到相应的线程,python-dev 邮件列表将举行实施讨论:-)。

无论哪种方式,您可能会在其他流行的实现中发现不同的行为,所以不要认为这是理所当然的。

【讨论】:

【参考方案2】:

其实很简单。 Python 的int 不是您可能从其他语言中习惯的那种原语,而是一个完整的对象,包括它的方法和所有东西。这就是开销的来源。

然后,您就拥有了有效负载本身,即被表示的整数。没有限制,除了你的记忆。

Python 的int 的大小是表示数字所需的大小加上一点开销。

如果您想进一步阅读,请查看relevant part of the documentation:

整数具有无限精度

【讨论】:

谢谢。是否有 PEP 解释了在最近的 Python 版本(3.0+)中这是如何完成的? @Vigneshwaren 这是您使用的任何解释器的实现细节。 Python 语言仅保证 int 具有任意精度,而不是如何实现。 @Vigneshwaren:您可以从sys.int_infolong_info on 2.7)查看 CPython 的基本信息。基本上,每个绝对大小(符号无关)的sys.int_info.bits_per_digit 或其一部分都需要额外的sys.int_info.sizeof_digit 字节来存储。注意:小的ints 缓存在 CPython 中,因此作为实现细节,从 (IIRC) -5 到 256 的值是单例;您只需为引用它们的指针支付 4-8 个字节,而不是对象本身的成本。

以上是关于了解 Python 中大整数的内存分配的主要内容,如果未能解决你的问题,请参考以下文章

由于 Objective-C 中的整数溢出,处理和报告内存分配错误的最佳方法是啥?

全局和局部变量的内存分配

Python GTK 内存分配问题

将内存和存储分配到指针中

JAVA堆内存和栈内存初步了解

蜡炬教育:如何处理机器学习中大型数据的加载问题?