it-swarm-eu.dev

Co je rychlejší, zapnout řetězec nebo jinak na typu?

Řekněme, že mám možnost identifikovat kódovou cestu, která se má provést na základě porovnání řetězců nebo jinak iffing typu:

Co je rychlejší a proč?

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)
{
}

Update: Hlavním důvodem, proč se ho ptám, je to, že příkaz switch je perkulární o tom, co se počítá jako případ. Například vám neumožňuje používat proměnné, pouze konstanty, které se přesunou do hlavního shromáždění. Předpokládal jsem, že má toto omezení kvůli nějaké funky věci to dělal. Je-li to pouze překladem do jiných textů (jak jeden plakát komentoval), proč proč jsme nepovolili proměnné v případových prohlášeních?

Upozornění: Jsem po optimalizaci. Tato metoda se nazývá many časy v pomalé části aplikace.

72
Quibblesome

Výsledky Gregova profilu jsou skvělé pro přesný scénář, na který se vztahuje, ale zajímavé je, že relativní náklady různých metod se dramaticky mění při zvažování řady různých faktorů, včetně počtu porovnávaných typů a relativní četnosti a všech vzorců v podkladových datech. .

Jednoduchou odpovědí je, že nikdo nemůže říct, jaký bude rozdíl ve výkonu ve vašem konkrétním scénáři, budete muset měřit výkon různými způsoby sami ve svém vlastním systému, abyste získali přesnou odpověď.

Řetězec If/Else je účinný přístup pro malý počet porovnání typu, nebo pokud můžete spolehlivě předpovědět, které typy typů budou tvořit většinu typů, které vidíte. Potenciálním problémem tohoto přístupu je, že s rostoucím počtem typů se zvyšuje i počet porovnání, která musí být provedena.

pokud provedu následující:

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

každé z předchozích, pokud musí být před vložením správného bloku vyhodnoceny podmínky. Na druhou stranu

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

provede jeden jednoduchý skok na správný bit kódu.

Tam, kde se dostane složitější ve vašem příkladu je, že vaše jiná metoda používá přepínač na řetězce, spíše než celá čísla, která dostane trochu složitější. Na nízké úrovni, řetězce nemohou být zapnuty stejným způsobem jako celočíselné hodnoty, takže kompilátor C # udělá nějakou magii, aby tuto práci pro vás dělal. 

Pokud je příkaz switch "dost malý" (kde kompilátor dělá to, co si myslí, že je nejlepší automaticky), přepnutí na řetězce vytvoří kód, který je stejný jako řetězec if/else.

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

je stejné jako:

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

Jakmile seznam položek ve slovníku dostane "dost velký" kompilátor automaticky vytvoří interní slovník, který mapuje z řetězců v přepínači na celočíselný index a pak přepínač založený na tomto indexu.

Vypadá to něco takového (jen si představte více záznamů, než bych chtěl napsat)

Statické pole je definováno v "skrytém" umístění, které je přidruženo k třídě obsahující příkaz switch typu Dictionary<string, int> a zadáno jméno s názvem. 

//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();
}

V některých rychlých testech, které jsem právě provozoval, je metoda If/Else asi 3x rychlejší než přepínač pro 3 různé typy (kde jsou typy náhodně rozděleny). Při 25 typech je přepínač rychlejší při malém rozpětí (16%) při 50 typech přepínačů je více než dvakrát rychlejší.

Pokud se chystáte spínat velký počet typů, navrhl bych třetí metodu:

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...
    }
}

To je podobné tomu, co navrhl Ted Elliot, ale použití runtime typu popisovačů místo objektů plného typu zabraňuje režii načítání objektu typu přes odraz.

