8种机械键盘轴体对比
本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?
看这段简单但不完全平凡的代码:1
2
3
4
5
6
7
8
9
10
11// Filename: 0.cvoid f(int n) {
f(++n);
}
int main() {
int i = 0;
for(;;++i) {
f(i*i);
}
}
如果用 clang 0.c 编译,进程会很快因为 f 这个函数无限递归导致栈溢出而终止。
如果用 clang -O3 0.c 编译,则进程会一直停留在死循环里,不会终止。
未经优化得到的汇编代码是1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56.section__TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 11
.globl_f
.align4, 0x90
_f: ## @f
.cfi_startproc
## BB#0:
pushq%rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq%rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
subq$16, %rsp
movl%edi, -4(%rbp)
movl-4(%rbp), %edi
addl$1, %edi
movl%edi, -4(%rbp)
callq_f
addq$16, %rsp
popq%rbp
retq
.cfi_endproc
.globl_main
.align4, 0x90
_main: ## @main
.cfi_startproc
## BB#0:
pushq%rbp
Ltmp3:
.cfi_def_cfa_offset 16
Ltmp4:
.cfi_offset %rbp, -16
movq%rsp, %rbp
Ltmp5:
.cfi_def_cfa_register %rbp
subq$16, %rsp
movl$0, -4(%rbp)
movl$0, -8(%rbp)
LBB1_1: ## =>This Inner Loop Header: Depth=1
movl-8(%rbp), %eax
imull-8(%rbp), %eax
movl%eax, %edi
callq_f
## BB#2: ## in Loop: Header=BB1_1 Depth=1
movl-8(%rbp), %eax
addl$1, %eax
movl%eax, -8(%rbp)
jmpLBB1_1
.cfi_endproc
.subsections_via_symbols
O3 优化后对应的汇编代码是1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39.section__TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 11
.globl_f
.align4, 0x90
_f: ## @f
.cfi_startproc
## BB#0:
pushq%rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq%rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
popq%rbp
retq
.cfi_endproc
.globl_main
.align4, 0x90
_main: ## @main
.cfi_startproc
## BB#0:
pushq%rbp
Ltmp3:
.cfi_def_cfa_offset 16
Ltmp4:
.cfi_offset %rbp, -16
movq%rsp, %rbp
Ltmp5:
.cfi_def_cfa_register %rbp
.align4, 0x90
LBB1_1: ## =>This Inner Loop Header: Depth=1
jmpLBB1_1
.cfi_endproc
.subsections_via_symbols
可见优化的版本里最后执行的是一个简单的死循环 (34-35 行),对函数 f 的调用被完全移除了,尽管还是“假模假式”地定义了 f (什么功能都没有,也没有递归调用的部分)。
至于 gcc (clang) 是怎么推断出 f 这个函数虽然在做事,但做的事是“封闭的”、对外界没有“逻辑上的”副作用 (尽管有事实上的副作用,那就是栈溢出),可以在这里看到一些端倪。想了想,也许是通过把递归转换为循环可以看出 f 这个函数没有副作用,所以就移除了。
那个页面提到 LLVM 会为了方便后续分析而把1for (i = 7; i*i < 1000; ++i)
变成1for (i = 0; i != 25; ++i)
真没想到它会做这样的变换。