Что быстрее, включите строку или elseif по типу?


допустим, у меня есть вариант определения кода путем на основе сравнения строки или еще если типа:

что быстрее и почему?

switch(childNode.Name)
{
    case "Bob":
      break;
    case "Jill":
      break;
    case "Marko":
      break;
}

if(childNode is Bob)
{
}
elseif(childNode is Jill)
{
}
else if(childNode is Marko)
{
}

обновление: главная причина я спрашиваю это потому, что оператор switch-это их специфических о том, как дела. Например, он не позволит вам использовать переменные, только константы, которые перемещаются в основную сборку. Я решила, что это ограничение из-за некоторых хитростей было делающий. Если это только перевод на elseifs (как прокомментировал один плакат), то почему нам не разрешены переменные в операторах case?

предостережение: Я после оптимизации. Этот метод называется много раз в медленной части приложения.

23 69

23 ответа:

результаты профиля Грега отлично подходят для точного сценария, который он охватил, но интересно, что относительные затраты на различные методы резко меняются при рассмотрении ряда различных факторов, включая количество сравниваемых типов, а также относительную частоту и любые шаблоны в базовых данных.

самый простой ответ заключается в том, что никто не может сказать вам, что разница в производительности будет в вашем конкретном случае, вам нужно будет измерить производительность по-разному себя в вашей системе, чтобы получить точный ответ.

цепочка If/Else является эффективным подходом для небольшого числа сравнений типов, или если вы можете надежно предсказать, какие несколько типов будут составлять большинство из тех, которые вы видите. Потенциальная проблема с этим подходом заключается в том, что по мере увеличения числа типов увеличивается и число сопоставлений, которые необходимо выполнить.

если я выполняю следующее:

int value = 25124;
if(value == 0) ...
else if (value == 1) ...
else if (value == 2) ...
...
else if (value == 25124) ... 

каждое из предыдущих условий if должно быть оценено до ввода правильного блока. С другой стороны

switch(value) {
 case 0:...break;
 case 1:...break;
 case 2:...break;
 ...
 case 25124:...break;
}

выполнит один простой переход к правильному биту кода.

где это становится более сложным в вашем примере является то, что ваш другой метод использует переключатель на строки, а не целые числа, которые становится немного сложнее. На низком уровне строки не могут быть включены так же, как целочисленные значения так что компилятор C# делает некоторые магии, чтобы сделать эту работу для вас.

если оператор switch "достаточно мал" (где компилятор делает то, что он считает лучшим автоматически) включение строк генерирует код, который совпадает с цепочкой if/else.

switch(someString) {
    case "Foo": DoFoo(); break;
    case "Bar": DoBar(); break;
    default: DoOther; break;
}

- это то же, что:

if(someString == "Foo") {
    DoFoo();
} else if(someString == "Bar") {
    DoBar();
} else {
    DoOther();
}

как только список элементов в словаре становится "достаточно большим" компилятор автоматически создаст внутренний словарь, который сопоставляется со строками в коммутаторе к целочисленному индексу, а затем к коммутатору на основе этого индекса.

это выглядит примерно так (просто представьте себе больше записей, чем я собираюсь набирать)

статическое поле определяется в "скрытом" расположении, связанном с классом, содержащим оператор switch типа Dictionary<string, int> и дали искаженное имя

//Make sure the dictionary is loaded
if(theDictionary == null) { 
    //This is simplified for clarity, the actual implementation is more complex 
    // in order to ensure thread safety
    theDictionary = new Dictionary<string,int>();
    theDictionary["Foo"] = 0;
    theDictionary["Bar"] = 1;
}

int switchIndex;
if(theDictionary.TryGetValue(someString, out switchIndex)) {
    switch(switchIndex) {
    case 0: DoFoo(); break;
    case 1: DoBar(); break;
    }
} else {
    DoOther();
}

в некоторых быстрых тестах, которые я только что запустил, метод If / Else примерно в 3 раза быстрее, чем коммутатор для 3 разных типов (где типы распределяются случайным образом). При 25 типах коммутатор работает быстрее с небольшим запасом (16%), при 50 типах коммутатор работает более чем в два раза быстрее.

если вы собираетесь переключаться на большое количество типов, я бы предложил 3-й метод:

