重要的是不要将C#switch语句与CIL switch指令混淆。
CIL开关是一个跳转表,它需要一个指向一组跳转地址的索引。
仅当C#开关的大小写相邻时,这才有用:
case 3: blah; break;
case 4: blah; break;
case 5: blah; break;
但是如果没有的话就没什么用了:
case 10: blah; break;
case 200: blah; break;
case 3000: blah; break;
(您需要一个约3000个条目的表,仅使用3个插槽)
使用不相邻的表达式,编译器可能会开始执行线性if-else-if-else检查。
对于较大的非相邻表达式集,编译器可以从二叉树搜索开始,最后是最后几项if-else-if-else。
使用包含大量相邻项的表达式集,编译器可以进行二叉树搜索,最后是CIL开关。
它充满了“ mays”和“ mights”,并且取决于编译器(可能与Mono或Rotor不同)。
我使用相邻的案例在您的计算机上复制了您的结果:
执行10向切换的总时间,10000次迭代(毫秒):25.1383
每10向切换的近似时间(毫秒):0.00251383
执行50向切换的总时间,10000次迭代(毫秒):26.593
每50向切换的近似时间(毫秒):0.0026593
执行5000次切换的总时间,10000次迭代(毫秒):23.7094
每5000次切换的近似时间(毫秒):0.00237094
执行50000路切换的总时间,10000次迭代(毫秒):20.0933
每50000路切换的大约时间(毫秒):0.00200933
然后,我还使用了非相邻的case表达式:
执行10向切换的总时间,10000次迭代(毫秒):19.6189
每10向切换的近似时间(毫秒):0.00196189
执行500路切换的总时间,10000次迭代(ms):19.1664
每500路切换的大约时间(ms):0.00191664
执行5000次切换的总时间,10000次迭代(毫秒):19.5871
每5000次切换大约的时间(毫秒):0.00195871
不相邻的50,000个case switch语句将不会编译。
“表达式太长或太复杂,无法在'ConsoleApplication1.Program.Main(string [])'附近编译”
有趣的是,二叉树搜索的速度比CIL切换指令的显示速度快(可能在统计上不是)。
Brian,您使用过“ 常数 ” 一词,从计算复杂性理论的角度来看,它具有非常明确的含义。简单的相邻整数示例可能会产生被认为是O(1)(常数)的CIL,而稀疏示例是O(log n)(对数),聚类示例位于两者之间的某个地方,小示例是O(n)(线性)。
这甚至无法解决Generic.Dictionary<string,int32>
可能会创建静态字符串的String的情况,并且在首次使用时会遭受一定的开销。此处的性能取决于的性能Generic.Dictionary
。
如果检查C#语言规范(不是CIL规范),您会发现“ 15.7.2 switch语句”没有提到“恒定时间”,或者底层实现甚至使用CIL switch指令(请谨慎假设这样的事情)。
归根结底,在现代系统上针对整数表达式的C#切换是亚微秒级的操作,通常不值得担心。
当然,这些时间将取决于机器和条件。我不会关注这些时序测试,我们正在谈论的微秒持续时间与正在运行的任何“真实”代码相形见((并且您必须包含一些“真实代码”,否则编译器会优化分支),或者系统中的抖动。我的答案是基于使用IL DASM来检查由C#编译器创建的CIL。当然,这不是最终的,因为实际运行的指令是由JIT创建的。
我已经检查了x86机器上实际执行的最终CPU指令,并可以确认一个简单的相邻set开关在执行以下操作:
jmp ds:300025F0[eax*4]
二叉树搜索充满以下内容:
cmp ebx, 79Eh
jg 3000352B
cmp ebx, 654h
jg 300032BB
…
cmp ebx, 0F82h
jz 30005EEE