三元运算符与数组?

时间:2021-02-08 16:39:09

标签: arrays c performance conditional-operator micro-optimization

在 C 中,索引数组是否比 ?: 运算符更快?

例如,(const int[]){8, 14}[N > 10] 会比 N > 10? 14 : 8 快吗?

2 个答案:

答案 0 :(得分:5)

坚持使用三元运算符:

  • 更简单
  • 输入的字符更少
  • 更容易阅读和理解
  • 更易于维护
  • 这可能不是您应用程序中的主要瓶颈
  • 对于 CPU 来说,这是一个简单的比较
  • 编译器很聪明,如果数组解决方案更快,编译器就会为两种变体生成相同的代码

强制报价(强调我的):

<块引用>

程序员浪费大量时间考虑或担心他们程序中非关键部分的速度,而这些提高效率的尝试实际上在考虑调试和维护时会产生强烈的负面影响。 我们应该忘记小效率,比如大约 97% 的时间:过早的优化是万恶之源。然而,我们不应该在关键的 3% 中放弃我们的机会

——唐纳德·克努斯 • https://wiki.c2.com/?PrematureOptimization


现在,让我们比较一下编译器实际生成的内容。

#include <stdlib.h>
int ternary(int n) { return n > 10 ? 14 : 8; }
int array(int n) { return (const int[]){8, 14}[n > 10]; }

在 Ubuntu 中使用 (g)cc 10.2.1 编译并启用优化:

$ cc -O3 -S -fno-stack-protector -fno-asynchronous-unwind-tables ternary.c

-S 在编译后停止并且不组装。您最终会得到一个包含生成的汇编代码的 .s 文件。 (-fno… 标志用于禁用我们的示例不需要的额外代码生成)。

ternary.s 汇编代码,与删除的方法无关的行:

ternary:
    endbr64
    cmpl    $10, %edi
    movl    $8, %edx
    movl    $14, %eax
    cmovle  %edx, %eax
    ret
array:
    endbr64
    movq    .LC0(%rip), %rax
    movq    %rax, -8(%rsp)
    xorl    %eax, %eax
    cmpl    $10, %edi
    setg    %al
    movl    -8(%rsp,%rax,4), %eax
    ret
.LC0:
    .long   8
    .long   14

如果您比较它们,您会注意到阵列版本的指令更多:6 条指令与 4 条指令。 没有理由编写每个开发人员都必须阅读两次的更复杂的代码;较短且直接的代码编译为更高效的机器代码。

答案 1 :(得分:2)

尽管优化级别是创建数组(由当前的实际编译器),因此使用复合文字(以及一般的数组)效率会低得多。更糟糕的是,它们是在堆栈上创建的,而不仅仅是索引静态常量数据(与最现代的 x86 cmov 或 AArch64 csel 等 ALU 选择操作相比,它仍然会更慢,至少延迟更高ISA 有)。

我已经使用我使用的所有编译器(包括 Keil 和 IAR)和一些我不使用的编译器(icc 和 clang)对其进行了测试。

int foo(int N)
{
    return (const int[]){8, 14}[N > 10]; 
}

int bar(int N)
{
    return  N > 10? 14 : 8;
}
foo:
        mov     rax, QWORD PTR .LC0[rip]     # load 8 bytes from .rodata
        mov     QWORD PTR [rsp-8], rax       # store both elements to the stack
        xor     eax, eax                      # prepare a zeroed reg for setcc
        cmp     edi, 10
        setg    al                            # materialize N>10 as a 0/1 integer
        mov     eax, DWORD PTR [rsp-8+rax*4]  # index the array with it
        ret

bar:
        cmp     edi, 10
        mov     edx, 8               # set up registers with both constants
        mov     eax, 14
        cmovle  eax, edx             # ALU select operation on FLAGS from CMP
        ret
.LC0:
        .long   8
        .long   14

https://godbolt.org/z/qK65Gv