我正在尋找一些代碼,其中包含一個巨大的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