我刚刚意识到 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
有no rules用于不断折叠。只有实施细节。他们曾经改变过,也将会再次改变。
哎呀,你甚至不能谈论“Python 3 行为”或“Python 3.6 行为”,因为这些实现细节在 3.6 之间发生了变化。4和3.6。5。在 3.6.4 上,2**66
示例被不断折叠。
目前,没有人知道“暂时”会持续多久,实现细节是 AST 优化器包含保护措施,以防止在常量折叠上花费太多时间或内存。这保障 https://github.com/python/cpython/blob/v3.7.3/Python/ast_opt.c#L210 for 2**66
or 4**33
基于 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
is #define
d 早为 128。2
是一个 2 位数字并且4
是一个 3 位数字,估计结果大小较小4**33
,因此它通过了检查并被常量折叠。
在 Python 3.6.5 上,实现细节大多相似,但这种常量折叠发生在字节码窥孔优化器 https://github.com/python/cpython/blob/v3.6.5/Python/peephole.c#L303而不是 AST 优化器,3.6.5 中不存在 AST 优化器。
在 Python 3.6.4 上,不存在预检查保护措施。窥孔优化器将discard https://github.com/python/cpython/blob/v3.6.3/Python/peephole.c#L305计算后的常量折叠结果太大,这会导致与预检查不同的阈值。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)