我正在寻找一些代码,其中包含一个巨大的switch语句和if-else语句,并立即感受到优化的冲动。作为一名优秀的开发人员总是应该做的我开始得到一些硬定时的事实,并开始与三个变种:条件运算符是否很慢?
原来的代码如下所示:
public static bool SwitchIfElse(Key inKey, out char key, bool shift) { switch (inKey) { case Key.A: if (shift) { key = 'A'; } else { key = 'a'; } return true; case Key.B: if (shift) { key = 'B'; } else { key = 'b'; } return true; case Key.C: if (shift) { key = 'C'; } else { key = 'c'; } return true; ... case Key.Y: if (shift) { key = 'Y'; } else { key = 'y'; } return true; case Key.Z: if (shift) { key = 'Z'; } else { key = 'z'; } return true; ... //some more cases with special keys... } key = (char)0; return false; }
第二个变量转换为使用条件运算符:
public static bool SwitchConditionalOperator(Key inKey, out char key, bool shift) { switch (inKey) { case Key.A: key = shift ? 'A' : 'a'; return true; case Key.B: key = shift ? 'B' : 'b'; return true; case Key.C: key = shift ? 'C' : 'c'; return true; ... case Key.Y: key = shift ? 'Y' : 'y'; return true; case Key.Z: key = shift ? 'Z' : 'z'; return true; ... //some more cases with special keys... } key = (char)0; return false; }
使用A捻字典预填充有键/字符对:
public static bool DictionaryLookup(Key inKey, out char key, bool shift) { key = '\0'; if (shift) return _upperKeys.TryGetValue(inKey, out key); else return _lowerKeys.TryGetValue(inKey, out key); }
注意:两个开关语句具有完全相同的情况下和字典具有字符的等量。
我在期待1)和2)在性能上有些类似,并且3)会稍微慢一些。
对于运行两次10.000.000迭代热身每个方法,然后定时,让我惊讶,我得到以下结果:每次通话
- 0.0000166毫秒每次通话
- 0.0000779毫秒
- 0.0000413毫秒每呼叫
这是怎么回事?条件运算符比if-else语句慢四倍,几乎比字典查找慢两倍。我在这里错过了一些必要的东西,还是条件运算符本身很慢?
更新1:有关我的测试设备的几句话。我在Visual Studio 2010中编译.Net 3.5项目的版本下为每个上述变体运行以下(伪)代码。打开代码优化并关闭DEBUG/TRACE常量。在进行定时运行之前,我将一次测量的方法用于热身。 run方法执行的方法进行大量的迭代,用shift
设置为true和false,并与选定的一组输入键:
Run(method);
var stopwatch = Stopwatch.StartNew();
Run(method);
stopwatch.Stop();
var measure = stopwatch.ElapsedMilliseconds/iterations;
Run方法是这样的:
for (int i = 0; i < iterations/4; i++)
{
method(Key.Space, key, true);
method(Key.A, key, true);
method(Key.Space, key, false);
method(Key.A, key, false);
}
更新2:进一步挖掘,我已经看过1)和2)生成的IL,并发现主开关结构与我所期望的完全相同,但案例主体略有差异。下面是我在看的IL:
1)if/else语句:
L_0167: ldarg.2
L_0168: brfalse.s L_0170
L_016a: ldarg.1
L_016b: ldc.i4.s 0x42
L_016d: stind.i2
L_016e: br.s L_0174
L_0170: ldarg.1
L_0171: ldc.i4.s 0x62
L_0173: stind.i2
L_0174: ldc.i4.1
L_0175: ret
2)条件运算符:
L_0165: ldarg.1
L_0166: ldarg.2
L_0167: brtrue.s L_016d
L_0169: ldc.i4.s 0x62
L_016b: br.s L_016f
L_016d: ldc.i4.s 0x42
L_016f: stind.i2
L_0170: ldc.i4.1
L_0171: ret
一些观察:
- 而如果/ else分支时
shift
为假时shift
等于true条件运算符分支。 - 虽然1)实际上编译到大于2的几个更多的指令),当
shift
是真或假执行的指令数,等于两个。 - 指令排序为1)是这样的,只有一个堆栈槽在所有时间被占用,而2)总是加载两项。
是否有任何这些观察结果暗示,条件运算符将执行慢?是否还有其他副作用?
你的意思是“有条件的”运营商,是吗? – 2010-02-14 00:53:49
正式的,它是“条件运算符”,但我经常听到它被称为“三元运算符”。据我所知,它是C#中唯一具有三个参数的运算符。那么谁来狡辩命名呢? :) – Nathan 2010-02-14 00:58:37
我不知道“总是应该做”。我的第一个反应是首先看目标代码,以确保1 /和2 /的编译方式不同。接下来,你需要关心吗?即使它们现在没有用相同的高效代码编译,它们也可能在您的编译器的下一个版本中。你试图获得的知识至多暂时的价值。 – 2010-02-14 00:59:18