为什么cython不编译逻辑或`||`表达式?
Why don't cython compile logic or to `||` expression?
例如,这里有一个或表达式:
c = f1 == 0 or f1 - f0 > th
编译后的C代码如下:
__pyx_t_24 = (__pyx_v_f1 == 0);
if (!__pyx_t_24) {
} else {
__pyx_t_23 = __pyx_t_24;
goto __pyx_L5_bool_binop_done;
}
__pyx_t_24 = ((__pyx_v_f1 - __pyx_v_f0) > __pyx_v_th);
__pyx_t_23 = __pyx_t_24;
__pyx_L5_bool_binop_done:;
__pyx_v_c = __pyx_t_23;
为什么不输出这个?
__pyx_v_c = (__pyx_v_f1 == 0) || ((__pyx_v_f1 - __pyx_v_f0) > __pyx_v_th)
goto 版本比 ||
快吗?
使用以下两个文件:
test.c
:
int main(int argc, char** argv) {
int c, f0, f1, th;
int hold, hold1;
f0 = (int) argv[1];
f1 = (int) argv[2];
th = (int) argv[3];
hold1 = (f0 == 0);
if (!hold1) {
} else {
hold = hold1;
goto done;
}
hold1 = (f1 - f0 > th);
hold = hold1;
done: c = hold;
return c;
}
test2.c
:
int main(int argc, char** argv) {
int c, f0, f1, th;
f0 = (int) argv[1];
f1 = (int) argv[2];
th = (int) argv[3];
c = (f1 == 0) || (f1 - f0 > th);
return c;
}
我必须将 f0
、f1
和 th
分配给某些东西,这样编译器就不会只是 return 1
,因为 C 规范指出 int
s 被初始化为 0
并且 f1 == 0
将产生 true
,因此整个布尔语句将产生 true
并且程序集将是:
main:
.LFB0:
.cfi_startproc
.L2:
movl , %eax
ret
.cfi_endproc
使用带有 -S -O2
标志的 GCC
进行编译(启用优化),test.s
和 test2.s
都变成:
main:
.LFB0:
.cfi_startproc
movl 8(%rsi), %edx
movq 16(%rsi), %rdi
movl , %eax
movq 24(%rsi), %rcx
testl %edx, %edx
je .L2
subl %edx, %edi
xorl %eax, %eax
cmpl %ecx, %edi
setg %al
.L2:
rep
ret
.cfi_endproc
因此,除非您禁用优化,其中带有 goto
的指令将多出大约 50% 的指令,否则结果将是相同的。
输出 C
代码难看的原因是解释器访问 AST 中节点的方式。当访问 or
节点时,解释器首先计算第一个参数,然后计算第二个。如果布尔表达式复杂得多,解析起来就会容易得多。想象一下调用一个 returns 布尔值的 lambda 函数(我不确定 Cython 是否支持这个);口译员必须遵循以下结构:
hold = ... evaluate the lambda expression...
if (hold) {
result = hold;
goto done; // short circuit
}
hold = ... evaluate the second boolean expression...
done:
...
在解释阶段进行优化将是一项艰巨的任务,因此 Cython 甚至不会打扰。
如果我对 C 的理解是正确的(而且我上次使用 C 是多年前的事,所以它可能已经生锈了)'||'仅限 C 中的 (OR) 运算符 return 布尔值(即 0 表示 False 或 1 表示 True)。如果这是正确的,那么这与 goto 是快还是慢无关。
||会给出与 goto 代码不同的结果。这是因为 'or' 在 Python 中的工作原理,让我们举个例子 -
c = a or b
在上面的语句中,首先评估 a
s 值,如果它是一个真值,则该值是从 or 表达式 returned(不是真或 1,但 a
s value) ,如果该值为 false(如 Python 中的 false 值为 0 、空字符串、空列表、False
等),则评估 b 的值为 return编辑。请注意 'or' return 是最后评估的值,而不是 True
(1) 或 False
(0).
当您想要设置默认值时,这基本上很有用 -
s = d or 'default value'
例如,这里有一个或表达式:
c = f1 == 0 or f1 - f0 > th
编译后的C代码如下:
__pyx_t_24 = (__pyx_v_f1 == 0);
if (!__pyx_t_24) {
} else {
__pyx_t_23 = __pyx_t_24;
goto __pyx_L5_bool_binop_done;
}
__pyx_t_24 = ((__pyx_v_f1 - __pyx_v_f0) > __pyx_v_th);
__pyx_t_23 = __pyx_t_24;
__pyx_L5_bool_binop_done:;
__pyx_v_c = __pyx_t_23;
为什么不输出这个?
__pyx_v_c = (__pyx_v_f1 == 0) || ((__pyx_v_f1 - __pyx_v_f0) > __pyx_v_th)
goto 版本比 ||
快吗?
使用以下两个文件:
test.c
:
int main(int argc, char** argv) {
int c, f0, f1, th;
int hold, hold1;
f0 = (int) argv[1];
f1 = (int) argv[2];
th = (int) argv[3];
hold1 = (f0 == 0);
if (!hold1) {
} else {
hold = hold1;
goto done;
}
hold1 = (f1 - f0 > th);
hold = hold1;
done: c = hold;
return c;
}
test2.c
:
int main(int argc, char** argv) {
int c, f0, f1, th;
f0 = (int) argv[1];
f1 = (int) argv[2];
th = (int) argv[3];
c = (f1 == 0) || (f1 - f0 > th);
return c;
}
我必须将 f0
、f1
和 th
分配给某些东西,这样编译器就不会只是 return 1
,因为 C 规范指出 int
s 被初始化为 0
并且 f1 == 0
将产生 true
,因此整个布尔语句将产生 true
并且程序集将是:
main:
.LFB0:
.cfi_startproc
.L2:
movl , %eax
ret
.cfi_endproc
使用带有 -S -O2
标志的 GCC
进行编译(启用优化),test.s
和 test2.s
都变成:
main:
.LFB0:
.cfi_startproc
movl 8(%rsi), %edx
movq 16(%rsi), %rdi
movl , %eax
movq 24(%rsi), %rcx
testl %edx, %edx
je .L2
subl %edx, %edi
xorl %eax, %eax
cmpl %ecx, %edi
setg %al
.L2:
rep
ret
.cfi_endproc
因此,除非您禁用优化,其中带有 goto
的指令将多出大约 50% 的指令,否则结果将是相同的。
输出 C
代码难看的原因是解释器访问 AST 中节点的方式。当访问 or
节点时,解释器首先计算第一个参数,然后计算第二个。如果布尔表达式复杂得多,解析起来就会容易得多。想象一下调用一个 returns 布尔值的 lambda 函数(我不确定 Cython 是否支持这个);口译员必须遵循以下结构:
hold = ... evaluate the lambda expression...
if (hold) {
result = hold;
goto done; // short circuit
}
hold = ... evaluate the second boolean expression...
done:
...
在解释阶段进行优化将是一项艰巨的任务,因此 Cython 甚至不会打扰。
如果我对 C 的理解是正确的(而且我上次使用 C 是多年前的事,所以它可能已经生锈了)'||'仅限 C 中的 (OR) 运算符 return 布尔值(即 0 表示 False 或 1 表示 True)。如果这是正确的,那么这与 goto 是快还是慢无关。
||会给出与 goto 代码不同的结果。这是因为 'or' 在 Python 中的工作原理,让我们举个例子 -
c = a or b
在上面的语句中,首先评估 a
s 值,如果它是一个真值,则该值是从 or 表达式 returned(不是真或 1,但 a
s value) ,如果该值为 false(如 Python 中的 false 值为 0 、空字符串、空列表、False
等),则评估 b 的值为 return编辑。请注意 'or' return 是最后评估的值,而不是 True
(1) 或 False
(0).
当您想要设置默认值时,这基本上很有用 -
s = d or 'default value'