Zde je několik rychlých časů na mém počítači:

 Testování 3 iterací s 5 000 000 datovými prvky (režim = Random) a 5 typů 
 Metoda Čas% optimálního 
 If/Else 179.67 100.00 
 TypeHandleDictionary 321.33 178.85 
 TypeDictionary 377.67 210.20 
 Přepínač 492.67 274.21 
 
 Testování 3 iterací s 5 000 000 datovými prvky (režim = Random) a 10 typů 
 Metoda Čas% optimálního 
 If/Else 271.33 100.00 
 TypeHandleDictionary 312.00 114.99 
 TypeDictionary 374.33 137.96 
 Přepínač 490.33 180.71 
 
 Testování 3 iterací s 5 000 000 datovými prvky (mode = Random) a 15 typů 
 Metoda Čas% optimálního 
 TypeHandleDictionary 312.00 100.00 
 If/Else 369.00 118.27 
 TypeDictionary 371.67 119.12 
 Switch 491,67 157,59 
 
 Testování 3 iterací s 5 000 000 dat prvky (režim = Random) a 20 typů 
 Metoda Čas% optimálního 
 TypeHandleDictionary 335.33 100.00 
 TypeDictionary 373.00 111.23 
 If/Else 462.67 137.97 
 Přepínač 490.33 146.22 
 
 Testování 3 iterací 5 000 000 datových prvků (režim = Random) a 25 typů 
 Metoda Čas% optimálního 
 TypeHandleDictionary 319.33 100.00 
 TypeDictionary 371.00 116.18 
 Přepínač 483.00 151.25 
 If/Else 562.00 175.99 
 
 Testování 3 iterací s 5 000 000 datovými prvky (mód = Random) a 50 typů 
 Metoda Čas% optimálního 
 TypeHandleDictionary 319.67 100.00 
 TypeDictionary 376.67 117.83 
 Switch 453.33 141.81 
 If/Else 1.032.67 323.04 
 

Přinejmenším na mém stroji bije přístup ke slovníku typu handle pro všechny ostatní pro více než 15 různých typů, když je rozdělení Typů použitých jako vstup do metody náhodné. 

Na druhé straně je vstup složen výhradně z typu, který je nejprve zkontrolován v řetězci if/else, že metoda je much rychlejší:

 Testování 3 iterací s 5 000 000 datovými prvky (režim = UniformFirst) a 50 typů 
 Metoda Čas% optimálního 
 If/Else 39.00 100.00 
 TypeHandleDictionary 317.33 813.68 
 TypeDictionary 396.00 1,015.38 
 Přepínač 403.00 1.033.33 

Naopak, pokud je vstup vždy poslední věcí v řetězci if/else, má opačný účinek:

 Testování 3 iterací s 5 000 000 datovými prvky (režim = UniformLast) a 50 typů 
 Metoda Čas% optimálního 
 TypeHandleDictionary 317.67 100.00 
 Přepínač 354.33 111.54 
 TypeDictionary 377.67 118.89 
 If/Else 1,907.67 600.52 

Pokud můžete udělat nějaké předpoklady o svém vstupu, můžete získat nejlepší výkon z hybridního přístupu, kde provedete, pokud/jinak kontroluje několik typů, které jsou nejčastější, a pak se vrátit zpět do přístupu řízeného slovníky, pokud selžou.

122
Andrew

Právě jsem implementoval rychlou testovací aplikaci a profiloval ji ANTS 4.
Spec: .Net 3.5 sp1 v 32bitovém Windows XP, kód postavený v režimu release.

3 miliony testů: 

  • Přepínač: 1,842 sekund
  • Pokud: 0.344 sekund.

Výsledky příkazu switch navíc odhalují (nepřekvapivě), že delší názvy trvají déle.

1 milion testů 

  • Bob: 0,612 sekund. 
  • Jill: 0,835 sekund. 
  • Marko: 1,093 sekund.

Vypadá to, že "If Else" je rychlejší, alespoň scénář, který jsem vytvořil. 

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";}}
18
Greg

Za prvé, porovnáváte jablka a pomeranče. Nejdříve je třeba porovnat přepínač na typu vs přepínač na řetězci, a pak na typu vs na řetězci a pak porovnat výherce.

Za druhé, toto je věc, pro kterou byl OO určen. V jazycích, které podporují OO, je zapínání typu (jakéhokoli druhu) kódovým pachem, který poukazuje na špatný design. Řešením je odvodit ze společné základny abstraktní nebo virtuální metodou (nebo podobnou konstrukcí, v závislosti na vašem jazyce)

např.

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
    }
}

Potom namísto provedení příkazu switch stačí zavolat childNode.Action ()

17
ilitirit

Příkaz Switch je rychlejší než příkaz if-else-if. To je způsobeno schopností kompilátoru optimalizovat příkaz switch. V případě žebříčku if-else-if musí kód zpracovat každý příkaz if v pořadí určeném programátorem. Protože však každý případ v příkazu switch nespoléhá na dřívější případy, kompilátor je schopen znovu objednat testování takovým způsobem, aby poskytoval nejrychlejší provedení.

12
Nescio

