不断折叠的具体规则是什么?

时间:2019-05-02 02:23:49

标签: python python-3.x cpython constantfolding

我刚刚意识到CPython似乎对待常量表达式,它们表示相同的值,而常量折叠方面却有所不同。例如:

>>> import dis
>>> dis.dis('2**66')
  1           0 LOAD_CONST               0 (2)
              2 LOAD_CONST               1 (66)
              4 BINARY_POWER
              6 RETURN_VALUE
>>> dis.dis('4**33')
  1           0 LOAD_CONST               2 (73786976294838206464)
              2 RETURN_VALUE

对于第二个示例,应用恒定折叠,而对于第一个示例,则不是两个折叠都代表相同的值。由于似乎也折叠了以下表达式,所以它似乎与指数的值无关,也与结果的大小无关。

>>> dis.dis('2.0**66')
  1           0 LOAD_CONST               2 (7.378697629483821e+19)
              2 RETURN_VALUE
>>> dis.dis('4**42')
  1           0 LOAD_CONST               2 (19342813113834066795298816)
              2 RETURN_VALUE

为什么前两个表达式会被不同地对待,并且更普遍地讲,CPython进行恒定折叠时遵循哪些具体规则?


经过测试:

$ python3.6 --version
Python 3.6.5 :: Anaconda, Inc.
$ python3.7 --version
Python 3.7.1

1 个答案:

答案 0 :(得分:3)

没有规则,可以不断折叠。只有实现细节。它们以前已经更改,并且将再次更改。

哎呀,您甚至不能谈论“ Python 3行为”或“ Python 3.6行为”,因为这些实现细节在3.6。 4 和3.6。 5之间进行了更改。 。在3.6.4上,2**66示例被固定折叠。

就目前而言,没有人知道“ for now”将持续多久,实现细节是AST优化器包括一些安全措施,以防止在固定折叠上花费过多的时间或内存。 2**664**33的{​​{3}}基于LHS中的位数和RHS的值:

if (PyLong_Check(v) && PyLong_Check(w) && Py_SIZE(v) && Py_SIZE(w) > 0) {
    size_t vbits = _PyLong_NumBits(v);
    size_t wbits = PyLong_AsSize_t(w);
    if (vbits == (size_t)-1 || wbits == (size_t)-1) {
        return NULL;
    }
    if (vbits > MAX_INT_SIZE / wbits) {
        return NULL;
    }
}

MAX_INT_SIZE早于#define,早于128。由于2是2位数字,而4是3位数字,因此估计结果的大小较小4**33,因此它通过了支票并被固定折叠。

在Python 3.6.5上,实现细节基本相似,但是这种不断的折叠发生在safeguard中,而不是在3.6.5中不存在的AST优化器中。

在Python 3.6.4上,预检查保护措施不存在。窥孔优化器在计算出bytecode peephole optimizer的恒定折叠结果后,它们会产生太大的恒定折叠结果,从而导致阈值与预先检查不同。