长亭百川云 - 文章详情

解密 Python 的小整数对象池

古明地觉的编程教室

69

2024-07-13

之前分析过浮点数的创建,而整数的创建与之是类似的,都是基于字面量形式的 C 数据创建 Python 数据。还是那句话,Python 底层的 API 设计的很优美,都非常的相似,比如:

  • 创建浮点数可以使用 PyFloat_FromDouble、PyFloat_FromString 等等;

  • 创建整数可以使用 PyLong_FromLong、PyLong_FromDouble、PyLong_FromString 等等。

创建整数的这些函数直接去 Objects/longobject.c 里面查看即可,这里就不多说了,我们重点来看一下小整数对象池。

我们知道整数属于不可变对象,运算之后会创建新的对象。

>>> a = 666  
>>> id(a)  
140018977594800  
>>> a += 1  
>>> id(a)  
140018977588112  
>>> 

显然这种做法一定存在性能缺陷,因为程序运行时会有大量的对象创建和销毁。根据浮点数的经验,我们猜测 Python 应该也对整数使用了缓存池吧。答案是差不多,只不过不是缓存池,而是小整数对象池。

Python 将那些使用频率高的整数预先创建好,而且都是单例模式,这些预先创建好的整数会放在一个静态数组里面,我们称为小整数对象池。如果需要使用的话会直接拿来用,而不用重新创建。注意:这些整数在解释器启动的时候,就已经创建了。

看一下小整数对象池的实现。

// Include/internal/pycore_global_objects.h  
#define _PY_NSMALLPOSINTS     257  
#define _PY_NSMALLNEGINTS     5  
  
struct _Py_static_objects {  
    // 该结构体保存了一些静态分配的对象,比如这里的小整数对象池  
    struct {  
        PyLongObject small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS];  
        // ...  
    } singletons;  
};

结构体的 small_ints 字段便是小整数对象池,它是一个类型为 PyLongObject、长度为 262 的数组,里面保存了 -5 到 256 之间的整数。

然后再来看一下它的初始化过程。

// Include/internal/pycore_runtime_init.h  
#define _PyRuntimeState_INIT(runtime) \  
    { \  
        /* ... */ \  
        .static_objects = { \  
            .singletons = { \  
                .small_ints = _Py_small_ints_INIT, \  
            }, \  
        }, \  
    }  
  
// runtime 保存了程序运行时的环境和行为,比如内存分配、异常处理等等  
// 上面的代码我们只截取了一小部分,可以看到小整数对象池被初始化为 _Py_small_ints_INIT  
  
// Include/internal/pycore_runtime_init_generated.h  
#define _Py_small_ints_INIT { \  
    _PyLong_DIGIT_INIT(-5), \  
    _PyLong_DIGIT_INIT(-4), \  
    _PyLong_DIGIT_INIT(-3), \  
    _PyLong_DIGIT_INIT(-2), \  
    _PyLong_DIGIT_INIT(-1), \  
    _PyLong_DIGIT_INIT(0), \  
    _PyLong_DIGIT_INIT(1), \  
    _PyLong_DIGIT_INIT(2), \  
    /* ... */ \  
    _PyLong_DIGIT_INIT(252), \  
    _PyLong_DIGIT_INIT(253), \  
    _PyLong_DIGIT_INIT(254), \  
    _PyLong_DIGIT_INIT(255), \  
    _PyLong_DIGIT_INIT(256), \  
}

所以解释器在启动的时候就会预先创建一个可以容纳 262 个整数的数组,并会依次初始化值为 -5 到 256 之间的 PyLongObject。当然这只是解释器的默认行为,因为 -5 到 256 之间的整数使用频率最高,但你也可以根据自身情况修改源码,让它缓存更多的整数,以提升效率,当然这也会额外占用一些内存。

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2