为什么 1 + 1 不使用 BINARY_ADD?
Why doesn't 1 + 1 use BINARY_ADD?
我这样做:
>>> dis.dis(lambda: 1 + 1)
0 LOAD_CONST 2 (2)
3 RETURN_VALUE
我期待 BINARY_ADD 操作码来执行加法。总和是如何计算的?
Python解释器从内向外解释,即它读取1 + 1
将其计算为2
,然后创建returns 常量 2
的函数对象(注意这里的顺序!)。最后,dis
函数求值。新创建的 lambda 函数对象,它只是 returns 一个 2
.
因此,在创建 lambda 函数对象时已经计算了 1+1
,并且 dis.dis()
函数对解释器读取 1+1
时发生的加法一无所知并将其评估为 2
。
如果你这样做:
>>> dis.dis(lambda: x + 1)
1 0 LOAD_GLOBAL 0 (x)
3 LOAD_CONST 1 (1)
6 BINARY_ADD
7 RETURN_VALUE
您会注意到使用了 BINARY_ADD 指令,因为 x + 1
本身无法进一步简化。
这是 Python 的窥视孔优化器的工作。它在编译期间评估仅使用常量的简单操作,并将结果作为常量存储在生成的字节码中。
/* Fold binary ops on constants.
LOAD_CONST c1 LOAD_CONST c2 BINOP --> LOAD_CONST binop(c1,c2) */
case BINARY_POWER:
case BINARY_MULTIPLY:
case BINARY_TRUE_DIVIDE:
case BINARY_FLOOR_DIVIDE:
case BINARY_MODULO:
case BINARY_ADD:
case BINARY_SUBTRACT:
case BINARY_SUBSCR:
case BINARY_LSHIFT:
case BINARY_RSHIFT:
case BINARY_AND:
case BINARY_XOR:
case BINARY_OR:
if (lastlc >= 2 &&
ISBASICBLOCK(blocks, i-6, 7) &&
fold_binops_on_constants(&codestr[i-6], consts)) {
i -= 2;
assert(codestr[i] == LOAD_CONST);
cumlc = 1;
}
break;
基本上,它会寻找这样的指令
LOAD_CONST c1
LOAD_CONST c2
BINARY_OPERATION
并对其进行评估,并用结果和 LOAD_CONST
指令替换这些指令。引用 comment in the fold_binops_on_constants
function,
/* Replace LOAD_CONST c1. LOAD_CONST c2 BINOP
with LOAD_CONST binop(c1,c2)
The consts table must still be in list form so that the
new constant can be appended.
Called with codestr pointing to the first LOAD_CONST.
Abandons the transformation if the folding fails (i.e. 1+'a').
If the new constant is a sequence, only folds when the size
is below a threshold value. That keeps pyc files from
becoming large in the presence of code like: (None,)*1000.
*/
此特定代码的实际计算发生 in this block、
case BINARY_ADD:
newconst = PyNumber_Add(v, w);
break;
我这样做:
>>> dis.dis(lambda: 1 + 1)
0 LOAD_CONST 2 (2)
3 RETURN_VALUE
我期待 BINARY_ADD 操作码来执行加法。总和是如何计算的?
Python解释器从内向外解释,即它读取1 + 1
将其计算为2
,然后创建returns 常量 2
的函数对象(注意这里的顺序!)。最后,dis
函数求值。新创建的 lambda 函数对象,它只是 returns 一个 2
.
因此,在创建 lambda 函数对象时已经计算了 1+1
,并且 dis.dis()
函数对解释器读取 1+1
时发生的加法一无所知并将其评估为 2
。
如果你这样做:
>>> dis.dis(lambda: x + 1)
1 0 LOAD_GLOBAL 0 (x)
3 LOAD_CONST 1 (1)
6 BINARY_ADD
7 RETURN_VALUE
您会注意到使用了 BINARY_ADD 指令,因为 x + 1
本身无法进一步简化。
这是 Python 的窥视孔优化器的工作。它在编译期间评估仅使用常量的简单操作,并将结果作为常量存储在生成的字节码中。
/* Fold binary ops on constants.
LOAD_CONST c1 LOAD_CONST c2 BINOP --> LOAD_CONST binop(c1,c2) */
case BINARY_POWER:
case BINARY_MULTIPLY:
case BINARY_TRUE_DIVIDE:
case BINARY_FLOOR_DIVIDE:
case BINARY_MODULO:
case BINARY_ADD:
case BINARY_SUBTRACT:
case BINARY_SUBSCR:
case BINARY_LSHIFT:
case BINARY_RSHIFT:
case BINARY_AND:
case BINARY_XOR:
case BINARY_OR:
if (lastlc >= 2 &&
ISBASICBLOCK(blocks, i-6, 7) &&
fold_binops_on_constants(&codestr[i-6], consts)) {
i -= 2;
assert(codestr[i] == LOAD_CONST);
cumlc = 1;
}
break;
基本上,它会寻找这样的指令
LOAD_CONST c1
LOAD_CONST c2
BINARY_OPERATION
并对其进行评估,并用结果和 LOAD_CONST
指令替换这些指令。引用 comment in the fold_binops_on_constants
function,
/* Replace LOAD_CONST c1. LOAD_CONST c2 BINOP
with LOAD_CONST binop(c1,c2)
The consts table must still be in list form so that the
new constant can be appended.
Called with codestr pointing to the first LOAD_CONST.
Abandons the transformation if the folding fails (i.e. 1+'a').
If the new constant is a sequence, only folds when the size
is below a threshold value. That keeps pyc files from
becoming large in the presence of code like: (None,)*1000.
*/
此特定代码的实际计算发生 in this block、
case BINARY_ADD:
newconst = PyNumber_Add(v, w);
break;