Cpython3.9源码分析python中的大小整数

其他教程   发布日期:2025年03月29日   浏览次数:106

这篇“Cpython3.9源码分析python中的大小整数”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Cpython3.9源码分析python中的大小整数”文章吧。

小整数

  1. /* interpreter state */
  2. #define _PY_NSMALLPOSINTS 257
  3. #define _PY_NSMALLNEGINTS 5

这是

  1. CPython
中定义的两个常量,它们用于控制解释器状态中的小整数对象池。在
  1. CPython
中,小整数对象池是一种优化机制,用于减少对常用小整数的内存分配和销毁开销。

  1. _PY_NSMALLPOSINTS
定义了正小整数对象池的大小。在这里,其值设置为257,表示解释器将为从0到256(包含0和256)的整数预分配对象并缓存。这些整数在很多场景下会被频繁使用,所以事先创建并缓存它们可以提高性能。

  1. _PY_NSMALLNEGINTS
定义了负小整数对象池的大小。在这里,其值设置为5,表示解释器将为从-1到-5(包含-1和-5)的整数预分配对象并缓存。

在Python解释器启动时,这些小整数对象会被创建并放入对象池。当需要这些整数值时,解释器会直接从对象池中获取对应的对象,而不是动态创建新对象。这样,对于这些小整数值的操作可以更快地进行,节省了内存分配和销毁的开销。

  1. static PyObject *
  2. get_small_int(sdigit ival)
  3. {
  4. assert(IS_SMALL_INT(ival));
  5. PyThreadState *tstate = _PyThreadState_GET();
  6. PyObject *v = (PyObject*)tstate->interp->small_ints[ival + NSMALLNEGINTS];
  7. Py_INCREF(v);
  8. return v;
  9. }
  10. typedef int32_t sdigit; /* signed variant of digit */
  11. #define IS_SMALL_INT(ival) (-NSMALLNEGINTS <= (ival) && (ival) < NSMALLPOSINTS)

这是

  1. get_small_int
函数的实现,它用于从小整数对象池中获取一个指定值的小整数对象。小整数对象池包含了一定范围内的整数对象,主要是为了避免对这些常用的整数对象进行频繁的内存分配和销毁。

  1. get_small_int
函数接受一个
  1. sdigit
类型的参数
  1. ival
,表示要获取的整数值。在函数内部,首先使用
  1. assert(IS_SMALL_INT(ival))
确保传入的整数值
  1. ival
在小整数对象池的范围内。

接下来,函数获取当前线程状态(

  1. PyThreadState
)并从其中获取解释器状态(
  1. tstate->interp
)。解释器状态包含了小整数对象池,即
  1. small_ints
数组

然后,根据

  1. ival
计算出在
  1. small_ints
数组中的索引(
  1. ival + NSMALLNEGINTS
),并将对应位置的对象赋值给
  1. v
  1. NSMALLNEGINTS
是一个宏定义,表示负小整数的个数。假设我们有一个整数值
  1. ival
,我们想要在
  1. small_ints
数组中查找这个值对应的预分配的小整数对象。
  1. NSMALLNEGINTS
是预分配的负数的数量。在
  1. CPython
中,
  1. NSMALLNEGINTS
的值通常为5,表示有5个预分配的负整数对象(-1, -2, -3, -4, -5)。

现在,我们将通过计算

  1. ival + NSMALLNEGINTS
来找到
  1. small_ints
数组中的索引。例如,假设
  1. ival
为3。那么,我们可以计算索引如下:
  1. index = ival + NSMALLNEGINTS
  2. index = 3 + 5
  3. index = 8

这意味着

  1. small_ints
数组中的第8个元素(从0开始计数)是我们要查找的整数对象。在这个例子中,我们将找到预分配的小整数对象3,并将其引用计数加1,然后返回这个对象。

接下来,通过调用

  1. Py_INCREF(v)
增加
  1. v
的引用计数,以防止对象在其引用计数变为0时被错误地回收。

最后,返回指向小整数对象的指针

  1. v

总之,

  1. get_small_int
