Python虚拟机中整型的实现原理是什么

数据结构

在 cpython 内部的 int 类型的实现数据结构如下所示:

typedef struct _longobject PyLongObject;struct _longobject {    PyObject_VAR_HEAD    digit ob_digit[1];};#define PyObject_VAR_HEAD      PyVarObject ob_base;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;

登录后复制

上面的数据结构用图的方式表示出来如下图所示:

Python虚拟机中整型的实现原理是什么

ob_refcnt,表示对象的引用记数的个数,这个对于垃圾回收很有用处,后面我们分析虚拟机中垃圾回收部分在深入分析。

ob_type,表示这个对象的数据类型是什么,在 python 当中有时候需要对数据的数据类型进行判断比如 isinstance, type 这两个关键字就会使用到这个字段。

立即学习“Python免费学习笔记(深入)”;

ob_size,这个字段表示这个整型对象数组 ob_digit 当中一共有多少个元素。

digit 类型其实就是 uint32_t 类型的一个 宏定义,表示 32 位的整型数据。

深入分析 PyLongObject 字段的语意

首先我们知道在 python 当中的整数是不会溢出的,这正是 PyLongObject 使用数组的原因。在 cpython 内部的实现当中,整数有 0 、正数、负数,对于这一点在 cpython 当中有以下几个规定:

ob_size,保存的是数组的长度,ob_size 大于 0 时保存的是正数,当 ob_size 小于 0 时保存的是负数。

ob_digit,保存的是整数的绝对值。在前面我们谈到了,ob_digit 是一个 32 位的数据,但是在 cpython 内部只会使用其中的前 30 位,这只为了避免溢出的问题。

我们下面使用几个例子来深入理解一下上面的规则:

Python虚拟机中整型的实现原理是什么

在上图当中 ob_size 大于 0 ,说明这个数是一个正数,而 ob_digit 指向一个 int32 的数据,数的值等于 10,因此上面这个数表示整数 10 。

Python虚拟机中整型的实现原理是什么

同理 ob_size 小于 0,而 ob_digit 等于 10,因此上图当中的数据表示 -10 。

Python虚拟机中整型的实现原理是什么

上面是一个 ob_digit 数组长度为 2 的例子,上面所表示数据如下所示:

1⋅20+1⋅21+1⋅22+…+1⋅229+0⋅230+0⋅231+1⋅232

因为对于每一个数组元素来说我们只使用前 30 位,因此到第二个整型数据的时候正好对应着 230,大家可以对应着上面的结果了解整个计算过程。

Python虚拟机中整型的实现原理是什么

上面也就很简单了:

−(1⋅20+1⋅21+1⋅22+…+1⋅229+0⋅230+0⋅231+1⋅232)

小整数池

为了避免频繁的创建一些常用的整数,加快程序执行的速度,我们可以将一些常用的整数先缓存起来,如果需要的话就直接将这个数据返回即可。在 cpython 当中相关的代码如下所示:(小整数池当中缓存数据的区间为[-5, 256])

#define NSMALLPOSINTS           257#define NSMALLNEGINTS           5 static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];

登录后复制

Python虚拟机中整型的实现原理是什么

我们使用下面的代码进行测试,看是否使用了小整数池当中的数据,如果使用的话,对于使用小整数池当中的数据,他们的 id() 返回值是一样的,id 这个内嵌函数返回的是 python 对象的内存地址。

>>> a = 1>>> b = 2>>> c = 1>>> id(a), id(c)(4343136496, 4343136496)>>> a = -6>>> c = -6>>> id(a), id(c)(4346020624, 4346021072)>>> a = 257>>> b = 257>>> id(a), id(c)(4346021104, 4346021072)>>>

登录后复制

从上面的结果我们可以看到的是,对于区间[-5, 256]当中的值,id 的返回值确实是一样的,不在这个区间之内的返回值就是不一样的。

我们还可以这个特性实现一个小的 trick,就是求一个 PyLongObject 对象所占的内存空间大小,因为我们可以使用 -5 和 256 这两个数据的内存首地址,然后将这个地址相减就可以得到 261 个 PyLongObject 所占的内存空间大小(注意虽然小整数池当中一共有 262 个数据,但是最后一个数据是内存首地址,并不是尾地址,因此只有 261 个数据),这样我们就可以求一个 PyLongObject 对象的内存大小。

>>> a = -5>>> b = 256>>> (id(b) - id(a)) / 26132.0>>>

登录后复制

从上面的输出结果我们可以看到一个 PyLongObject 对象占 32 个字节。我们可以使用下面的 C 程序查看一个 PyLongObject 真实所占的内存空间大小。

#include "Python.h"#include  int main(){  printf("%ld", sizeof(PyLongObject));  return 0;}

登录后复制

上面的程序的输出结果如下所示:

Python虚拟机中整型的实现原理是什么

上面两个结果是相等的,因此也验证了我们的想法。

从小整数池当中获取数据的核心代码如下所示:

