常量折叠的具体规则是什么?
What are the specific rules for constant folding?
我刚刚意识到 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
常量折叠没有规则。只有实现细节。他们已经改变了,他们还会再改变。
哎呀,你甚至不能谈论 "Python 3 behavior" 或 "Python 3.6 behavior",因为这些实现细节在 3.6.4 和 3.6 之间发生了变化.5。在 3.6.4 上,2**66
示例被常量折叠。
目前,没有人知道 "for now" 会持续多久,实施细节是 AST 优化器包括防止在常量折叠上花费太多时间或内存的安全措施。 2**66
或4**33
的safeguard是根据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
d为128。由于2
是2位数,4
是3位数,估计结果大小4**33
较小,因此它通过了检查并被常量折叠。
在 Python 3.6.5 上,实现细节大部分相似,但这种常量折叠发生在 bytecode peephole optimizer 而不是 AST 优化器中,后者在 3.6.5 上不存在。
在 Python 3.6.4 上,预检查保护措施不存在。窥孔优化器将 discard 在计算后得出太大的常量折叠结果,这会导致与预检查不同的阈值。
我刚刚意识到 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
常量折叠没有规则。只有实现细节。他们已经改变了,他们还会再改变。
哎呀,你甚至不能谈论 "Python 3 behavior" 或 "Python 3.6 behavior",因为这些实现细节在 3.6.4 和 3.6 之间发生了变化.5。在 3.6.4 上,2**66
示例被常量折叠。
目前,没有人知道 "for now" 会持续多久,实施细节是 AST 优化器包括防止在常量折叠上花费太多时间或内存的安全措施。 2**66
或4**33
的safeguard是根据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
d为128。由于2
是2位数,4
是3位数,估计结果大小4**33
较小,因此它通过了检查并被常量折叠。
在 Python 3.6.5 上,实现细节大部分相似,但这种常量折叠发生在 bytecode peephole optimizer 而不是 AST 优化器中,后者在 3.6.5 上不存在。
在 Python 3.6.4 上,预检查保护措施不存在。窥孔优化器将 discard 在计算后得出太大的常量折叠结果,这会导致与预检查不同的阈值。