PyF*_*Fox 8 python cpython virtual-machine python-3.x
我试图弄清楚Python 3(使用CPython作为解释器)如何执行其程序.我发现步骤是:
通过CPython编译器将Python源代码(.py文件)编译为Python字节码(.pyc)文件.在导入任何模块的情况下,保存.pyc文件,如果运行一个main.py Python脚本,则不保存它们.
Python虚拟机将字节码解释为硬件特定的机器码.
在这里找到一个很好的答案/sf/answers/121266841/说,与JVM相比,Python虚拟机运行其字节码所需的时间更长,因为java字节码包含有关数据类型的信息,而Python虚拟机解释了一行一个并且必须确定数据类型.
我的问题是Python虚拟机如何确定数据类型并在解释到机器代码期间或在单独的进程(例如会生成另一个中间代码)期间发生?
CPython 的动态、运行时分派(与 Java 的静态、编译时分派相比)只是 Java 比纯 CPython 更快的原因之一:Java 中有 jit 编译、不同的垃圾收集策略、存在原生类型,如int、double与 CPython 中的不可变数据结构等。
我之前的肤浅实验表明,动态调度只负责大约 30% 的运行 - 你无法用它来解释某些数量级因素的速度差异。
为了让这个答案不那么抽象,让我们看一个例子:
def add(x,y):
   return x+y
查看字节码:
import dis
dis.dis(add)
这使:
2         0 LOAD_FAST                0 (x)
          2 LOAD_FAST                1 (y)
          4 BINARY_ADD
          6 RETURN_VALUE
x我们可以看到,在字节码级别上,和是整数、浮点数还是其他东西没有区别y——解释器并不关心。
Java中的情况完全不同:
int add(int x, int y) {return x+y;}
和
float add(float x, float y) {return x+y;}
将导致完全不同的操作码,并且调用调度将在编译时发生 - 根据编译时已知的静态类型选择正确的版本。
通常,CPython 解释器不必知道参数的确切类型:内部有一个基本的“类/接口”(显然 C 中没有类,因此它被称为“协议”,但对于了解 C++ 的人来说) /Java“接口”可能是正确的心智模型),所有其他“类”都是从中派生的。这个基“类”被称为PyObject,这里是其协议的描述。。因此,只要该函数是该协议/接口的一部分,CPython 解释器就可以调用它,而无需知道确切的类型,并且调用将被分派到正确的实现(很像 C++ 中的“虚拟”函数)。
在纯 Python 方面,变量似乎没有类型:
a=1
a="1"
但是,内部a有一个类型 - 它是PyObject*,并且此引用可以绑定到整数 ( 1) 和 unicode 字符串 ( "1") - 因为它们都“继承”自PyObject。
CPython 解释器有时会尝试找出引用的正确类型,对于上面的示例也是如此 - 当它看到BINARY_ADD-opcode 时,会执行以下 C 代码:
    case TARGET(BINARY_ADD): {
        PyObject *right = POP();
        PyObject *left = TOP();
        PyObject *sum;
        ...
        if (PyUnicode_CheckExact(left) &&
                 PyUnicode_CheckExact(right)) {
            sum = unicode_concatenate(left, right, f, next_instr);
            /* unicode_concatenate consumed the ref to left */
        }
        else {
            sum = PyNumber_Add(left, right);
            Py_DECREF(left);
        }
        Py_DECREF(right);
        SET_TOP(sum);
        if (sum == NULL)
            goto error;
        DISPATCH();
    }
这里解释器查询,两个对象是否都是 unicode 字符串,如果是这种情况,则使用特殊方法(可能更有效,事实上它试图就地更改不可变的 unicode 对象,请参阅此SO-answer) ,否则工作将分派至PyNumber-protocol。
显然,解释器还必须知道创建对象时的确切类型,例如  使用a="1"不同a=1的“类” - 但正如我们所见,它不是唯一的一个地方。
因此解释器会在运行时干扰类型,但大多数时候它不必这样做 - 可以通过动态分派达到目标。
| 归档时间: | 
 | 
| 查看次数: | 644 次 | 
| 最近记录: |