static PyObject *get_small_int(sdigit ival){    PyObject *v;    assert(-NSMALLNEGINTS 

整数的加法实现

如果你了解过大整数加法就能够知道,大整数加法的具体实现过程了,在 cpython 内部的实现方式其实也是一样的,就是不断的进行加法操作然后进行进位操作。

#define Py_ABS(x) ((x) ob_digit[i] + b->ob_digit[i];        // 将低 30 位的数据保存下来        z->ob_digit[i] = carry & PyLong_MASK;        // 将 carry 右移 30 位,如果上面的加法有进位的话 刚好可以在下一次加法当中使用(注意上面的 carry)        // 使用的是 += 而不是 =        carry >>= PyLong_SHIFT; // PyLong_SHIFT = 30    }    // 将剩下的长度保存 (因为 a 的 size 是比 b 大的)    for (; i ob_digit[i];        z->ob_digit[i] = carry & PyLong_MASK;        carry >>= PyLong_SHIFT;    }    // 最后保存高位的进位    z->ob_digit[i] = carry;    return long_normalize(z); // long_normalize 这个函数的主要功能是保证 ob_size 保存的是真正的数据的长度 因为可以是一个正数加上一个负数 size 还变小了} PyLongObject *_PyLong_New(Py_ssize_t size){    PyLongObject *result;    /* 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;    }    // offsetof 会调用 gcc 的一个内嵌函数 __builtin_offsetof     // offsetof(PyLongObject, ob_digit)  这个功能是得到 PyLongObject 对象 字段 ob_digit 之前的所有字段所占的内存空间的大小    result = PyObject_MALLOC(offsetof(PyLongObject, ob_digit) +                             size*sizeof(digit));    if (!result) {        PyErr_NoMemory();        return NULL;    }    // 将对象的 result 的引用计数设置成 1    return (PyLongObject*)PyObject_INIT_VAR(result, &PyLong_Type, size);}  static PyLongObject *long_normalize(PyLongObject *v){    Py_ssize_t j = Py_ABS(Py_SIZE(v));    Py_ssize_t i = j;     while (i > 0 && v->ob_digit[i-1] == 0)        --i;    if (i != j)        Py_SIZE(v) = (Py_SIZE(v) 

登录后复制

以上就是Python虚拟机中整型的实现原理是什么的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2240222.html

(0)
上一篇 2025年2月26日 20:14:28
下一篇 2025年2月26日 20:15:04

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • vscode如何配置python开发环境

    1、安装插件 如下图,安装后打开软件,点击左边最下面的图标,搜索Python,选择列表的第一个插件并点击install安装程序。 2、打开工作目录 如下图,点击左边的 文件图标,再点击“Open Folder”按钮,选择一个文件夹作为工作目…

    2025年4月2日 编程技术
    100
  • notepad如何运行python代码?

    notepad++在程序员使用当中非常普遍流行的,其中运维人员也十分喜欢使用,功能强大,插件多,关键还可以直接运行python脚本,下面给大家介绍一下在notepad++上运行python脚本文件的方法。 Notepad++运行python…

    2025年4月2日 编程技术
    100
  • vscode下python无法F5代码

    现象: 报错:Timeout waiting for debugger connection 在集成终端中显示:‘cmd’ 不是内部或外部命令,也不是可运行的程序或批处理文件。 注意如果调试的时候不选integrated terminal是…

    2025年4月2日
    100
  • vscode如何编译python?

    vscode如何编译python? 1、想要编译Python我们首先要安装python,进入官网(https://www.python.org/downloads/)下载python3 (不要下载python2.7,不就之后就不会再使用低版…

    2025年4月2日 编程技术
    100
  • vscode怎么运行python文件

    1、首先需要确保安装了vscode的python插件,打开python脚本,可以直接拖入,点击文件,点击首选项里的用户设置,这时候会用户设置配置文件。 2、然后在左边文件Ctrl+F搜索Python关键字,找到pythonPath所在行 3…

    2025年4月2日 编程技术
    100
  • 使用Symfony/css-selector库简化前端开发中的CSS选择器转换

    可以通过一下地址学习composer:学习地址 在前端开发过程中,常常需要将 css 选择器转换为 xpath 表达式以便在后端进行处理。然而,手动转换不仅耗时而且容易出错。最近在开发一个需要频繁进行此类转换的项目时,我遇到了这个问题。幸运…

    编程技术 2025年4月2日
    100
  • CentOS下PyTorch如何进行深度学习

    在centos系统上利用pytorch进行深度学习,需要分步操作: 一、PyTorch安装 您可以选择Anaconda或pip两种方式安装PyTorch。 A. Anaconda安装 下载Anaconda: 从Anaconda官方网站下载适…

    编程技术 2025年4月2日
    100
  • CentOS上PyTorch版本怎么选

    在centos系统上安装pytorch,需要仔细选择合适的版本,并考虑以下几个关键因素: 一、系统环境兼容性: 操作系统: 建议使用CentOS 7或更高版本。 CUDA与cuDNN: PyTorch版本与CUDA版本密切相关。例如,PyT…

    编程技术 2025年4月2日
    100
  • CentOS上PyTorch的GPU支持情况如何

    在centos系统上启用pytorch gpu加速,需要安装cuda、cudnn以及pytorch的gpu版本。以下步骤将引导您完成这一过程: CUDA和cuDNN安装 确定CUDA版本兼容性: 使用nvidia-smi命令查看您的NVID…

    编程技术 2025年4月2日
    100
  • CentOS上PyTorch的分布式训练如何操作

    在centos系统上进行pytorch分布式训练,需要按照以下步骤操作: PyTorch安装: 前提是CentOS系统已安装Python和pip。根据您的CUDA版本,从PyTorch官网获取合适的安装命令。 对于仅需CPU的训练,可以使用…

    编程技术 2025年4月2日
    100

发表回复

登录后才能评论