三元运算符与数组?
Ternary operator vs array?
在 C 中,索引数组是否比 ?:
运算符更快?
例如,(const int[]){8, 14}[N > 10]
会比 N > 10? 14 : 8
快吗?
坚持使用三元运算符:
- 更简单
- 输入的字符更少
- 更容易阅读和理解
- 更易于维护
- 这可能不是您应用程序的主要瓶颈
- 对于CPU是一个简单的比较
- 编译器很聪明,如果数组解决方案更快,编译器就会为两种变体生成相同的代码
强制引用(强调我的):
Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered.
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%
— Donald Knuth • 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
编译后停止,不assemble。您最终会得到一个包含生成的汇编代码的 .s
文件。 (-fno…
标志用于禁用我们的示例不需要的额外代码生成)。
ternary.s
汇编代码,删除与方法无关的行:
ternary:
endbr64
cmpl , %edi
movl , %edx
movl , %eax
cmovle %edx, %eax
ret
array:
endbr64
movq .LC0(%rip), %rax
movq %rax, -8(%rsp)
xorl %eax, %eax
cmpl , %edi
setg %al
movl -8(%rsp,%rax,4), %eax
ret
.LC0:
.long 8
.long 14
如果比较它们,您会发现数组版本的指令多得多:6 条指令与 4 条指令。
没有理由编写每个开发人员都必须阅读两次的更复杂的代码;更短和直接的代码编译成更高效的机器代码。
尽管有优化级别,但使用复合文字(和一般数组)的效率将大大降低,因为(由当前的现实世界编译器创建)数组。更糟糕的是,它们是在堆栈上创建的,而不仅仅是索引静态常量数据(这仍然比像 x86 cmov
或 AArch64 [=13= 这样的 ALU select 操作更慢,至少延迟更高] 大多数现代 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
在 C 中,索引数组是否比 ?:
运算符更快?
例如,(const int[]){8, 14}[N > 10]
会比 N > 10? 14 : 8
快吗?
坚持使用三元运算符:
- 更简单
- 输入的字符更少
- 更容易阅读和理解
- 更易于维护
- 这可能不是您应用程序的主要瓶颈
- 对于CPU是一个简单的比较
- 编译器很聪明,如果数组解决方案更快,编译器就会为两种变体生成相同的代码
强制引用(强调我的):
Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%
— Donald Knuth • 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
编译后停止,不assemble。您最终会得到一个包含生成的汇编代码的 .s
文件。 (-fno…
标志用于禁用我们的示例不需要的额外代码生成)。
ternary.s
汇编代码,删除与方法无关的行:
ternary:
endbr64
cmpl , %edi
movl , %edx
movl , %eax
cmovle %edx, %eax
ret
array:
endbr64
movq .LC0(%rip), %rax
movq %rax, -8(%rsp)
xorl %eax, %eax
cmpl , %edi
setg %al
movl -8(%rsp,%rax,4), %eax
ret
.LC0:
.long 8
.long 14
如果比较它们,您会发现数组版本的指令多得多:6 条指令与 4 条指令。 没有理由编写每个开发人员都必须阅读两次的更复杂的代码;更短和直接的代码编译成更高效的机器代码。
尽管有优化级别,但使用复合文字(和一般数组)的效率将大大降低,因为(由当前的现实世界编译器创建)数组。更糟糕的是,它们是在堆栈上创建的,而不仅仅是索引静态常量数据(这仍然比像 x86 cmov
或 AArch64 [=13= 这样的 ALU select 操作更慢,至少延迟更高] 大多数现代 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