private delegate void NodeHandler(ChildNode node);

static Dictionary<RuntimeTypeHandle, NodeHandler> TypeHandleSwitcher = CreateSwitcher();

private static Dictionary<RuntimeTypeHandle, NodeHandler> CreateSwitcher()
{
    var ret = new Dictionary<RuntimeTypeHandle, NodeHandler>();

    ret[typeof(Bob).TypeHandle] = HandleBob;
    ret[typeof(Jill).TypeHandle] = HandleJill;
    ret[typeof(Marko).TypeHandle] = HandleMarko;

    return ret;
}

void HandleChildNode(ChildNode node)
{
    NodeHandler handler;
    if (TaskHandleSwitcher.TryGetValue(Type.GetRuntimeType(node), out handler))
    {
        handler(node);
    }
    else
    {
        //Unexpected type...
    }
}

это похоже на то, что предложил Тед Эллиот, но использование дескрипторов типа времени выполнения вместо объектов полного типа позволяет избежать накладных расходов на загрузку объекта типа через отражение.

вот некоторые быстрые тайминги на моей машине:

Testing 3 iterations with 5,000,000 data elements (mode=Random) and 5 types
Method                Time    % of optimal
If/Else               179.67  100.00
TypeHandleDictionary  321.33  178.85
TypeDictionary        377.67  210.20
Switch                492.67  274.21

Testing 3 iterations with 5,000,000 data elements (mode=Random) and 10 types
Method                Time    % of optimal
If/Else               271.33  100.00
TypeHandleDictionary  312.00  114.99
TypeDictionary        374.33  137.96
Switch                490.33  180.71

Testing 3 iterations with 5,000,000 data elements (mode=Random) and 15 types
Method                Time    % of optimal
TypeHandleDictionary  312.00  100.00
If/Else               369.00  118.27
TypeDictionary        371.67  119.12
Switch                491.67  157.59

Testing 3 iterations with 5,000,000 data elements (mode=Random) and 20 types
Method                Time    % of optimal
TypeHandleDictionary  335.33  100.00
TypeDictionary        373.00  111.23
If/Else               462.67  137.97
Switch                490.33  146.22

Testing 3 iterations with 5,000,000 data elements (mode=Random) and 25 types
Method                Time    % of optimal
TypeHandleDictionary  319.33  100.00
TypeDictionary        371.00  116.18
Switch                483.00  151.25
If/Else               562.00  175.99

Testing 3 iterations with 5,000,000 data elements (mode=Random) and 50 types
Method                Time      % of optimal
TypeHandleDictionary  319.67    100.00
TypeDictionary        376.67    117.83
Switch                453.33    141.81
If/Else               1,032.67  323.04

на моей машине, по крайней мере, подход к словарю типа handle превосходит все остальные для чего-либо более 15 различных типов, когда распределение типы используемых в качестве входных данных для метода является случайным.

если с другой стороны, вход полностью состоит из типа, который проверяется первым в цепочке if/else, что метод много быстрее:

Testing 3 iterations with 5,000,000 data elements (mode=UniformFirst) and 50 types
Method                Time    % of optimal
If/Else               39.00   100.00
TypeHandleDictionary  317.33  813.68
TypeDictionary        396.00  1,015.38
Switch                403.00  1,033.33

наоборот, если вход всегда последняя вещь в цепочке if / else, она имеет противоположный эффект:

Testing 3 iterations with 5,000,000 data elements (mode=UniformLast) and 50 types
Method                Time      % of optimal
TypeHandleDictionary  317.67    100.00
Switch                354.33    111.54
TypeDictionary        377.67    118.89
If/Else               1,907.67  600.52

если вы можете сделать некоторые предположения о своем вводе, вы можете получить лучшую производительность от гибридного подхода, где вы выполняете проверки if/else для нескольких наиболее распространенных типов, а затем возвращаетесь к подходу, основанному на словаре, если они терпят неудачу.

Я только что реализовал быстрое тестовое приложение и профилировал его с помощью ANTS 4.
Спецификации: .Net 3.5 с пакетом обновления 1 в 32-разрядной ОС Windows ХР, код собран в режиме release.

3 миллионов тестов:

  • переключатель: 1.842 секунд
  • если: 0.344 секунд.

