2009-12-04 UPDATE: для профилирования результатов по ряду предложений, размещенных здесь, см. ниже!
Вопрос
Рассмотрим следующий очень безобидный, очень простой метод, который использует оператор switch
для возврата определенного значения перечисления:
public static MarketDataExchange GetMarketDataExchange(string ActivCode) {
if (ActivCode == null) return MarketDataExchange.NONE;
switch (ActivCode) {
case "": return MarketDataExchange.NBBO;
case "A": return MarketDataExchange.AMEX;
case "B": return MarketDataExchange.BSE;
case "BT": return MarketDataExchange.BATS;
case "C": return MarketDataExchange.NSE;
case "MW": return MarketDataExchange.CHX;
case "N": return MarketDataExchange.NYSE;
case "PA": return MarketDataExchange.ARCA;
case "Q": return MarketDataExchange.NASDAQ;
case "QD": return MarketDataExchange.NASDAQ_ADF;
case "W": return MarketDataExchange.CBOE;
case "X": return MarketDataExchange.PHLX;
case "Y": return MarketDataExchange.DIRECTEDGE;
}
return MarketDataExchange.NONE;
}
Мой коллега и я вчера обсудили несколько идей о том, как на самом деле сделать этот метод быстрее, и мы придумали некоторые интересные модификации, которые на самом деле значительно улучшили его производительность (пропорционально, конечно, конечно). Мне было бы интересно узнать, какие варианты оптимизации могут предложить кому-то еще, которые могли бы не произойти с нами.
С самого начала, позвольте мне просто предложить быстрое выражение об отказе от ответственности: это для забавы, а не для того, чтобы подпитывать все "оптимизировать или не оптимизировать" дебаты. Тем не менее, если вы считаете себя среди тех, кто догматично полагает, что "преждевременная оптимизация - корень всего зла", просто отдавайте себе отчет в том, что я работаю в высокочастотной торговой фирме, где все должно работать как можно быстрее - узкое место или нет. Итак, хотя я размещаю это на SO для fun, это не просто огромная трата времени.
Еще одно замечание: меня интересуют два типа ответов: те, которые предполагают, что каждый вход будет действительным ActivCode (одна из строк в выражении switch
выше), а те, которые этого не делают. Я почти уверен, что первое предположение позволяет улучшить скорость; во всяком случае, это было для нас. Но я знаю, что улучшения возможны в любом случае.
Результаты
Хорошо, оказывается, что самое быстрое решение, которое я тестировал, произошло от João Angelo, предложение которого было на самом деле очень простым, но чрезвычайно умным. Решение, которое мой коллега и я разработали (после опробования нескольких подходов, многие из которых были придуманы здесь), заняли второе место; Я собирался опубликовать его, но оказывается, что Марк Рэнсом придумал ту же самую идею, поэтому просто проверьте его ответ!
Поскольку я запускал эти тесты, некоторые другие пользователи опубликовали еще более новые идеи... Я проведу их в свое время, когда у меня будет еще несколько минут.
Я проверил эти тесты на двух разных машинах: мой персональный компьютер дома (двухъядерный Athlon с 4 Гб оперативной памятью под управлением Windows 7 64-бит) и моя машина разработки на работе (двухъядерный Athlon с 2 Гб оперативной памяти под управлением Windows XP SP3). Очевидно, времена были разные; однако относительные времена, означающие, как каждый метод сравнивался с любым другим методом, были одинаковыми. То есть, самый быстрый был самым быстрым на обеих машинах и т.д.
Теперь о результатах. (Время, которое я публикую ниже, находится на моем домашнем компьютере.)
Но сначала для справки - исходный оператор switch:
1000000 пробегов: 98,88 мс
Средний: 0.09888 микросекунд
Самая быстрая оптимизация:
-
Идея João Angelo о назначении значений перечислениям на основе хеш-кодов строк ActivCode, а затем непосредственно обтекание
ActivCode.GetHashCode()
доMarketDataExchange
:
1000000 пробегов: 23,64 мс
Средний балл: 0.02364 микросекунд
Увеличение скорости: 329.90% -
Мой коллега и моя идея лить
ActivCode[0]
вint
и получить соответствующийMarketDataExchange
из массива, инициализированного при запуске (эта же идея была предложена Марком Рэнсом):
1000000 пробегов: 28,76 мс
Средний балл: 0,02876 микросекунд
Увеличение скорости: 253,13% -
tster идея включения вывода
ActivCode.GetHashCode()
вместоActivCode
:
1000000 пробегов: 34,69 мс
Средний: 0.03469 микросекунды
Увеличение скорости: 185,04% -
Идея, предложенная несколькими пользователями, включая Auraseer, tster и kyoryu, включить
ActivCode[0]
вместоActivCode
:
1000000 пробегов: 36,57 мс
Средний балл: 0.03657 микросекунд
Увеличение скорости: 174.66% -
Идея Loadmaster использования быстрого хеша,
ActivCode[0] + ActivCode[1]*0x100
:
1000000 пробегов: 39,53 мс
Средний балл: 0.03953 microsecond
Увеличение скорости: 153.53% -
Использование хэш-таблицы (
Dictionary<string, MarketDataExchange>
), как полагают многие:
1000000 пробегов: 88,32 мс
Средний: 0.08832 микросекунд
Увеличение скорости: 12,36% -
Использование двоичного поиска:
1000000 пробегов: 1031 мс
Средний: 1.031 микросекунды
Увеличение скорости: нет (ухудшение характеристик)
Позвольте мне просто сказать, что было здорово видеть, сколько разных идей у людей было по этой простой проблеме. Это было очень интересно для меня, и я очень благодарен всем, кто внес свой вклад и сделал предложение до сих пор.