Nehmen wir an, ich habe die Möglichkeit, einen Codepfad anhand eines Zeichenfolgenvergleichs zu identifizieren oder den Typ zu bestimmen:
Welches ist schneller und warum?
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: Der Hauptgrund, warum ich dies frage, ist, dass die switch-Anweisung genau angibt, was als Fall gilt. Beispielsweise können Sie keine Variablen verwenden, sondern nur Konstanten, die in die Hauptassembly verschoben werden. Ich nahm an, dass es diese Einschränkung aufgrund einiger funky Sachen gab, die es tat. Wenn es nur in elseifs übersetzt wird (wie ein Poster kommentierte), warum sind dann in case-Anweisungen keine Variablen zulässig?
Vorsichtsmaßnahme: Ich optimiere nach. Diese Methode wird aufgerufen , viele Male in einem langsamen Teil der App.
c#
performance
Streit
quelle
quelle
Antworten:
Gregs Profilergebnisse sind für das genaue Szenario, das er abdeckte, großartig, aber interessanterweise ändern sich die relativen Kosten der verschiedenen Methoden dramatisch, wenn eine Reihe verschiedener Faktoren berücksichtigt werden, einschließlich der Anzahl der verglichenen Typen sowie der relativen Häufigkeit und aller Muster in den zugrunde liegenden Daten .
Die einfache Antwort lautet: Niemand kann Ihnen sagen, wie hoch der Leistungsunterschied in Ihrem speziellen Szenario sein wird. Sie müssen die Leistung in Ihrem eigenen System auf unterschiedliche Weise selbst messen, um eine genaue Antwort zu erhalten.
Die If / Else-Kette ist ein effektiver Ansatz für eine kleine Anzahl von Typvergleichen oder wenn Sie zuverlässig vorhersagen können, welche wenigen Typen die Mehrheit der angezeigten Typen ausmachen werden. Das potenzielle Problem bei diesem Ansatz besteht darin, dass mit zunehmender Anzahl von Typen auch die Anzahl der Vergleiche zunimmt, die ausgeführt werden müssen.
wenn ich folgendes ausführe:
int value = 25124; if(value == 0) ... else if (value == 1) ... else if (value == 2) ... ... else if (value == 25124) ...
Jede der vorherigen if-Bedingungen muss ausgewertet werden, bevor der richtige Block eingegeben wird. Andererseits
switch(value) { case 0:...break; case 1:...break; case 2:...break; ... case 25124:...break; }
führt einen einfachen Sprung zum richtigen Codebit durch.
In Ihrem Beispiel wird es komplizierter, wenn Ihre andere Methode einen Schalter für Zeichenfolgen anstelle von Ganzzahlen verwendet, was etwas komplizierter wird. Auf einer niedrigen Ebene können Zeichenfolgen nicht auf die gleiche Weise wie ganzzahlige Werte eingeschaltet werden, sodass der C # -Compiler etwas Magie ausübt, damit dies für Sie funktioniert.
Wenn die switch-Anweisung "klein genug" ist (wobei der Compiler automatisch das tut, was er für am besten hält), generiert das Einschalten von Zeichenfolgen Code, der mit einer if / else-Kette identisch ist.
switch(someString) { case "Foo": DoFoo(); break; case "Bar": DoBar(); break; default: DoOther; break; }
ist das gleiche wie:
if(someString == "Foo") { DoFoo(); } else if(someString == "Bar") { DoBar(); } else { DoOther(); }
Sobald die Liste der Elemente im Wörterbuch "groß genug" wird, erstellt der Compiler automatisch ein internes Wörterbuch, das die Zeichenfolgen im Switch einem ganzzahligen Index und anschließend einem auf diesem Index basierenden Switch zuordnet.
Es sieht ungefähr so aus (Stellen Sie sich mehr Einträge vor, als ich tippen werde)
Ein statisches Feld wird an einem "versteckten" Ort definiert, der der Klasse zugeordnet ist, die die switch-Anweisung vom Typ enthält
Dictionary<string, int>
und einen verstümmelten Namen erhält//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(); }
In einigen Schnelltests, die ich gerade ausgeführt habe, ist die If / Else-Methode ungefähr dreimal so schnell wie der Wechsel für 3 verschiedene Typen (wobei die Typen zufällig verteilt sind). Bei 25 Typen ist der Switch um einen kleinen Abstand (16%) schneller. Bei 50 Typen ist der Switch mehr als doppelt so schnell.
Wenn Sie eine große Anzahl von Typen einschalten möchten, würde ich eine dritte Methode vorschlagen:
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... } }
Dies ähnelt dem, was Ted Elliot vorgeschlagen hat, aber die Verwendung von Laufzeit-Typ-Handles anstelle von Objekten vom vollständigen Typ vermeidet den Aufwand für das Laden des Typ-Objekts durch Reflexion.
Hier sind einige kurze Zeitangaben auf meiner Maschine:
Zumindest auf meinem Computer übertrifft der Typ-Handle-Wörterbuch-Ansatz alle anderen für mehr als 15 verschiedene Typen, wenn die Verteilung der als Eingabe für die Methode verwendeten Typen zufällig ist.
Wenn andererseits die Eingabe vollständig aus dem Typ besteht, der zuerst in der if / else-Kette überprüft wird, ist diese Methode viel schneller:
Wenn umgekehrt die Eingabe immer das Letzte in der if / else-Kette ist, hat dies den gegenteiligen Effekt:
Wenn Sie einige Annahmen über Ihre Eingabe treffen können, erzielen Sie möglicherweise die beste Leistung mit einem hybriden Ansatz, bei dem Sie die wenigen am häufigsten verwendeten Typen prüfen und dann auf einen wörterbuchgesteuerten Ansatz zurückgreifen, wenn diese fehlschlagen.
quelle
Zunächst vergleichen Sie Äpfel und Orangen. Sie müssen zuerst den Einschalttyp mit dem Einschaltstring und dann den Typ mit dem String vergleichen und dann die Gewinner vergleichen.
Zweitens ist dies das, wofür OO entwickelt wurde. In Sprachen, die OO unterstützen, ist das Einschalten des Typs (jeglicher Art) ein Codegeruch, der auf ein schlechtes Design hinweist. Die Lösung besteht darin, von einer gemeinsamen Basis mit einer abstrakten oder virtuellen Methode (oder einem ähnlichen Konstrukt, abhängig von Ihrer Sprache) abzuleiten.
z.B.
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 } }
Anstatt die switch-Anweisung auszuführen, rufen Sie einfach childNode.Action () auf.
quelle
Node
ausgewählt und instanziiert wurde (z. B. die Factory).Ich habe gerade eine Schnelltestanwendung implementiert und sie mit ANTS 4 profiliert.
Spezifikation: .Net 3.5 sp1 in 32-Bit-Windows XP, Code im Release-Modus.
3 Millionen Tests:
Darüber hinaus zeigen die Ergebnisse der switch-Anweisung (nicht überraschend), dass längere Namen länger dauern.
1 Million Tests
Ich sehe aus wie das "Wenn sonst" schneller ist, zumindest das Szenario, das ich erstellt habe.
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";}}
quelle
Die Switch-Anweisung ist schneller auszuführen als die if-else-if-Leiter. Dies liegt an der Fähigkeit des Compilers, die switch-Anweisung zu optimieren. Im Fall der if-else-if-Leiter muss der Code jede if-Anweisung in der vom Programmierer festgelegten Reihenfolge verarbeiten. Da jedoch jeder Fall innerhalb einer switch-Anweisung nicht auf früheren Fällen beruht, kann der Compiler die Tests so neu anordnen, dass die schnellste Ausführung möglich ist.
quelle
Wenn Sie die Klassen erstellt haben, würde ich vorschlagen, ein Strategie-Entwurfsmuster anstelle von switch oder elseif zu verwenden.
quelle
Versuchen Sie, für jedes Objekt Aufzählungen zu verwenden. Sie können Aufzählungen schnell und einfach aktivieren.
quelle
Wenn Sie dies nicht bereits geschrieben haben und feststellen, dass Sie ein Leistungsproblem haben, würde ich mir keine Sorgen machen, welches schneller ist. Gehen Sie mit dem, der besser lesbar ist. Denken Sie daran: "Vorzeitige Optimierung ist die Wurzel allen Übels." - Donald Knuth
quelle
Ein SWITCH-Konstrukt war ursprünglich für ganzzahlige Daten vorgesehen. Es war beabsichtigt, das Argument direkt als Index für eine "Versandtabelle", eine Zeigertabelle, zu verwenden. Als solches würde es einen einzelnen Test geben und dann direkt zum relevanten Code starten, anstatt eine Reihe von Tests.
Die Schwierigkeit hierbei ist, dass seine Verwendung auf "String" -Typen verallgemeinert wurde, die offensichtlich nicht als Index verwendet werden können, und dass alle Vorteile des SWITCH-Konstrukts verloren gehen.
Wenn Geschwindigkeit Ihr beabsichtigtes Ziel ist, ist das Problem NICHT Ihr Code, sondern Ihre Datenstruktur. Wenn der Bereich "Name" so einfach ist, wie Sie ihn anzeigen, codieren Sie ihn besser in einen ganzzahligen Wert (z. B. wenn Daten erstellt werden) und verwenden Sie diese Ganzzahl im Abschnitt "Viele Male in einem langsamen Teil der App".
quelle
Wenn es sich bei den eingeschalteten Typen um primitive .NET-Typen handelt, können Sie Type.GetTypeCode (Type) verwenden. Wenn es sich jedoch um benutzerdefinierte Typen handelt, werden alle als TypeCode.Object zurückgegeben.
Ein Wörterbuch mit Delegaten oder Handlerklassen könnte ebenfalls funktionieren.
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 }
quelle
Der Schalter () wird zu Code kompiliert, der einer Reihe anderer ifs entspricht. Die Zeichenfolgenvergleiche sind viel langsamer als die Typvergleiche.
quelle
Ich erinnere mich, dass ich in mehreren Nachschlagewerken gelesen habe, dass die Verzweigung von if / else schneller ist als die switch-Anweisung. Ein wenig Forschung über Blackwasp zeigt jedoch, dass die switch-Anweisung tatsächlich schneller ist: http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx
Wenn Sie in der Realität die typischen 3 bis 10 (oder so) Aussagen vergleichen, bezweifle ich ernsthaft, dass es mit der einen oder anderen einen echten Leistungsgewinn gibt.
Wie Chris bereits gesagt hat, achten Sie auf Lesbarkeit: Was geht schneller, schalten Sie den String ein oder sonst den Typ?
quelle
Ich denke, das Hauptproblem bei der Leistung ist, dass Sie im Switch-Block Zeichenfolgen vergleichen und im if-else-Block nach Typen suchen ... Diese beiden sind nicht gleich, und deshalb würde ich Ihnen sagen "Kartoffeln mit Bananen vergleichen".
Ich würde damit beginnen, dies zu vergleichen:
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") {}
quelle
Ich bin mir nicht sicher, wie schnell das richtige Design für Polymorphismus sein könnte.
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(); }
Zu sehen, was Ihre switch-Anweisung bewirkt, hilft besser. Wenn Ihre Funktion nicht wirklich etwas mit einer Aktion für den Typ zu tun hat, können Sie möglicherweise für jeden Typ eine Aufzählung definieren.
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(); } }
Ich gehe davon aus, dass dies schneller sein muss als beide fraglichen Ansätze. Vielleicht möchten Sie die abstrakte Klassenroute ausprobieren, wenn Nanosekunden für Sie von Bedeutung sind .
quelle
Ich habe eine kleine Konsole erstellt, um meine Lösung zu zeigen, nur um den Geschwindigkeitsunterschied hervorzuheben. Ich habe einen anderen String-Hash-Algorithmus verwendet, da die Zertifikatversion zur Laufzeit für mich zu langsam ist und Duplikate unwahrscheinlich sind. In diesem Fall würde meine switch-Anweisung fehlschlagen (bisher noch nie geschehen). Meine einzigartige Hash-Erweiterungsmethode ist im folgenden Code enthalten.
Ich werde jederzeit 29 Ticks über 695 Ticks nehmen, insbesondere wenn ich kritischen Code verwende.
Mit einer Reihe von Zeichenfolgen aus einer bestimmten Datenbank können Sie eine kleine Anwendung erstellen, um die Konstante in einer bestimmten Datei zu erstellen, die Sie in Ihrem Code verwenden können. Wenn Werte hinzugefügt werden, führen Sie einfach Ihren Stapel erneut aus und Konstanten werden von generiert und übernommen die Lösung.
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(); }
quelle
Der Zeichenfolgenvergleich hängt immer vollständig von der Laufzeitumgebung ab (es sei denn, die Zeichenfolgen sind statisch zugeordnet, obwohl die Notwendigkeit, diese miteinander zu vergleichen, umstritten ist). Der Typvergleich kann jedoch durch dynamische oder statische Bindung erfolgen. In beiden Fällen ist er für die Laufzeitumgebung effizienter als der Vergleich einzelner Zeichen in einer Zeichenfolge.
quelle
Sicherlich würde der Einschalt-String zu einem String-Vergleich (einer pro Fall) kompiliert, der langsamer als ein Typvergleich ist (und weitaus langsamer als der typische Ganzzahl-Vergleich, der für Schalter / Fall verwendet wird).
quelle
Drei Gedanken:
1) Wenn Sie je nach Objekttyp etwas anderes tun, ist es möglicherweise sinnvoll, dieses Verhalten in diese Klassen zu verschieben. Dann würden Sie anstelle von switch oder if-else einfach childNode.DoSomething () aufrufen.
2) Der Vergleich von Typen ist viel schneller als der Vergleich von Zeichenfolgen.
3) Im if-else-Design können Sie möglicherweise die Möglichkeit nutzen, die Tests neu zu ordnen. Wenn "Jill" -Objekte 90% der dort durchlaufenden Objekte ausmachen, testen Sie sie zuerst.
quelle
Eines der Probleme, die Sie mit dem Schalter haben, ist die Verwendung von Zeichenfolgen wie "Bob". Dies führt zu viel mehr Zyklen und Zeilen im kompilierten Code. Die generierte IL muss eine Zeichenfolge deklarieren, auf "Bob" setzen und dann im Vergleich verwenden. In diesem Sinne werden Ihre IF-Anweisungen schneller ausgeführt.
PS. Das Beispiel von Aeon funktioniert nicht, da Sie Typen nicht einschalten können. (Nein, ich weiß nicht genau warum, aber wir haben es versucht und es funktioniert nicht. Es hat damit zu tun, dass der Typ variabel ist.)
Wenn Sie dies testen möchten, erstellen Sie einfach eine separate Anwendung und erstellen Sie zwei einfache Methoden, die das tun, was oben beschrieben wurde, und verwenden Sie etwas wie Ildasm.exe, um die IL anzuzeigen. Sie werden viel weniger Zeilen in der IL der IF-Anweisungsmethode bemerken.
Ildasm kommt mit VisualStudio ...
ILDASM Seite - http://msdn.microsoft.com/en-us/library/f7dy01k1(VS.80).aspx
ILDASM-Tutorial - http://msdn.microsoft.com/en-us/library/aa309387(VS.71).aspx
quelle
Denken Sie daran, der Profiler ist Ihr Freund. Vermutungen sind meistens Zeitverschwendung. Übrigens habe ich gute Erfahrungen mit dem dotTrace- Profiler von JetBrains gemacht .
quelle
Die Einschaltzeichenfolge wird im Grunde genommen zu einer If-else-If-Leiter kompiliert. Versuchen Sie, eine einfache zu dekompilieren. In jedem Fall sollte das Testen der Zeichenfolgengleichheit billiger sein, da sie interniert sind und lediglich eine Referenzprüfung erforderlich wäre. Tun Sie, was in Bezug auf die Wartbarkeit sinnvoll ist. Wenn Sie Zeichenfolgen enthalten, wechseln Sie die Zeichenfolge. Wenn Sie nach Typ auswählen, ist eine Typleiter am besten geeignet.
quelle
Ich mache es ein bisschen anders. Die Zeichenfolgen, die Sie einschalten, werden Konstanten sein, sodass Sie die Werte zur Kompilierungszeit vorhersagen können.
In Ihrem Fall würde ich die Hash-Werte verwenden, dies ist ein int-Schalter, Sie haben 2 Optionen, verwenden Kompilierungszeitkonstanten oder berechnen zur Laufzeit.
//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; } }
Die Erweiterungsmethode für GetUniquetHashCode kann ungefähr so aussehen:
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); } }
Die Quelle dieses Codes wurde veröffentlicht hier veröffentlicht Bitte beachten Sie, dass die Verwendung von Kryptografie langsam ist. Normalerweise wird die unterstützte Zeichenfolge beim Start der Anwendung aufgewärmt. Ich speichere sie in statischen Feldern, da sie sich nicht ändern und nicht instanzrelevant sind. Bitte beachten Sie, dass ich den Tag-Wert des Knotenobjekts festlege, jede Eigenschaft verwenden oder eine hinzufügen kann. Stellen Sie einfach sicher, dass diese mit dem tatsächlichen Text synchron sind.
Ich arbeite auf Systemen mit geringer Latenz und alle meine Codes werden als Befehlsfolge geliefert: Wert, Befehl: Wert ....
Jetzt sind alle Befehle als 64-Bit-Integer-Werte bekannt, sodass eine solche Umschaltung CPU-Zeit spart.
quelle
Ich habe gerade die Liste der Antworten hier durchgelesen und wollte diesen Benchmark-Test teilen , der das
switch
Konstrukt mit demif-else
und dem ternären vergleicht?
Operatoren vergleicht.Was ich an diesem Beitrag mag , ist, dass er nicht nur Einzel-Links-Konstrukte (z. B.
if-else
) vergleicht, sondern auch Doppel- und Dreifach-Konstrukte (zif-else-if-else
. B. ).Den Ergebnissen zufolge war das
if-else
Konstrukt in 8/9 Testfällen am schnellsten; dasswitch
Konstrukt war in 5/9 Testfällen am schnellsten.Wenn Sie also nach Geschwindigkeit suchen,
if-else
scheint dies der schnellste Weg zu sein.quelle
Möglicherweise fehlt mir etwas, aber können Sie nicht anstelle des Strings eine switch-Anweisung für den Typ ausführen? Das ist,
switch(childNode.Type) { case Bob: break; case Jill: break; case Marko: break; }
quelle