函数的作用是从小整数对象池中获取一个指定值的小整数对象,并增加其引用计数,然后返回该对象。这样可以提高对常用小整数的操作性能。

大整数

  1. /* Long integer representation.
  2. The absolute value of a number is equal to
  3. SUM(for i=0 through abs(ob_size)-1) ob_digit[i] * 2**(SHIFT*i)
  4. Negative numbers are represented with ob_size < 0;
  5. zero is represented by ob_size == 0.
  6. In a normalized number, ob_digit[abs(ob_size)-1] (the most significant
  7. digit) is never zero. Also, in all cases, for all valid i,
  8. 0 <= ob_digit[i] <= MASK.
  9. The allocation function takes care of allocating extra memory
  10. so that ob_digit[0] ... ob_digit[abs(ob_size)-1] are actually available.
  11. CAUTION: Generic code manipulating subtypes of PyVarObject has to
  12. aware that ints abuse ob_size's sign bit.
  13. */

这是

  1. CPython
源码中关于长整数表示的一段注释。它解释了
  1. PyLongObject
如何表示大整数的绝对值和符号。让我们逐行分析这个注释:

1.首先,注释指出大整数的绝对值等于:

  1. SUM(for i=0 through abs(ob_size)-1) ob_digit[i] * 2**(SHIFT*i)

  1. ob_digit
表示长整数的每个“数字”,
  1. SHIFT
是每个“数字”的位数,通常为30或15。
  1. ob_size
表示长整数的符号和长度,它的绝对值表示长整数的长度,即“数字”的个数。

2.对于负数,

  1. ob_size
小于0。对于0,
  1. ob_size
等于0。

3.在规范化的数中,最高有效位(即最高“数字”)永远不会为零。此外,在所有情况下,对于所有有效的i,

  1. ob_digit[i]
的取值范围在0到
  1. MASK
之间。
  1. MASK
的值通常为
  1. (1 << PyLong_SHIFT) - 1
,即
  1. 2**PyLong_SHIFT - 1

4.注释还提到分配函数负责分配额外的内存,以确保

  1. ob_digit[0]
  1. ob_digit[abs(ob_size)-1]
实际上是可用的。

5.最后,注释中的“警告”部分提醒开发者,操纵

  1. PyVarObject
子类型的通用代码需要注意整数会滥用
  1. ob_size
的符号位。这是因为
  1. ob_size
的符号位同时表示整数的长度和符号,而通常情况下
  1. ob_size
仅用于表示长度。

额外解释

  1. ob_digit
是一个表示大整数中每个 “数字” 的数组,它是一个整数数组,用于表示长整数对象(
  1. PyLongObject
)中的整数值。每个 “数字” 都有一个固定的位数,由
  1. PyLong_SHIFT
定义(通常为 30 或 15)。例如,假设我们有一个长整数对象,其值为 12345678901234567890。

在这个例子中,假设

  1. PyLong_SHIFT
为 30,这意味着每个 “数字” 可以表示 2^30 = 1073741824 个不同的值。为了将这个大整数表示为
  1. ob_digit
数组,我们需要将整数拆分为基于 2^30 的 “数字”。在这种情况下,我们可以将整数表示为:
  1. 12345678901234567890 = 4 * 2^(30*2) + 726238597 * 2^(30*1) + 1026062870 * 2^(30*0)

所以,

  1. ob_digit
数组将包含以下元素:
  1. ob_digit[0] = 1026062870
  2. ob_digit[1] = 726238597
  3. ob_digit[2] = 4

在实际的

  1. CPython
源码中,
  1. PyLongObject
的定义如下:
  1. typedef struct {
  2. PyObject_VAR_HEAD
  3. digit ob_digit[1];
  4. } PyLongObject;

在这里,

  1. ob_digit
是一个长度为1的数组,但实际上,它是一个可变长度数组,根据所需的 “数字” 数量动态分配。要注意的是,当一个
  1. PyLongObject
被创建时,会根据整数值的大小动态分配适当数量的空间来存储
  1. ob_digit
数组。

以上就是Cpython3.9源码分析python中的大小整数的详细内容,更多关于Cpython3.9源码分析python中的大小整数的资料请关注九品源码其它相关文章!