кроме того, результаты оператора switch показывают (неудивительно), что более длинные имена занимают больше времени.

1 миллиона тестов

  • Боб: 0.612 секунд.
  • Джилл: 0.835 секунд.
  • Марко: 1.093 секунд.

похоже, что "если еще" быстрее, по крайней мере, сценарий, который я создал.

class Program
{
    static void Main( string[] args )
    {
        Bob bob = new Bob();
        Jill jill = new Jill();
        Marko marko = new Marko();

        for( int i = 0; i < 1000000; i++ )
        {
            Test( bob );
            Test( jill );
            Test( marko );
        }
    }

    public static void Test( ChildNode childNode )
    {   
        TestSwitch( childNode );
        TestIfElse( childNode );
    }

    private static void TestIfElse( ChildNode childNode )
    {
        if( childNode is Bob ){}
        else if( childNode is Jill ){}
        else if( childNode is Marko ){}
    }

    private static void TestSwitch( ChildNode childNode )
    {
        switch( childNode.Name )
        {
            case "Bob":
                break;
            case "Jill":
                break;
            case "Marko":
                break;
        }
    }
}

class ChildNode { public string Name { get; set; } }

class Bob : ChildNode { public Bob(){ this.Name = "Bob"; }}

class Jill : ChildNode{public Jill(){this.Name = "Jill";}}

class Marko : ChildNode{public Marko(){this.Name = "Marko";}}

во-первых, вы сравниваете яблоки и апельсины. Сначала нужно сравнить переключатель на тип ВС переключиться на строку, и тогда, если на тип ВС если на строку, а затем сравнить победителей.

во-вторых, это то, для чего был разработан OO. В языках, поддерживающих OO, включение типа (любого вида) - это запах кода, который указывает на плохой дизайн. Решение состоит в том, чтобы вывести из общей базы абстрактный или виртуальный метод (или аналогичную конструкцию, в зависимости от вашего язык)

например.

class Node
{
    public virtual void Action()
    {
        // Perform default action
    }
}

class Bob : Node
{
    public override void Action()
    {
        // Perform action for Bill
    }
}

class Jill : Node
{
    public override void Action()
    {
        // Perform action for Jill
    }
}

затем, вместо выполнения оператора switch, вы просто вызываете childNode.Действие ()

оператор Switch выполняется быстрее, чем лестница if-else-if. Это связано с возможностью компилятора оптимизировать оператор switch. В случае лестницы if-else-if код должен обрабатывать каждый оператор if в порядке, определенном программистом. Однако, поскольку каждый случай в инструкции switch не зависит от предыдущих случаев, компилятор может изменить порядок тестирования таким образом, чтобы обеспечить самое быстрое выполнение.

Если у вас есть классы, я бы предложил использовать шаблон разработки стратегии вместо switch или elseif.

попробуйте использовать перечисления для каждого объекта, вы можете быстро и легко переключаться на перечисления.

Если вы уже не написали это и не обнаружили у вас проблемы с производительностью, я бы не беспокоился о том, что быстрее. Идите с тем, что более читаемо. Помните: "преждевременная оптимизация-корень всех зол.- Дональд Кнут

конструкция коммутатора изначально предназначалась для целочисленных данных; ее целью было использовать аргумент непосредственно в качестве индекса в "таблице диспетчеризации", таблице указателей. Таким образом, будет один тест, а затем запустить непосредственно в соответствующий код, а не серию тестов.

трудность здесь заключается в том, что его использование было обобщено на "строковые" типы, которые, очевидно, не могут использоваться в качестве индекса, и все преимущества конструкции SWITCH теряются.

Если скорость-это ваша предполагаемая цель, проблема не в вашем коде, а в вашей структуре данных. Если пространство " имя "так же просто, как вы его показываете, лучше закодировать его в целочисленное значение (например, при создании данных) и использовать это целое число в"много раз в медленной части приложения".

если типы, которые вы включаете, являются примитивными типами .NET, вы можете использовать тип.GetTypeCode (тип), но если они являются пользовательскими типами, они все вернутся как TypeCode.Объект.

словарь с делегатами или классами обработчиков также может работать.

