Quelle est la différence de performance relative entre if / else et la déclaration de switch en Java?

S’inquiétant des performances de mon application web, je me demande lequel des énoncés “if / else” ou switch est meilleur en termes de performances?

Related of "Quelle est la différence de performance relative entre if / else et la déclaration de switch en Java?"

C’est la micro-optimisation et l’optimisation prématurée, qui sont mauvaises. Préoccupez-vous plutôt de la lisibilité et de la maintenabilité du code en question. S’il y a plus de deux blocs if/else collés ensemble ou si leur taille est imprévisible, vous pouvez alors considérer une instruction switch .

Vous pouvez également saisir le polymorphism . Commencez par créer une interface:

 public interface Action { void execute(Ssortingng input); } 

Et obtenez toutes les implémentations dans certaines Map . Vous pouvez le faire de manière statique ou dynamic:

 Map actions = new HashMap(); 

Enfin, remplacez le if/else ou le switch par quelque chose comme ceci (en laissant de côté des contrôles sortingviaux comme des nullpoint):

 actions.get(name).execute(input); 

Il peut être moins puissant que if/else ou switch , mais le code est au moins beaucoup plus facile à maintenir.

Lorsque vous parlez d’applications Web, vous pouvez utiliser HttpServletRequest#getPathInfo() comme clé d’action (écrivez éventuellement du code supplémentaire pour séparer la dernière partie de pathinfo en boucle jusqu’à ce qu’une action soit trouvée). Vous pouvez trouver ici des réponses similaires:

  • Utiliser un framework orienté Servlet personnalisé, trop de servlets, est-ce un problème?
  • Java Front Controller

Si vous vous souciez des performances de l’application Web Java EE en général, cet article peut également vous être utile. Il y a d’autres domaines qui donnent un gain de performance beaucoup plus élevé que seulement l’optimisation (micro) du code Java brut.

Je suis totalement d’accord avec l’idée qu’une optimisation prématurée est quelque chose à éviter.

Mais il est vrai que la machine virtuelle Java a des bytecodes spéciaux qui pourraient être utilisés pour switch ().

Voir WM Spec ( commutateur de recherche et commutateur de table )

Il pourrait donc y avoir des gains de performance, si le code fait partie du graphique du processeur de performance.

Il est extrêmement improbable qu’un if / else ou un switch soit la source de vos problèmes de performance. Si vous rencontrez des problèmes de performances, vous devez d’abord effectuer une parsing de profilage des performances pour déterminer où se trouvent les zones lentes. L’optimisation prématurée est la racine de tout Mal!

Néanmoins, il est possible de parler de la performance relative de switch vs. if / else avec les optimisations du compilateur Java. Notez tout d’abord qu’en Java, les instructions de commutation opèrent sur un domaine très limité: les entiers. En général, vous pouvez afficher une instruction de commutation comme suit:

 switch () { case c_0: ... case c_1: ... ... case c_n: ... default: ... } 

c_0 , c_1 , … et c_N sont des nombres entiers qui sont des cibles de l’instruction switch et que doit se transformer en une expression entière.

  • Si cet ensemble est “dense” – c’est-à-dire (max (c i ) + 1 – min (c i )) / n> α, où 0 k est supérieur à une valeur empirique, une table de saut peut être générée, ce qui est très efficace.

  • Si cet ensemble n’est pas très dense, mais n> = β, un arbre de recherche binary peut trouver la cible dans O (2 * log (n)) qui est toujours aussi efficace.

Dans tous les autres cas, une instruction switch est aussi efficace que la série équivalente d’instructions if / else. Les valeurs précises de α et β dépendent d’un certain nombre de facteurs et sont déterminées par le module d’optimisation du code du compilateur.

Enfin, bien entendu, si le domaine de n’est pas les entiers, une instruction switch est totalement inutile.

Utilisez le commutateur!