Pokud máte vytvořené třídy, navrhl bych namísto přepínače nebo jinak použít návrhový vzor strategie.

6
Gary Kephart

Zkuste použít výčet pro každý objekt, můžete zapnout enumy rychle a snadno.

4
Rick Minerich

Pokud jste to již nepsali a zjistíte, že máte problém s výkonností, neměla bych si starosti, což je rychlejší. Jděte s tím, co je čitelnější. Pamatujte si: "Předčasná optimalizace je kořenem všeho zla." - Donalde Knuthovi

3
Chris Upchurch

Konstrukce SWITCH byla původně určena pro celočíselná data; jeho záměrem bylo použít argument přímo jako index do "dispečerské tabulky", tabulky ukazatelů. Jako takový by existovala jediná zkouška, pak se spouští přímo na příslušný kód, spíše než na řadu testů.

Potíž je v tom, že jeho použití bylo zobecněno na "stringové" typy, které samozřejmě nelze použít jako index, a veškerá výhoda konstrukce SWITCH je ztracena.

Pokud je rychlost vaším zamýšleným cílem, problém NENÍ váš kód, ale struktura dat. Pokud je prostor "name" tak jednoduchý, jak ho ukážete, je lepší jej kódovat do celočíselné hodnoty (například když jsou data vytvořena, například) a použít toto celé číslo v "mnohokrát v pomalé části aplikace".

3
user17983

Pokud jsou typy, které zapínáte, primitivní typy .NET, můžete použít Type.GetTypeCode (Type), ale pokud jsou to vlastní typy, všechny se vrátí jako TypeCode.Object. 

Stejně tak může fungovat slovník s delegáty nebo třídami obsluhy.

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
}
3
Ted Elliott

Vytvořil jsem malou konzoli, abych ukázal své řešení, jen abych zdůraznil rozdíl v rychlosti. Použil jsem jiný řetězec hash algoritmus jako verze certifikátu je pomalý pro mě na runtime a duplikáty jsou nepravděpodobné, a pokud ano můj přepínač prohlášení by selhat (nikdy se stalo až do teď). Moje jedinečná metoda rozšíření hash je obsažena v kódu níže.

 Core 2 console app with output

Budu mít 29 klíšťat přes 695 klíšťat kdykoliv, zvláště při použití kritického kódu.

Se sadou řetězců z dané databáze můžete vytvořit malou aplikaci, která vytvoří konstantu v daném souboru, kterou chcete použít ve svém kódu, pokud se přidají hodnoty, stačí znovu spustit dávku a konstanty se vygenerují a vyzvednou. řešení.

  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();

        }

Vzpomínám si na čtení v několika referenčních knihách, že větvení if/else je rychlejší než příkaz switch. Trochu výzkumu Blackwasp však ukazuje, že příkaz switch je ve skutečnosti rychlejší: http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx

Ve skutečnosti, pokud porovnáváte typická 3 až 10 (nebo tak) prohlášení, vážně pochybuji, že existuje nějaký skutečný nárůst výkonu pomocí jednoho nebo druhého.

Jak už Chris řekl, jděte pro čitelnost: Co je rychlejší, zapněte řetězec nebo jinak na typu?

2
Metro Smurf

Switch () bude kompilovat kód ekvivalentní sadě jiných ifs. Porovnání řetězců bude mnohem pomalejší než porovnání typu.

2
moonshadow

Myslím, že hlavním problémem je, že v bloku přepínačů porovnáváte řetězce a to v bloku if-else, kontrolujete typy ... Ty dva nejsou stejné, a proto bych vám říkal 're "porovnání brambor s banány".

Začnu srovnáním:

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")
{}
2
SaguiItay

Nejsem si jistý, jak rychlejší by mohl být správný design, kdyby šlo o polymorfismus. 

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();
}

Když zjistíte, co váš příkaz switch změní, pomůže vám to lépe. Pokud vaše funkce není ve skutečnosti nic o akci na typu, možná byste mohli definovat výčet na každém typu.

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();
    }
}

Předpokládám, že to musí být rychlejší než oba zmíněné přístupy. Možná budete chtít vyzkoušet abstraktní třídní trasu pokud na vás nanosekundy záleží .

2
nawfal

Právě jsem četl seznam odpovědí a chtěl jsem sdílet tento benchmark test který porovnává switch konstrukt s operátory if-else a ternary ?.

To, co se mi líbí na ten post je to nejen porovnává jedno-levé konstrukty (např. if-else), ale dvojité a trojnásobné úrovně konstruktů (např. if-else-if-else).

