我想可能与以前的SO问题有些重叠,但我找不到关于这个主题的Delphi特定问题。
假设您要检查无符号的32位整数变量“MyAction”是否等于任何常量ACTION1,ACTION2,...,ACTIONn,其中n是 - 比如说1000.我想这是更优雅,
case MyAction of
ACTION1: {code};
ACTION2: {code};
...
ACTIONn: {code};
end;
比
快得多if MyAction = ACTION1 then
// code
else if MyAction = ACTION2 then
// code
...
else if MyAction = ACTIONn then
// code;
我猜if if变量需要时间O(n)来完成(即找到正确的动作)如果正确的动作ACTIONi具有高的i值,而case变量需要花费更少的时间(O(1) )?)。
答案 0 :(得分:19)
首先检查现实总是好的......
Delphi 2010编译器似乎很喜欢测试和分支。例如,以下简单代码不会编译到分支表中。
var
c: (aaa, bbb, ccc);
begin
case c of
aaa: sleep(0);
bbb: sleep(0);
ccc: sleep(0);
end;
end.
编译器将生成以下代码:
Project56.dpr.24: case c of
0040A1C4 0FB6053C0E4100 movzx eax,[$00410e3c]
0040A1CB 2C01 sub al,$01
0040A1CD 7208 jb $0040a1d7
0040A1CF 740F jz $0040a1e0
0040A1D1 FEC8 dec al
0040A1D3 7414 jz $0040a1e9
0040A1D5 EB19 jmp $0040a1f0
Project56.dpr.25: aaa: sleep(0);
0040A1D7 6A00 push $00
0040A1D9 E86EDAFFFF call Sleep
0040A1DE EB10 jmp $0040a1f0
Project56.dpr.26: bbb: sleep(0);
0040A1E0 6A00 push $00
0040A1E2 E865DAFFFF call Sleep
0040A1E7 EB07 jmp $0040a1f0
Project56.dpr.27: ccc: sleep(0);
0040A1E9 6A00 push $00
0040A1EB E85CDAFFFF call Sleep
更复杂的案例将被编译成一个测试和跳转系列。例如......
var
c: (aaa, bbb, ccc, eee, fff, ggg, hhh);
begin
case c of
aaa: sleep(0);
bbb: sleep(0);
ccc: sleep(0);
hhh: sleep(0);
end;
end.
...编译成......
Project56.dpr.24: case c of
0040A1C4 0FB6053C0E4100 movzx eax,[$00410e3c]
0040A1CB 2C01 sub al,$01
0040A1CD 720C jb $0040a1db
0040A1CF 7413 jz $0040a1e4
0040A1D1 FEC8 dec al
0040A1D3 7418 jz $0040a1ed
0040A1D5 2C04 sub al,$04
0040A1D7 741D jz $0040a1f6
0040A1D9 EB22 jmp $0040a1fd
Project56.dpr.25: aaa: sleep(0);
0040A1DB 6A00 push $00
0040A1DD E86ADAFFFF call Sleep
0040A1E2 EB19 jmp $0040a1fd
Project56.dpr.26: bbb: sleep(0);
0040A1E4 6A00 push $00
0040A1E6 E861DAFFFF call Sleep
0040A1EB EB10 jmp $0040a1fd
Project56.dpr.27: ccc: sleep(0);
0040A1ED 6A00 push $00
0040A1EF E858DAFFFF call Sleep
0040A1F4 EB07 jmp $0040a1fd
Project56.dpr.28: hhh: sleep(0);
0040A1F6 6A00 push $00
0040A1F8 E84FDAFFFF call Sleep
导致此类代码的最可能原因是跳转表与L1缓存不能很好地发挥作用,并且如果没有大量的案例标签,那么测试跳转版本可能会更快。 / p>
该推理的“证明”是以下程序, 被转换为跳转表。
var
b: byte;
begin
case b of
0: sleep(0);
1: sleep(0);
2: sleep(0);
3: sleep(0);
4: sleep(0);
5: sleep(0);
6: sleep(0);
7: sleep(0);
8: sleep(0);
9: sleep(0);
10: sleep(0);
11: sleep(0);
12: sleep(0);
13: sleep(0);
14: sleep(0);
15: sleep(0);
16: sleep(0);
17: sleep(0);
18: sleep(0);
19: sleep(0);
20: sleep(0);
end;
end.
Project56.dpr.12: case b of
0040A178 0FB6C0 movzx eax,al
0040A17B 83F814 cmp eax,$14
0040A17E 0F8728010000 jnbe $0040a2ac
0040A184 FF24858BA14000 jmp dword ptr [eax*4+$40a18b]
...
Project56.dpr.14: 1: sleep(0);
0040A1EB 6A00 push $00
0040A1ED E85ADAFFFF call Sleep
0040A1F2 E9B5000000 jmp $0040a2ac
Project56.dpr.15: 2: sleep(0);
0040A1F7 6A00 push $00
0040A1F9 E84EDAFFFF call Sleep
0040A1FE E9A9000000 jmp $0040a2ac
Project56.dpr.16: 3: sleep(0);
0040A203 6A00 push $00
0040A205 E842DAFFFF call Sleep
0040A20A E99D000000 jmp $0040a2ac
...
巴里可以给我们一个明确的答案。我只是在测试和漫步。
答案 1 :(得分:19)
编译器会将case语句转换为以下语句之一:
它使用启发式,例如案例数,案例范围,不同备选方案的数量(每个备选方案可以实现一系列不同的值)等。
case语句的直觉是它是O(1)
操作。
答案 2 :(得分:15)
答案 3 :(得分:3)
请注意,如果MyAction的值已加权,则可以使用级联if..else获得良好的性能,其中您将最可能的案例放在顶部附近。我并不是说当你处理整数时,它会在性能方面与case / switch语句竞争。但是如果一个案例不适合(例如,假设你有字符串),那就把你的高百分比测试放在最顶层。