为什么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;
}

我必须将 f0f1th 分配给某些东西,这样编译器就不会只是 return 1,因为 C 规范指出 ints 被初始化为 0 并且 f1 == 0 将产生 true,因此整个布尔语句将产生 true 并且程序集将是:

main:
.LFB0:
    .cfi_startproc
.L2:
    movl    , %eax
    ret
    .cfi_endproc

使用带有 -S -O2 标志的 GCC 进行编译(启用优化),test.stest2.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

在上面的语句中,首先评估 as 值,如果它是一个真值,则该值是从 or 表达式 returned(不是真或 1,但 as value) ,如果该值为 false(如 Python 中的 false 值为 0 、空字符串、空列表、False 等),则评估 b 的值为 return编辑。请注意 'or' return 是最后评估的值,而不是 True(1) 或 False(0).

当您想要设置默认值时,这基本上很有用 -

s = d or 'default value'