Podle výsledků byl konstrukt if-else nejrychlejší v 8/9 testovacích případech; konstrukce switch svázaná pro nejrychlejší v 5/9 testovacích případech. 

Pokud tedy hledáte rychlost if-else, zdá se být nejrychlejší cestou.

0
Thrawn Wannabe

Nezapomeňte, že profiler je váš přítel. Většina dohadů je většinou ztráta času. BTW, měl jsem dobré zkušenosti s JetBrains ' dotTrace profilerem. 

0
Eddie Velasquez

Zapnutí řetězce se v podstatě zkomplikuje do žebříčku if-else-if. Zkuste dekompilovat jednoduchý. V každém případě by testovací řetězec equailty měl být levnější, protože je internován a vše, co by bylo potřeba, je referenční kontrola. Udělejte, co má smysl z hlediska udržovatelnosti; Pokud používáte řetězce, proveďte přepínač řetězce. Pokud vyberete podle typu, je vhodnější typový žebřík.

0
nimish

Porovnání řetězců bude vždy zcela záviset na běhovém prostředí (pokud řetězce nejsou staticky přiděleny, i když je potřeba porovnávat ty navzájem diskutabilní). Porovnání typů však lze provést pomocí dynamické nebo statické vazby a v každém případě je efektivnější pro běhové prostředí než porovnání jednotlivých znaků v řetězci.

0
Magsol

Přepínač na Stringu by se určitě zkompiloval na porovnání se Stringem (jeden na případ), který je pomalejší než porovnání typu (a mnohem pomalejší než typické celočíselné porovnání, které se používá pro přepínač/case)?

0
JeeBee

Tak trochu to dělám trochu jinak, Řetězy, které zapínáte, budou konstanty, takže můžete předpovědět hodnoty při kompilaci. 

ve vašem případě bych použil hodnoty hash, to je přepínač int, máte 2 možnosti, použijte časové konstanty kompilace nebo vypočítejte v době běhu.

//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;
   }
}

Metoda rozšíření pro GetUniquetHashCode může být něco takového:

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);
        }


    }

Zdroj tohoto kódu byl publikován zde . změny a nejsou relevantní. Vezměte prosím na vědomí, že jsem nastavil hodnotu tagu objektu uzlu, mohl jsem použít libovolnou vlastnost nebo přidat jednu, jen se ujistěte, že jsou v synchronizaci se skutečným textem. 

Pracuji na systémech s nízkou latencí a všechny mé kódy přicházejí jako řetězec příkazů: value, command: value .... 

nyní jsou všechny příkazy označovány jako 64bitové celočíselné hodnoty, takže přepínání takhle šetří čas CPU. 

Jedním z problémů, které máte s přepínačem, je použití řetězců, jako je "Bob", což způsobí mnohem více cyklů a řádků v kompilovaném kódu. Generovaný IL bude muset deklarovat řetězec, nastavit ho na "Bob" a pak jej použít ve srovnání. Takže s tímto vědomím budou vaše příkazy IF běžet rychleji.

PS. Aeonův příklad nebude fungovat, protože nemůžete zapnout Typy. (Ne Nevím proč přesně, ale vyzkoušeli jsme to, že to nefunguje. Má to co dělat s proměnným typu)

Pokud to chcete otestovat, vytvořte si samostatnou aplikaci a vytvořte dvě jednoduché metody, které dělají to, co je napsáno výše, a pro zobrazení IL použijte něco jako Ildasm.exe. Všimnete si mnohem méně řádků v příkazu IF metody IL.

Ildasm je dodáván s VisualStudio ... 

ILDASM stránka - http://msdn.Microsoft.com/en-us/library/f7dy01k1(VS.80).aspx

ILDASM Tutoriál - http://msdn.Microsoft.com/en-us/library/aa309387(VS.71).aspx

0
Joe

Tři myšlenky:

1) Pokud se chystáte udělat něco jiného na základě typů objektů, může to mít smysl přesunout toto chování do těchto tříd. Pak místo přepínače nebo if-else by jste zavolali childNode.DoSomething (). 

2) Porovnání typů bude mnohem rychlejší než porovnávání řetězců.

3) V designu if-else můžete být schopni využít přeskupení testů. Pokud objekty "Jill" tvoří 90% objektů, které tam procházejí, proveďte nejprve test.

0
Mark Bessey