Dictionary<Type, HandlerDelegate> handlers = new Dictionary<Type, HandlerDelegate>();
handlers[typeof(Bob)] = this.HandleBob;
handlers[typeof(Jill)] = this.HandleJill;
handlers[typeof(Marko)] = this.HandleMarko;

handlers[childNode.GetType()](childNode);
/// ...

private void HandleBob(Node childNode) {
    // code to handle Bob
}

коммутатор () будет компилироваться в код, эквивалентный набору других ifs. Сравнение строк будет намного медленнее, чем сравнение типов.

Я помню, что читал в нескольких справочниках, что ветвление if/else быстрее, чем оператор switch. Тем не менее, небольшое исследование Blackwasp показывает, что оператор switch на самом деле быстрее: http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx

на самом деле, если вы сравниваете типичные утверждения от 3 до 10 (или около того), я серьезно сомневаюсь, что есть какой-либо реальный прирост производительности с использованием одного или другого.

Как уже сказал Крис, идите удобочитаемость: что быстрее, включите строку или elseif по типу?

Я думаю, что основная проблема производительности здесь заключается в том, что в блоке switch вы сравниваете строки, а в блоке if-else вы проверяете типы... Эти два не то же самое, и поэтому я бы сказал, что вы "сравниваете картошку с бананами".

Я бы начал с сравнения этого:

switch(childNode.Name)
{
    case "Bob":
        break;
    case "Jill":
        break;
    case "Marko":
      break;
}

if(childNode.Name == "Bob")
{}
else if(childNode.Name == "Jill")
{}
else if(childNode.Name == "Marko")
{}

Я не уверен, как быстрее это может быть правильный дизайн, чтобы пойти на полиморфизм.

interface INode
{
    void Action;
}

class Bob : INode
{
    public void Action
    {

    }
}

class Jill : INode
{
    public void Action
    {

    }
}

class Marko : INode
{
    public void Action
    {

    }
}

//Your function:
void Do(INode childNode)
{
    childNode.Action();
}

видя, что ваш оператор switch не поможет лучше. Если ваша функция на самом деле ничего не говорит о действии на типе, возможно, вы можете определить перечисление для каждого типа.

enum NodeType { Bob, Jill, Marko, Default }

interface INode
{
    NodeType Node { get; };
}

class Bob : INode
{
    public NodeType Node { get { return NodeType.Bob; } }
}

class Jill : INode
{
    public NodeType Node { get { return NodeType.Jill; } }
}

class Marko : INode
{
    public NodeType Node { get { return NodeType.Marko; } }
}

//Your function:
void Do(INode childNode)
{
    switch(childNode.Node)
    {
        case Bob:
          break;
        case Jill:
          break;
        case Marko:
          break;
        Default:
          throw new ArgumentException();
    }
}

Я предполагаю, что это должно быть быстрее, чем оба рассматриваемых подхода. Вы можете попробовать абстрактный класс route если наносекунды имеет значение для вас.

Я создал небольшую консоль, чтобы показать мое решение, просто чтобы подчеркнуть разницу в скорости. Я использовал другой алгоритм хэширования строк, поскольку версия сертификата должна замедляться для меня во время выполнения, и дубликаты маловероятны, и если это так, то мой оператор switch потерпит неудачу (никогда не случалось до сих пор). Мой уникальный метод расширения хэша включен в код ниже.

Core 2 console app with output

Я возьму 29 тиков за 695 ТИКов в любое время, особенно при использовании критических код.