Je déteste entretenir les blocs if-else! Faites un test:

 public class SpeedTestSwitch { private static void do1(int loop) { int temp = 0; for (; loop > 0; --loop) { int r = (int) (Math.random() * 10); switch (r) { case 0: temp = 9; break; case 1: temp = 8; break; case 2: temp = 7; break; case 3: temp = 6; break; case 4: temp = 5; break; case 5: temp = 4; break; case 6: temp = 3; break; case 7: temp = 2; break; case 8: temp = 1; break; case 9: temp = 0; break; } } System.out.println("ignore: " + temp); } private static void do2(int loop) { int temp = 0; for (; loop > 0; --loop) { int r = (int) (Math.random() * 10); if (r == 0) temp = 9; else if (r == 1) temp = 8; else if (r == 2) temp = 7; else if (r == 3) temp = 6; else if (r == 4) temp = 5; else if (r == 5) temp = 4; else if (r == 6) temp = 3; else if (r == 7) temp = 2; else if (r == 8) temp = 1; else if (r == 9) temp = 0; } System.out.println("ignore: " + temp); } public static void main(Ssortingng[] args) { long time; int loop = 1 * 100 * 1000 * 1000; System.out.println("warming up..."); do1(loop / 100); do2(loop / 100); System.out.println("start"); // run 1 System.out.println("switch:"); time = System.currentTimeMillis(); do1(loop); System.out.println(" -> time needed: " + (System.currentTimeMillis() - time)); // run 2 System.out.println("if/else:"); time = System.currentTimeMillis(); do2(loop); System.out.println(" -> time needed: " + (System.currentTimeMillis() - time)); } } 

Mon code standard C # pour l’parsing comparative

Je me souviens avoir lu qu’il existe 2 types d’instructions Switch dans le bytecode Java. (Je pense que c’était dans ‘Java Performance Tuning’ L’une est une implémentation très rapide qui utilise les valeurs entières de l’instruction switch pour connaître le décalage du code à exécuter. Cela nécessiterait que tous les entiers soient consécutifs et dans une plage bien définie Je suppose que l’utilisation de toutes les valeurs d’un Enum tomberait également dans cette catégorie.

Je suis d’accord avec beaucoup d’autres affiches cependant … il peut être prématuré de s’en préoccuper, à moins que ce soit un code très très chaud.

Selon Cliff Click dans son Java One talk 2009, un cours intensif sur le matériel moderne :

Aujourd’hui, les performances sont dominées par les modèles d’access à la mémoire. Les manques de cache dominent – la mémoire est le nouveau disque. [Diapositive 65]

Vous pouvez obtenir ses diapositives complètes ici .

Cliff donne un exemple (en terminant sur la diapositive 30) montrant que même avec le CPU qui fait un changement de registre, une prédiction de twig et une exécution spéculative, il ne peut lancer que 7 opérations en 4 cycles avant de devoir bloquer 300 cycles d’horloge à retourner.

Donc, il dit que pour accélérer votre programme, vous ne devriez pas regarder ce genre de problème mineur, mais sur des problèmes plus importants, comme la conversion inutile de formats de données, comme la conversion de “SOAP → XML → DOM → SQL → … “qui” transmet toutes les données à travers le cache “.

Dans mon test, la meilleure performance est ENUM> MAP> SWITCH> IF / ELSE IF sous Windows7.

 import java.util.HashMap; import java.util.Map; public class SsortingngsInSwitch { public static void main(Ssortingng[] args) { Ssortingng doSomething = null; //METHOD_1 : SWITCH long start = System.currentTimeMillis(); for (int i = 0; i < 99999999; i++) { String input = "Hello World" + (i & 0xF); switch (input) { case "Hello World0": doSomething = "Hello World0"; break; case "Hello World1": doSomething = "Hello World0"; break; case "Hello World2": doSomething = "Hello World0"; break; case "Hello World3": doSomething = "Hello World0"; break; case "Hello World4": doSomething = "Hello World0"; break; case "Hello World5": doSomething = "Hello World0"; break; case "Hello World6": doSomething = "Hello World0"; break; case "Hello World7": doSomething = "Hello World0"; break; case "Hello World8": doSomething = "Hello World0"; break; case "Hello World9": doSomething = "Hello World0"; break; case "Hello World10": doSomething = "Hello World0"; break; case "Hello World11": doSomething = "Hello World0"; break; case "Hello World12": doSomething = "Hello World0"; break; case "Hello World13": doSomething = "Hello World0"; break; case "Hello World14": doSomething = "Hello World0"; break; case "Hello World15": doSomething = "Hello World0"; break; } } System.out.println("Time taken for String in Switch :"+ (System.currentTimeMillis() - start)); //METHOD_2 : IF/ELSE IF start = System.currentTimeMillis(); for (int i = 0; i < 99999999; i++) { String input = "Hello World" + (i & 0xF); if(input.equals("Hello World0")){ doSomething = "Hello World0"; } else if(input.equals("Hello World1")){ doSomething = "Hello World0"; } else if(input.equals("Hello World2")){ doSomething = "Hello World0"; } else if(input.equals("Hello World3")){ doSomething = "Hello World0"; } else if(input.equals("Hello World4")){ doSomething = "Hello World0"; } else if(input.equals("Hello World5")){ doSomething = "Hello World0"; } else if(input.equals("Hello World6")){ doSomething = "Hello World0"; } else if(input.equals("Hello World7")){ doSomething = "Hello World0"; } else if(input.equals("Hello World8")){ doSomething = "Hello World0"; } else if(input.equals("Hello World9")){ doSomething = "Hello World0"; } else if(input.equals("Hello World10")){ doSomething = "Hello World0"; } else if(input.equals("Hello World11")){ doSomething = "Hello World0"; } else if(input.equals("Hello World12")){ doSomething = "Hello World0"; } else if(input.equals("Hello World13")){ doSomething = "Hello World0"; } else if(input.equals("Hello World14")){ doSomething = "Hello World0"; } else if(input.equals("Hello World15")){ doSomething = "Hello World0"; } } System.out.println("Time taken for String in if/else if :"+ (System.currentTimeMillis() - start)); //METHOD_3 : MAP //Create and build Map Map map = new HashMap(); for (int i = 0; i < = 15; i++) { String input = "Hello World" + (i & 0xF); map.put(input, new ExecutableClass(){ public void execute(String doSomething){ doSomething = "Hello World0"; } }); } //Start test map start = System.currentTimeMillis(); for (int i = 0; i < 99999999; i++) { String input = "Hello World" + (i & 0xF); map.get(input).execute(doSomething); } System.out.println("Time taken for String in Map :"+ (System.currentTimeMillis() - start)); //METHOD_4 : ENUM (This doesn't use muliple string with space.) start = System.currentTimeMillis(); for (int i = 0; i < 99999999; i++) { String input = "HW" + (i & 0xF); HelloWorld.valueOf(input).execute(doSomething); } System.out.println("Time taken for String in ENUM :"+ (System.currentTimeMillis() - start)); } } interface ExecutableClass { public void execute(String doSomething); } // Enum version enum HelloWorld { HW0("Hello World0"), HW1("Hello World1"), HW2("Hello World2"), HW3( "Hello World3"), HW4("Hello World4"), HW5("Hello World5"), HW6( "Hello World6"), HW7("Hello World7"), HW8("Hello World8"), HW9( "Hello World9"), HW10("Hello World10"), HW11("Hello World11"), HW12( "Hello World12"), HW13("Hello World13"), HW14("Hello World4"), HW15( "Hello World15"); private String name = null; private HelloWorld(String name) { this.name = name; } public String getName() { return name; } public void execute(String doSomething){ doSomething = "Hello World0"; } public static HelloWorld fromString(String input) { for (HelloWorld hw : HelloWorld.values()) { if (input.equals(hw.getName())) { return hw; } } return null; } } //Enum version for betterment on coding format compare to interface ExecutableClass enum HelloWorld1 { HW0("Hello World0") { public void execute(String doSomething){ doSomething = "Hello World0"; } }, HW1("Hello World1"){ public void execute(String doSomething){ doSomething = "Hello World0"; } }; private String name = null; private HelloWorld1(String name) { this.name = name; } public String getName() { return name; } public void execute(String doSomething){ // super call, nothing here } } /* * http://stackoverflow.com/questions/338206/why-cant-i-switch-on-a-string * https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.10 * http://forums.xkcd.com/viewtopic.php?f=11&t=33524 */ 

Pour la plupart des switch et la plupart des blocs if-then-else , je ne peux pas imaginer qu’il existe des problèmes de performance appréciables ou significatifs.

Mais voici la chose: si vous utilisez un bloc de switch , son utilisation même suggère que vous switch une valeur provenant d’un ensemble de constantes connues au moment de la compilation. Dans ce cas, vous ne devriez vraiment pas utiliser d’instructions switch si vous pouvez utiliser un enum avec des méthodes spécifiques à la constante.

Par rapport à une instruction de switch , une énumération fournit une sécurité de type et un code plus faciles à gérer. Les énumérations peuvent être conçues de manière à ce que si une constante est ajoutée à l’ensemble de constantes, votre code ne soit pas compilé sans fournir une méthode spécifique à la constante pour la nouvelle valeur. D’un autre côté, oublier d’append un nouveau case à un bloc de switch ne peut parfois être détecté qu’au moment de l’exécution si vous avez la chance d’avoir configuré votre bloc pour lancer une exception.

Les performances entre un switch et une méthode spécifique à une enum ne devraient pas être significativement différentes, mais cette dernière est plus lisible, plus sûre et plus facile à gérer.