6

我刚刚意识到 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
4

1 回答 1

8

常量折叠没有规则。只有实现细节。他们以前变过,以后还会变。

哎呀,你甚至不能谈论“Python 3 行为”或“Python 3.6 行为”,因为这些实现细节在 3.6 之间发生了变化。4和 3.6。5 . 在 3.6.4 上,该2**66示例被恒定折叠。

目前,没有人知道“暂时”会持续多久,具体实现细节是 AST 优化器包括保护措施,以防止在不断折叠上花费太多时间或内存。or的保障基于LHS 中的位数和 RHS 的值:2**664**33

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#defined 早于 128。由于是22 位数字和43 位数字,因此估计的结果大小较小4**33,因此它通过了检查并得到常数折叠。

在 Python 3.6.5 上,实现细节大多相似,但这种常量折叠发生在字节码窥孔优化器而不是 AST 优化器中,而 AST 优化器在 3.6.5 上不存在。

在 Python 3.6.4 上,不存在预检查保护措施。窥孔优化器将在计算后丢弃太大的常数折叠结果,这导致与预检查不同的阈值。

于 2019-05-02T02:50:55.597 回答