С набором строк из базы данных, вы можете создать небольшое приложение для создания постоянной в заданном файл для использования в вашем коде, если вы просто повторно запускать пакетные и константы автоматически и подобрал решение.

  public static class StringExtention
    {
        public static long ToUniqueHash(this string text)
        {
            long value = 0;
            var array = text.ToCharArray();
            unchecked
            {
                for (int i = 0; i < array.Length; i++)
                {
                    value = (value * 397) ^ array[i].GetHashCode();
                    value = (value * 397) ^ i;
                }
                return value;
            }
        }
    }

    public class AccountTypes
    {

        static void Main()
        {
            var sb = new StringBuilder();

            sb.AppendLine($"const long ACCOUNT_TYPE = {"AccountType".ToUniqueHash()};");
            sb.AppendLine($"const long NET_LIQUIDATION = {"NetLiquidation".ToUniqueHash()};");
            sb.AppendLine($"const long TOTAL_CASH_VALUE = {"TotalCashValue".ToUniqueHash()};");
            sb.AppendLine($"const long SETTLED_CASH = {"SettledCash".ToUniqueHash()};");
            sb.AppendLine($"const long ACCRUED_CASH = {"AccruedCash".ToUniqueHash()};");
            sb.AppendLine($"const long BUYING_POWER = {"BuyingPower".ToUniqueHash()};");
            sb.AppendLine($"const long EQUITY_WITH_LOAN_VALUE = {"EquityWithLoanValue".ToUniqueHash()};");
            sb.AppendLine($"const long PREVIOUS_EQUITY_WITH_LOAN_VALUE = {"PreviousEquityWithLoanValue".ToUniqueHash()};");
            sb.AppendLine($"const long GROSS_POSITION_VALUE ={ "GrossPositionValue".ToUniqueHash()};");
            sb.AppendLine($"const long REQT_EQUITY = {"ReqTEquity".ToUniqueHash()};");
            sb.AppendLine($"const long REQT_MARGIN = {"ReqTMargin".ToUniqueHash()};");
            sb.AppendLine($"const long SPECIAL_MEMORANDUM_ACCOUNT = {"SMA".ToUniqueHash()};");
            sb.AppendLine($"const long INIT_MARGIN_REQ = { "InitMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long MAINT_MARGIN_REQ = {"MaintMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long AVAILABLE_FUNDS = {"AvailableFunds".ToUniqueHash()};");
            sb.AppendLine($"const long EXCESS_LIQUIDITY = {"ExcessLiquidity".ToUniqueHash()};");
            sb.AppendLine($"const long CUSHION = {"Cushion".ToUniqueHash()};");
            sb.AppendLine($"const long FULL_INIT_MARGIN_REQ = {"FullInitMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long FULL_MAINTMARGIN_REQ ={ "FullMaintMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long FULL_AVAILABLE_FUNDS = {"FullAvailableFunds".ToUniqueHash()};");
            sb.AppendLine($"const long FULL_EXCESS_LIQUIDITY ={ "FullExcessLiquidity".ToUniqueHash()};");
            sb.AppendLine($"const long LOOK_AHEAD_INIT_MARGIN_REQ = {"LookAheadInitMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long LOOK_AHEAD_MAINT_MARGIN_REQ = {"LookAheadMaintMarginReq".ToUniqueHash()};");
            sb.AppendLine($"const long LOOK_AHEAD_AVAILABLE_FUNDS = {"LookAheadAvailableFunds".ToUniqueHash()};");
            sb.AppendLine($"const long LOOK_AHEAD_EXCESS_LIQUIDITY = {"LookAheadExcessLiquidity".ToUniqueHash()};");
            sb.AppendLine($"const long HIGHEST_SEVERITY = {"HighestSeverity".ToUniqueHash()};");
            sb.AppendLine($"const long DAY_TRADES_REMAINING = {"DayTradesRemaining".ToUniqueHash()};");
            sb.AppendLine($"const long LEVERAGE = {"Leverage".ToUniqueHash()};");
            Console.WriteLine(sb.ToString());

            Test();    
        }    

        public static void Test()
        {
            //generated constant values
            const long ACCOUNT_TYPE = -3012481629590703298;
            const long NET_LIQUIDATION = 5886477638280951639;
            const long TOTAL_CASH_VALUE = 2715174589598334721;
            const long SETTLED_CASH = 9013818865418133625;
            const long ACCRUED_CASH = -1095823472425902515;
            const long BUYING_POWER = -4447052054809609098;
            const long EQUITY_WITH_LOAN_VALUE = -4088154623329785565;
            const long PREVIOUS_EQUITY_WITH_LOAN_VALUE = 6224054330592996694;
            const long GROSS_POSITION_VALUE = -7316842993788269735;
            const long REQT_EQUITY = -7457439202928979430;
            const long REQT_MARGIN = -7525806483981945115;
            const long SPECIAL_MEMORANDUM_ACCOUNT = -1696406879233404584;
            const long INIT_MARGIN_REQ = 4495254338330797326;
            const long MAINT_MARGIN_REQ = 3923858659879350034;
            const long AVAILABLE_FUNDS = 2736927433442081110;
            const long EXCESS_LIQUIDITY = 5975045739561521360;
            const long CUSHION = 5079153439662500166;
            const long FULL_INIT_MARGIN_REQ = -6446443340724968443;
            const long FULL_MAINTMARGIN_REQ = -8084126626285123011;
            const long FULL_AVAILABLE_FUNDS = 1594040062751632873;
            const long FULL_EXCESS_LIQUIDITY = -2360941491690082189;
            const long LOOK_AHEAD_INIT_MARGIN_REQ = 5230305572167766821;
            const long LOOK_AHEAD_MAINT_MARGIN_REQ = 4895875570930256738;
            const long LOOK_AHEAD_AVAILABLE_FUNDS = -7687608210548571554;
            const long LOOK_AHEAD_EXCESS_LIQUIDITY = -4299898188451362207;
            const long HIGHEST_SEVERITY = 5831097798646393988;
            const long DAY_TRADES_REMAINING = 3899479916235857560;
            const long LEVERAGE = 1018053116254258495;

            bool found = false;
            var sValues = new string[] {
              "AccountType"
              ,"NetLiquidation"
              ,"TotalCashValue"
              ,"SettledCash"
              ,"AccruedCash"
              ,"BuyingPower"
              ,"EquityWithLoanValue"
              ,"PreviousEquityWithLoanValue"
              ,"GrossPositionValue"
              ,"ReqTEquity"
              ,"ReqTMargin"
              ,"SMA"
              ,"InitMarginReq"
              ,"MaintMarginReq"
              ,"AvailableFunds"
              ,"ExcessLiquidity"
              ,"Cushion"
              ,"FullInitMarginReq"
              ,"FullMaintMarginReq"
              ,"FullAvailableFunds"
              ,"FullExcessLiquidity"
              ,"LookAheadInitMarginReq"
              ,"LookAheadMaintMarginReq"
              ,"LookAheadAvailableFunds"
              ,"LookAheadExcessLiquidity"
              ,"HighestSeverity"
              ,"DayTradesRemaining"
              ,"Leverage"
            };

            long t1, t2;
            var sw = System.Diagnostics.Stopwatch.StartNew();
            foreach (var name in sValues)
            {
                switch (name)
                {
                    case "AccountType": found = true; break;
                    case "NetLiquidation": found = true; break;
                    case "TotalCashValue": found = true; break;
                    case "SettledCash": found = true; break;
                    case "AccruedCash": found = true; break;
                    case "BuyingPower": found = true; break;
                    case "EquityWithLoanValue": found = true; break;
                    case "PreviousEquityWithLoanValue": found = true; break;
                    case "GrossPositionValue": found = true; break;
                    case "ReqTEquity": found = true; break;
                    case "ReqTMargin": found = true; break;
                    case "SMA": found = true; break;
                    case "InitMarginReq": found = true; break;
                    case "MaintMarginReq": found = true; break;
                    case "AvailableFunds": found = true; break;
                    case "ExcessLiquidity": found = true; break;
                    case "Cushion": found = true; break;
                    case "FullInitMarginReq": found = true; break;
                    case "FullMaintMarginReq": found = true; break;
                    case "FullAvailableFunds": found = true; break;
                    case "FullExcessLiquidity": found = true; break;
                    case "LookAheadInitMarginReq": found = true; break;
                    case "LookAheadMaintMarginReq": found = true; break;
                    case "LookAheadAvailableFunds": found = true; break;
                    case "LookAheadExcessLiquidity": found = true; break;
                    case "HighestSeverity": found = true; break;
                    case "DayTradesRemaining": found = true; break;
                    case "Leverage": found = true; break;
                    default: found = false; break;
                }

                if (!found)
                    throw new NotImplementedException();
            }
            t1 = sw.ElapsedTicks;
            sw.Restart();
            foreach (var name in sValues)
            {
                switch (name.ToUniqueHash())
                {
                    case ACCOUNT_TYPE:
                        found = true;
                        break;
                    case NET_LIQUIDATION:
                        found = true;
                        break;
                    case TOTAL_CASH_VALUE:
                        found = true;
                        break;
                    case SETTLED_CASH:
                        found = true;
                        break;
                    case ACCRUED_CASH:
                        found = true;
                        break;
                    case BUYING_POWER:
                        found = true;
                        break;
                    case EQUITY_WITH_LOAN_VALUE:
                        found = true;
                        break;
                    case PREVIOUS_EQUITY_WITH_LOAN_VALUE:
                        found = true;
                        break;
                    case GROSS_POSITION_VALUE:
                        found = true;
                        break;
                    case REQT_EQUITY:
                        found = true;
                        break;
                    case REQT_MARGIN:
                        found = true;
                        break;
                    case SPECIAL_MEMORANDUM_ACCOUNT:
                        found = true;
                        break;
                    case INIT_MARGIN_REQ:
                        found = true;
                        break;
                    case MAINT_MARGIN_REQ:
                        found = true;
                        break;
                    case AVAILABLE_FUNDS:
                        found = true;
                        break;
                    case EXCESS_LIQUIDITY:
                        found = true;
                        break;
                    case CUSHION:
                        found = true;
                        break;
                    case FULL_INIT_MARGIN_REQ:
                        found = true;
                        break;
                    case FULL_MAINTMARGIN_REQ:
                        found = true;
                        break;
                    case FULL_AVAILABLE_FUNDS:
                        found = true;
                        break;
                    case FULL_EXCESS_LIQUIDITY:
                        found = true;
                        break;
                    case LOOK_AHEAD_INIT_MARGIN_REQ:
                        found = true;
                        break;
                    case LOOK_AHEAD_MAINT_MARGIN_REQ:
                        found = true;
                        break;
                    case LOOK_AHEAD_AVAILABLE_FUNDS:
                        found = true;
                        break;
                    case LOOK_AHEAD_EXCESS_LIQUIDITY:
                        found = true;
                        break;
                    case HIGHEST_SEVERITY:
                        found = true;
                        break;
                    case DAY_TRADES_REMAINING:
                        found = true;
                        break;
                    case LEVERAGE:
                        found = true;
                        break;
                    default:
                        found = false;
                        break;
                }

                if (!found)
                    throw new NotImplementedException();
            }
            t2 = sw.ElapsedTicks;
            sw.Stop();
            Console.WriteLine($"String switch:{t1:N0} long switch:{t2:N0}");
            var faster = (t1 > t2) ? "Slower" : "faster";
            Console.WriteLine($"String switch: is {faster} than long switch: by {Math.Abs(t1-t2)} Ticks");
            Console.ReadLine();

        }

сравнение строк всегда будет полностью зависеть от среды выполнения (если строки не выделены статически, хотя необходимость сравнивать их друг с другом является спорной). Однако сравнение типов может выполняться с помощью динамической или статической привязки, и в любом случае это более эффективно для среды выполнения, чем сравнение отдельных символов в строке.

конечно, строка включения будет компилироваться до сравнения строк (по одному на случай), которое медленнее, чем сравнение типов (и намного медленнее, чем типичное целочисленное сравнение, которое используется для switch/case)?

три мысли:

1) Если вы собираетесь сделать что-то другое, основанное на типах объектов, может иметь смысл переместить это поведение в эти классы. Тогда вместо switch или if-else вы просто вызовете childNode.DoSomething ().

2) Сравнение типов будет намного быстрее, чем сравнение строк.

3) в дизайне if-else вы можете воспользоваться переупорядочением тестов. Если объекты "Джилл" составляют 90% объектов, идущих там, сначала проверьте их.

одна из проблем, которые вы имеете с коммутатором, - это использование строк, таких как "Bob", это вызовет гораздо больше циклов и строк в скомпилированном коде. Сгенерированный IL должен будет объявить строку, установить ее в "Bob", а затем использовать ее в сравнении. Так что с учетом этого ваши заявления IF будут работать быстрее.

PS. Пример Aeon не будет работать, потому что вы не можете включить типы. (Нет, я не знаю, почему именно, но мы пробовали это, и это не работает. Это связано с типом бытия переменная)

Если вы хотите проверить это, просто создайте отдельное приложение и создайте два простых метода, которые делают то, что написано выше, и используют что-то вроде Ildasm.exe, чтобы увидеть IL. Вы заметите намного меньше строк в IL метода оператора IF.

Ildasm поставляется с VisualStudio...

страница ILDASM -http://msdn.microsoft.com/en-us/library/f7dy01k1 (VS. 80). aspx

ILDASM учебник - http://msdn.microsoft.com/en-us/library/aa309387(против.71).аспн

помните, что профилировщик-ваш друг. Любая догадка-это пустая трата времени большую часть времени. Кстати, у меня был хороший опыт работы с JetBrains'dotTrace профайлер.

строка включения в основном компилируется в лестницу if-else-if. Попробуйте декомпилировать простой. В любом случае, тестирование строки equailty должно быть дешевле, так как они интернированы, и все, что потребуется, - это проверка ссылки. Делайте то, что имеет смысл с точки зрения ремонтопригодности; если вы включаете строки, сделайте переключатель строк. Если вы выбираете на основе типа, лестница типа является более подходящим.

Я вроде как делаю это немного по-другому, Строки, которые вы включаете, будут константами, поэтому вы можете предсказать значения во время компиляции.

в вашем случае я бы использовал хэш-значения, это переключатель int, у вас есть 2 варианта, используйте константы времени компиляции или вычисляйте во время выполнения.

//somewhere in your code
static long _bob = "Bob".GetUniqueHashCode();
static long _jill = "Jill".GetUniqueHashCode();
static long _marko = "Marko".GeUniquetHashCode();

void MyMethod()
{
   ...
   if(childNode.Tag==0)
      childNode.Tag= childNode.Name.GetUniquetHashCode()

   switch(childNode.Tag)
   {
       case _bob :
        break;
       case _jill :
         break;
       case _marko :
        break;
   }
}

метод расширения для GetUniquetHashCode может быть что-то вроде этого:

public static class StringExtentions
    {
        /// <summary>
        /// Return unique Int64 value for input string
        /// </summary>
        /// <param name="strText"></param>
        /// <returns></returns>
        public static Int64 GetUniquetHashCode(this string strText)
        {
            Int64 hashCode = 0;
            if (!string.IsNullOrEmpty(strText))
            {
                //Unicode Encode Covering all character-set
                byte[] byteContents = Encoding.Unicode.GetBytes(strText);
                System.Security.Cryptography.SHA256 hash =  new System.Security.Cryptography.SHA256CryptoServiceProvider();
                byte[] hashText = hash.ComputeHash(byteContents);
                //32Byte hashText separate
                //hashCodeStart = 0~7  8Byte
                //hashCodeMedium = 8~23  8Byte
                //hashCodeEnd = 24~31  8Byte
                //and Fold
                Int64 hashCodeStart = BitConverter.ToInt64(hashText, 0);
                Int64 hashCodeMedium = BitConverter.ToInt64(hashText, 8);
                Int64 hashCodeEnd = BitConverter.ToInt64(hashText, 24);
                hashCode = hashCodeStart ^ hashCodeMedium ^ hashCodeEnd;
            }
            return (hashCode);
        }


    }

источник этого кода был опубликован здесь Пожалуйста обратите внимание, что использование криптографии происходит медленно, вы обычно разогреваете поддерживаемую строку при запуске приложения, я делаю это, сохраняя их в статических полях, поскольку они не изменяются и не являются релевантными экземплярами. обратите внимание, что я установил значение тега объекта node, я мог бы использовать любое свойство или добавить его, просто убедитесь, что они синхронизированы с фактическим текстом.

Я работаю на системах с низкой задержкой и все мои коды приходят в виде строки command:value,command: value....

теперь все команды известны как 64-битные целочисленные значения, поэтому такое переключение экономит некоторое время процессора.

Я просто читал список ответов здесь, и хотел поделиться этот тест тест, который сравнивает switch построить с if-else и тройных ? операторы.

мне нравится этот пост это не только сравнивает одиночные левые конструкции (например,if-else) но конструкции двойного и тройного уровня (например,if-else-if-else).

по результатам if-else конструкция была самой быстрой в 8/9 тестовых случаях; switch построить привязанный для самого быстрого в 5/9 тестовых случаев.

так что если вы ищете скорость if-else кажется, самый быстрый способ пойти.

возможно, я что-то упустил, но не могли бы вы сделать оператор switch для типа вместо строки? То есть,

switch(childNode.Type)
{
case Bob:
  break;
case Jill:
  break;
case Marko:
  break;
}