Quelle est la différence de performance relative entre L'instruction if/else et l'instruction switch en Java?

en M'inquiétant des performances de mon application web, Je me demande si "if/else" ou "switch" est le meilleur en termes de performances?

104
demandé sur BalusC 2010-01-18 17:09:30

8 réponses

c'est la micro optimisation et l'optimisation prématurée, qui sont mauvaises. Plutôt s'inquiéter de la lisibilité et de la maintenabilité du code en question. S'il ya plus de deux if/else blocs collés ensemble ou sa taille est imprévisible, alors vous pouvez fortement considérer un switch énoncé.

alternativement, vous pouvez également saisir polymorphisme . D'abord créer une interface:

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

et obtenir tenir de toutes les mises en œuvre dans certains Map . Vous pouvez le faire de façon statique ou dynamique:

Map<String, Action> actions = new HashMap<String, Action>();

remplacer finalement le if/else ou switch par quelque chose comme ceci (en laissant de côté les contrôles insignifiants comme les nullpointers):

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

Il pourrait être microslower que if/else ou switch , mais le code est au moins autant mieux maintenable.

comme vous êtes en parlant d'applications Web, vous pouvez utiliser HttpServletRequest#getPathInfo() comme clé d'action (éventuellement écrire plus de code pour séparer la dernière partie de pathinfo dans une boucle jusqu'à ce qu'une action soit trouvée). Vous pouvez trouver ici des réponses similaires:

si vous vous inquiétez des performances de Java EE webapplication en général, alors vous pouvez trouver cet article aussi utile. Il ya d'autres domaines qui donne un beaucoup plus gain de performance que seulement (micro)l'optimisation du code Java brut.

101
répondu BalusC 2017-05-23 12:10:33

je suis totalement d'accord avec l'opinion que l'optimisation prématurée est quelque chose à éviter.

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

Voir WM Spec ( lookupswitch et tableswitch )

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

100
répondu Waverick 2014-05-20 15:50:21

il est extrêmement improbable qu'un if/else ou un switch soit à l'origine de vos problèmes de performance. Si vous avez des problèmes de performance, vous devriez d'abord faire une analyse de profil de performance pour déterminer où sont les points lents. L'optimisation prématurée est la racine de tous les maux!

néanmoins, il est possible de parler de la performance relative de switch vs. if/else avec les optimisations du compilateur Java. Tout d'abord, Notez Qu'en Java, les déclarations switch opérer sur un domaine très limité ... des entiers. En général, vous pouvez voir un État de commutateur comme suit:

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

c_0 , c_1 , ..., et c_N sont des nombres intégraux qui sont des cibles de l'instruction switch, et <condition> doit se résoudre à une expression entière.

  • Si cet ensemble est "dense" -- c'est, (max(c je ) + 1 - min(c je )) / n > α, Où 0 < k < α < 1, où k est plus grand qu'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 binaire peut trouver la cible dans O(2 * log(n)) qui est toujours efficace.

pour tous les autres cas, une instruction de commutation est exactement aussi efficace que la série équivalente d'instructions if/else. Le 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 sûr, si le domaine de <condition> n'est pas les entiers, un commutateur la déclaration est totalement inutile.

47
répondu John Feminella 2015-06-24 18:15:42

utilisez l'interrupteur!

je déteste maintenir SI-AUTREMENT-bloque! 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(String[] 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 C# code standard pour l'analyse comparative

9
répondu Bitterblue 2017-05-23 12:18:20

je me souviens avoir lu qu'il y avait deux types d'instructions de Switch dans Java bytecode. (Je pense que C'était dans "Java Performance Tuning" L'un 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 consécutifs et dans une plage définie. Je suppose qu'utiliser toutes les valeurs d'un Enum tomberait aussi dans cette catégorie.

je suis d'accord avec beaucoup d'autres affiches bien... il peut être prématuré de s'inquiéter à ce sujet, à moins que c'est très très chaud code.

8
répondu malaverdiere 2010-01-18 15:42:15

selon Cliff Click dans son Java One talk 2009 un cours accéléré dans le matériel moderne :

Aujourd'hui, la performance est dominée par des modèles d'accès à la mémoire. Cache manque dominate-la mémoire est le nouveau disque. [Diapositive 65]

vous pouvez obtenir ses diapositives complètes ici .

Cliff donne un exemple (finissant sur la diapositive 30) montrant que même avec le CPU faisant registre-renommer, prédiction de branche, et l'exécution spéculative, il est seulement capable de commencer 7 opérations en 4 cycles d'horloge avant d'avoir à bloquer en raison de deux erreurs de cache qui prennent 300 cycles d'horloge à retourner.

donc il dit que pour accélérer votre programme vous ne devriez pas être à la recherche de ce genre de problème mineur, mais sur les plus grands tels que si vous faites des conversions de format de données inutiles, tels que la conversion "SOAP → XML → DOM → SQL → ..." qui "passe toutes les données dans le cache".

8
répondu Jim Ferrans 2014-05-07 04:03:58

dans mon essai, la meilleure performance est ENUM > MAP > SWITCH > IF/ELSE si dans Windows7.

import java.util.HashMap;
import java.util.Map;

public class StringsInSwitch {
public static void main(String[] args) {
    String 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<String, ExecutableClass> map = new HashMap<String, ExecutableClass>();
    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
 */ 
4
répondu Kanagavelu Sugumar 2015-05-20 11:32:25

pour la plupart des blocs switch et if-then-else , Je ne peux pas imaginer qu'il y ait des préoccupations appréciables ou significatives liées au rendement.

Mais voici la chose: si vous utilisez un bloc switch , son utilisation même suggère que vous activez une valeur prise à partir d'un ensemble de constantes connues au moment de la compilation. Dans ce cas, vous ne devriez vraiment pas utiliser switch si vous pouvez utiliser un enum avec constante-spécifique méthode.

par rapport à l'énoncé switch , un enum offre une meilleure sécurité de type et un code plus facile à maintenir. Enums peut être conçu de sorte que si une constante est ajoutée à l'ensemble des constantes, votre code ne compilera pas sans fournir une méthode spécifique à la constante pour la nouvelle valeur. D'un autre côté, oublier d'ajouter un nouveau case à un switch bloc ne peut parfois être attrapé à l'Heure de course si vous avez la chance d'avoir réglé votre bloc pour lancer exception.

Performance entre switch et un enum de la constante spécifique de la méthode ne devrait pas être significativement différentes, mais le dernier est plus lisible, plus sûr et plus facile à maintenir.

2
répondu scottb 2013-07-31 07:34:26