What is the relative performance difference of if/else versus switch statement in Java?
Asked Answered
P

11

148

Worrying about my web application's performances, I am wondering which of "if/else" or switch statement is better regarding performance?

Pharmacopsychosis answered 18/1, 2010 at 14:9 Comment(11)
Do you have any reason to think the same bytecode is not generated for the two constructs?Euton
@Pascal: there might be optimization done by using table look-ups instead of a list of if etc.Lupulin
"Premature optimization is the root of all evil" - Donald KnuthPerimeter
You should give this a read.Statolith
While this is definitely premature optimization, "Mindless adherence to a quote taken badly out of context is the reason we need a high-end multi-core computer just to display a reasonably responsive GUI today" - Me.Monolayer
These questions will never stop.Closing
Knuth has a precise mind. Please note the qualifier "premature". Optimization is a perfectly valid concern. That said, a server is IO bound and the bottlenecks of network and disk I/O are orders of magnitude more significant than anything else you have going on in your server.Ventilate
@PascalCuoq javap and the javac source code: https://mcmap.net/q/47243/-difference-between-jvm-39-s-lookupswitch-and-tableswitchVestige
Try using caliper to microbenchmark your code. If you're doing it for optimization then I would agree with the sentiment of others that it isn't a good idea, but as an educational exercise it would be nice to see. Check trajano.net/2014/08/… for an exampleMartyr
@LawrenceDol This question is a decade late but is there a story you're referring to with the high-end computer GUI comment?Utrecht
@Utrecht : No story. Just making the counterpoint to the Knuth quote which, as usual, is stripped of its context and used as a blunt excuse to write crappy code.Monolayer
K
120

That's micro optimization and premature optimization, which are evil. Rather worry about readabililty and maintainability of the code in question. If there are more than two if/else blocks glued together or its size is unpredictable, then you may highly consider a switch statement.

Alternatively, you can also grab Polymorphism. First create some interface:

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

And get hold of all implementations in some Map. You can do this either statically or dynamically:

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

Finally replace the if/else or switch by something like this (leaving trivial checks like nullpointers aside):

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

It might be microslower than if/else or switch, but the code is at least far better maintainable.

As you're talking about webapplications, you can make use of HttpServletRequest#getPathInfo() as action key (eventually write some more code to split the last part of pathinfo away in a loop until an action is found). You can find here similar answers:

If you're worrying about Java EE webapplication performance in general, then you may find this article useful as well. There are other areas which gives a much more performance gain than only (micro)optimizing the raw Java code.

Konyn answered 18/1, 2010 at 14:11 Comment(6)
or consider polymorphism insteadSammons
That's indeed more recommended in case of "unpredictable" amount of if/else blocks.Konyn
I'm not so quick to dismiss all early optimization as "evil". Being too aggressive is foolish, but when faced with constructs of comparable readability choosing one known to perform better is an appropriate decision.Resurrect
The HashMap lookup version can easily be 10 times slower compared to a tableswitsch instruction. I wouldn't call this "microslower"!Fumble
I mean, slower in terms of "microseconds".Konyn
I'm interested in actually knowing the inner workings of Java in the general case with switch statements - I'm not interested in whether or not somebody thinks this is related to over-prioritizing early optimization. That being said, I have absolutely no idea why this answer is upvoted so much and why it is the accepted answer...this does nothing to answer the initial question.Coolth
G
149

I totally agree with the opinion that premature optimization is something to avoid.

But it's true that the Java VM has special bytecodes which could be used for switch()'s.

See WM Spec (lookupswitch and tableswitch)

So there could be some performance gains, if the code is part of the performance CPU graph.

Gerger answered 18/1, 2010 at 14:23 Comment(2)
I wonder why this comment isn't rated higher: it is the most informitive of all of them. I mean: we all already know about premature optimalization being bad and such, no need to explain that for the 1000th time.Kalfas
+1 As of https://mcmap.net/q/46057/-why-does-java-switch-on-contiguous-ints-appear-to-run-faster-with-added-cases it seems the performance gains are really there, and you should see an advantage if you use 18+ cases.Impostume
K
120

That's micro optimization and premature optimization, which are evil. Rather worry about readabililty and maintainability of the code in question. If there are more than two if/else blocks glued together or its size is unpredictable, then you may highly consider a switch statement.

Alternatively, you can also grab Polymorphism. First create some interface:

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

And get hold of all implementations in some Map. You can do this either statically or dynamically:

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

Finally replace the if/else or switch by something like this (leaving trivial checks like nullpointers aside):

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

It might be microslower than if/else or switch, but the code is at least far better maintainable.

As you're talking about webapplications, you can make use of HttpServletRequest#getPathInfo() as action key (eventually write some more code to split the last part of pathinfo away in a loop until an action is found). You can find here similar answers:

If you're worrying about Java EE webapplication performance in general, then you may find this article useful as well. There are other areas which gives a much more performance gain than only (micro)optimizing the raw Java code.

Konyn answered 18/1, 2010 at 14:11 Comment(6)
or consider polymorphism insteadSammons
That's indeed more recommended in case of "unpredictable" amount of if/else blocks.Konyn
I'm not so quick to dismiss all early optimization as "evil". Being too aggressive is foolish, but when faced with constructs of comparable readability choosing one known to perform better is an appropriate decision.Resurrect
The HashMap lookup version can easily be 10 times slower compared to a tableswitsch instruction. I wouldn't call this "microslower"!Fumble
I mean, slower in terms of "microseconds".Konyn
I'm interested in actually knowing the inner workings of Java in the general case with switch statements - I'm not interested in whether or not somebody thinks this is related to over-prioritizing early optimization. That being said, I have absolutely no idea why this answer is upvoted so much and why it is the accepted answer...this does nothing to answer the initial question.Coolth
C
52

It's extremely unlikely that an if/else or a switch is going to be the source of your performance woes. If you're having performance problems, you should do a performance profiling analysis first to determine where the slow spots are. Premature optimization is the root of all evil!

Nevertheless, it's possible to talk about the relative performance of switch vs. if/else with the Java compiler optimizations. First note that in Java, switch statements operate on a very limited domain -- integers. In general, you can view a switch statement as follows:

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

where c_0, c_1, ..., and c_N are integral numbers that are targets of the switch statement, and <condition> must resolve to an integer expression.

  • If this set is "dense" -- that is, (max(ci) + 1 - min(ci)) / n > α, where 0 < k < α < 1, where k is larger than some empirical value, a jump table can be generated, which is highly efficient.

  • If this set is not very dense, but n >= β, a binary search tree can find the target in O(2 * log(n)) which is still efficient too.

For all other cases, a switch statement is exactly as efficient as the equivalent series of if/else statements. The precise values of α and β depend on a number of factors and are determined by the compiler's code-optimization module.

Finally, of course, if the domain of <condition> is not the integers, a switch statement is completely useless.

Cinelli answered 18/1, 2010 at 14:12 Comment(4)
+1. There is a good chance that time spent on network I/O is easily eclipsing this particular issue.Illegible
It should be noted that switches work with more than just ints. From the Java Tutorials: "A switch works with the byte, short, char, and int primitive data types. It also works with enumerated types (discussed in Enum Types), the String class, and a few special classes that wrap certain primitive types: Character, Byte, Short, and Integer (discussed in Numbers and Strings)." Support for String is more recent addition; added in Java 7. docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.htmlEllord
@jhonFeminella Could you please compare BIG O notion effects for Java7 String in Swtich compared to String in if / else if ..?Allbee
More precisely, javac 8 gives a weight of 3 to time complexity over space complexity: https://mcmap.net/q/47243/-difference-between-jvm-39-s-lookupswitch-and-tableswitchVestige
A
11

Use switch!

I hate to maintain if-else-blocks! Have a 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));
    }
}

My C# standard code for benchmarking

Ashling answered 31/7, 2013 at 7:8 Comment(9)
Could you please (sometime) elaborate a bit on how you did benchmark this?Negrete
Thank you very much for your update. I mean, they differ by one order of magnitude - wich is possible of course. Are you sure that the compiler did not just optimze the switches away?Negrete
@Negrete I don't remember how I got the old results. I got very different today. But try it yourself and let me know how it turns out.Ashling
when i run it on my laptop ; switch time needed: 3585, if/else time needed: 3458 so if/else is better :) or not worseBillings
@Billings 2nd sentence of the accepted answer is absolutely right. In your case I would take switch anyways. The times aren't that different.Ashling
You could use break instead of these else-if statements though.Unmistakable
The dominant cost in the test is the random number generation. I modified the test to generate the random number before the loop, and used the temp value to feed back into r. The switch is then almost twice as fast as the if-else chain.Picrite
Inaccurate.., the random number should be generated before and same one should be fed to both the methods.Florez
As others remarked, random is costly and hides differences in performance. I replaced int r = (int) (Math.random() * 10); with int r = (loop & 0b11111) != 0b0 ? 0 : loop % 10; and got that the if/else is three times as fast as the switch, which shows it also depends on the distribution of your data.Primateship
A
9

In my test the better performance is ENUM > MAP > SWITCH > IF/ELSE IF in 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
    }
}


/*
 * https://mcmap.net/q/47242/-why-can-39-t-i-use-switch-statement-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
 */ 
Allbee answered 19/11, 2014 at 12:20 Comment(2)
Time taken for String in Switch :3235 Time taken for String in if/else if :3143 Time taken for String in Map :4194 Time taken for String in ENUM :2866Billings
@Billings I am not sure how this code works on different environments, but you have mentioned if/elseif is better than Switch and Map, that i am not able to convince since if/elseif has to perform more no of times equals comparison.Allbee
G
8

According to Cliff Click in his 2009 Java One talk A Crash Course in Modern Hardware:

Today, performance is dominated by patterns of memory access. Cache misses dominate – memory is the new disk. [Slide 65]

You can get his full slides here.

Cliff gives an example (finishing on Slide 30) showing that even with the CPU doing register-renaming, branch prediction, and speculative execution, it's only able to start 7 operations in 4 clock cycles before having to block due to two cache misses which take 300 clock cycles to return.

So he says to speed up your program you shouldn't be looking at this sort of minor issue, but on larger ones such as whether you're making unnecessary data format conversions, such as converting "SOAP → XML → DOM → SQL → …" which "passes all the data through the cache".

Gerhart answered 18/1, 2010 at 14:32 Comment(0)
H
8

I remember reading that there are 2 kinds of Switch statements in Java bytecode. (I think it was in 'Java Performance Tuning' One is a very fast implementation which uses the switch statement's integer values to know the offset of the code to be executed. This would require all integers to be consecutive and in a well-defined range. I'm guessing that using all the values of an Enum would fall in that category too.

I agree with many other posters though... it may be premature to worry about this, unless this is very very hot code.

Hrvatska answered 18/1, 2010 at 15:42 Comment(2)
+1 for the hot code comment. If its in your main loop its not premature.Grosbeak
Yes, javac implements switch several different ways, some more efficient than others. In general, the efficiency will be no worse than a straight-forward "if ladder", but there are enough variations (especially with the JITC) that it's hard to be much more precise than that.Walter
C
2

For most switch and most if-then-else blocks, I can't imagine that there are any appreciable or significant performance related concerns.

But here's the thing: if you're using a switch block, its very use suggests that you're switching on a value taken from a set of constants known at compile time. In this case, you really shouldn't be using switch statements at all if you can use an enum with constant-specific methods.

Compared to a switch statement, an enum provides better type safety and code that is easier to maintain. Enums can be designed so that if a constant is added to the set of constants, your code won't compile without providing a constant-specific method for the new value. On the other hand, forgetting to add a new case to a switch block can sometimes only be caught at run time if you're lucky enough to have set your block up to throw an exception.

Performance between switch and an enum constant-specific method should not be significantly different, but the latter is more readable, safer, and easier to maintain.

Contagium answered 31/7, 2013 at 7:34 Comment(0)
C
1

I'm going to try to be a bit more definitive than the other answers; the answer my testing found is actually quite surprising and perhaps an important lesson in premature optimization.

As everyone has suggested, reading code and deciding that its slow and needs to be mangled such that it is more performant is a very poor method of software development. I would suggest using a profiler to find your hotloops and (as is perfectly reasonable to expect in my opinion), should your hot code be a large switch statement, trying to find a method to express that same code more performantly is a perfectly reasonable course of action. Thankfully on the JVM more performant solutions are often perfectly readable.

As per Waverick's answer, there are various different ways of formatting jumps for switch statements. Because there are different opcodes its reasonable to assume there will be different levels of performance. Conceptually it makes makes that how you express the switch would affect its performance. If we can convert our switch into a kind of table, such that picking the right if/elseif/case/etc block to execute simply requires looking up the subject in a table, we would expect it to perform better than if we have to test each condition in the table one-by-one. But this is just conjecture

So lets test it:

My Code:

JMH and 'node' code omitted, should be relatively obvious from below, though it includes some non-trivial setup.


package org.example;

public class JavaSwitchExpressions {

    public static int typeSwitchExpression(Node node) {
        return switch (node) {
            case Node.FirstNodeType first -> first.firstId();
            case Node.SecondNodeType second -> second.secondId();
            case Node.ThirdNodeType third -> third.thirdId();
            case Node.FourthNodeType fourth -> fourth.fourthId();
        };
    }

    public static int intSwitchExpression(Node node) {
        return switch(node.typeInt()){
            case 0 -> ((Node.FirstNodeType) node).firstId();
            case 1 -> ((Node.SecondNodeType) node).secondId();
            case 2 -> ((Node.ThirdNodeType) node).thirdId();
            case 3 -> ((Node.FourthNodeType) node).fourthId();
            default -> throw new UnsupportedOperationException();
        };
    }

    public static int intSwitchStatement(Node node) {
        int result = -1;

        switch(node.typeInt()){
            case 0: result = ((Node.FirstNodeType) node).firstId(); break;
            case 1: result = ((Node.SecondNodeType) node).secondId(); break;
            case 2: result = ((Node.ThirdNodeType) node).thirdId(); break;
            case 3: result = ((Node.FourthNodeType) node).fourthId(); break;
        }
        return result;
    }

    public static int typeIfInstanceofElseIf(Node node) {
        int result = -1;

        if(node instanceof Node.FirstNodeType first){
            result = first.firstId();
        }
        else if (node instanceof Node.SecondNodeType second){
            result = second.secondId();
        }
        else if (node instanceof Node.ThirdNodeType third){
            result = third.thirdId();
        }
        else if (node instanceof Node.FourthNodeType fourth){
            result = fourth.fourthId();
        }

        return result;
    }

}

and in my case I use a lot of Kotlin, so I threw in some Kotlin code:

fun typeSwitchExpression(node: KtNode): Int {
    return when(node){
        is KtNode.FirstNodeType -> node.firstId
        is KtNode.SecondNodeType -> node.secondId
        is KtNode.ThirdNodeType -> node.thirdId
        is KtNode.FourthNodeType -> node.fourthId
    }
}

fun intSwitchExpression(node: KtNode): Int {
    return when(node.typeInt){
        0 -> (node as KtNode.FirstNodeType).firstId
        1 -> (node as KtNode.SecondNodeType).secondId
        2 -> (node as KtNode.ThirdNodeType).thirdId
        3 -> (node as KtNode.FourthNodeType).fourthId
        else -> TODO()
    }
}

fun ifIsElseIfExpression(node: KtNode): Int {
    return if(node is KtNode.FirstNodeType){
        node.firstId
    }
    else if(node is KtNode.SecondNodeType){
        node.secondId
    }
    else if (node is KtNode.ThirdNodeType){
        node.thirdId
    }
    else if (node is KtNode.FourthNodeType){
        node.fourthId
    }
    else TODO()
}

Some notes about compiled code:

when using a java type switch expression typeSwitchExpression, we see some cool new code to format class objects as labels for a regular TableSwitch:

INVOKEDYNAMIC typeSwitch(Ljava/lang/Object;I)I [
  // handle kind 0x6 : INVOKESTATIC
  java/lang/runtime/SwitchBootstraps.typeSwitch(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  // arguments:
  org.example.Node$FirstNodeType.class, 
  org.example.Node$SecondNodetype.class, 
  org.example.Node$ThirdNodeType.class, 
  org.example.Node$FourthNodeType.class
]
TABLESWITCH
  0: L2
  1: L3
  2: L4
  3: L5
  default: L6
L6

note the method SwitchBootstraps.typeSwitch appears to be called once at linker time, not once per-switch time. So presumably we're seeing an increased in class-load time for the typeswitch than we would for the int switches. That delay wont be counted here and is, hopefully, reasonably trivial.

all ...intSwitch generate code akin to:

INVOKEINTERFACE org/example/Node.typeInt ()I (itf)
TABLESWITCH
  0: L3
  1: L4
  2: L5
  3: L6
  default: L7

which I presume would be the fastest as its the most elegant,

and the Ifis/InstanceOfElseIf as well as the kotlin type switch, all generate code akin to:

   L1
    LINENUMBER 30 L1
    ALOAD 0
    INSTANCEOF org/example/Node$FirstNodeType
    IFEQ L2
    ALOAD 0
    CHECKCAST org/example/Node$FirstNodeType
    ASTORE 2
   L3
    LINENUMBER 31 L3
    ALOAD 2
    INVOKEVIRTUAL org/example/Node$FirstNodeType.firstId ()I
    ISTORE 1
    GOTO L4
   L2
    LINENUMBER 33 L2
   FRAME APPEND [I]
    ALOAD 0
    INSTANCEOF org/example/Node$SecondNodeType
    IFEQ L5
    ALOAD 0
    CHECKCAST org/example/Node$SecondNodeType
    ASTORE 3
   L6
    LINENUMBER 34 L6
    ALOAD 3
    INVOKEVIRTUAL org/example/Node$SecondNodeType.secondId ()I
   ...

Reuslts:

Well, in a surprising turn, the if instanceof Else if performs the best. My initial testing revealed that they all perform very similarly, but after reading the docs for JMH and switching my testing strategy around a bit (from this to this) the performance reuslts opened up a bit.

Benchmark                                            Mode  Cnt    Score    Error   Units
SwitchBenchmarks.measureJavaIntSwitchExpression     thrpt    5   52.469 �  3.233  ops/us
SwitchBenchmarks.measureJavaIntSwitchStatement      thrpt    5   52.551 �  7.293  ops/us
SwitchBenchmarks.measureJavaTypeIfInstanceofElseIf  thrpt    5  385.904 � 24.989  ops/us
SwitchBenchmarks.measureJavaTypeSwitchExpression    thrpt    5  169.941 � 15.782  ops/us
SwitchBenchmarks.measureKotlinIfIsElseIf            thrpt    5  362.322 � 93.074  ops/us
SwitchBenchmarks.measureKotlinIntSwitchExpression   thrpt    5   52.228 �  6.920  ops/us
SwitchBenchmarks.measureKotlinTypeSwitchExpression  thrpt    5  379.040 � 43.105  ops/us

The type switch wins? With its low-performance procedural if ... else if ... else? Seems unintuitive.

Why would the procedural is instanceof else if be faster than an int switch? Its hard to know, but I suspect that the JVM' JIT compiler has heuristics specifically targeting that case and is able to find some fast optimization for it, and that the performance of that optimization is able to out perform whatever normal optimizations are done for an InvokeInterface (on typeInt) and TableSwitch.

Or maybe my entire test setup is botched. Try it yourself!

Key takeaways

  • Premature optimization is a bad idea. With so many layers between you and the runtime it is easy to speculate incorrectly about whats fastest (EG: TableSwitch must be faster than a chain of InstanceOf, right?)

  • The JVM and its JIT are one of the main attractions of java. They are quite good at their jobs. They are written for mainline cases. The more you can make your code look like everybody else's java the faster its likely to be.

  • I think the entire purpose of performance on the JVM is to focus on reducing the time complexity of your algorithms by doing things like moving from full Cartesian products (IE big joins like for(outer in data){ for(inner in data) { ... }}) to multiple passes with smart data structures. Hashing data structures, better caches should be your performance bread and butter. When they're not enough, dig up some algorithms text books and look for clever encoding schemes to moving things into stack primitives. If that still isn't enough, well your beyond my skill to help you, but replacing your switch statements probably isn't the best approach.


tl;dr don't worry about the performance of your switch expressions.

Camus answered 7/9, 2023 at 0:47 Comment(0)
B
0

A good explanation at the link below:
https://www.geeksforgeeks.org/switch-vs-else/

Test(c++17)
1 - If grouped
2 - If sequential
3 - Goto Array
4 - Switch Case - Jump Table
https://onlinegdb.com/Su7HNEBeG

Bascinet answered 4/1, 2021 at 2:7 Comment(0)
C
0

Speed: A switch statement might prove to be faster than ifs provided number of cases are good. If there are only few cases, it might not effect the speed in any case. Prefer switch if the number of cases are more than 5 otherwise, you may use if-else too. If a switch contains more than five items, it’s implemented using a lookup table or a hash list. This means that all items get the same access time, compared to a list of if:s where the last item takes much more time to reach as it has to evaluate every previous condition first. Clarity in readability: A switch looks much cleaner when you have to combine cases. Ifs are quite vulnerable to errors too. Missing an else statement can land you up in havoc. Adding/removing labels is also easier with a switch and makes your code significantly easier to change and maintain.

Example:-> String environment="QA";

    switch(environment.toLowerCase().trim()) {
    case "qa":
        System.out.println(environment+" :"+"Environment running the TestCases");
        break;
    case "Stage":
        System.out.println(environment+" :"+"Environment running the TestCases");
        break;
    case "Dev":
        System.out.println(environment+" :"+"Environment running the TestCases");
        break;
    case "UAT":
        System.out.println(environment+" :"+"Environment running the TestCases");
        break;
    case "Prod":
        System.out.println(environment+" :"+"Environment running the TestCases");
        break;
    default:
        System.out.println(environment+" :"+"Please pass the right Environment");
        break;
    }
    
    String browser="Chrome";
    
    if (browser.equals("chrome")) {
        System.out.println(browser + ": " + " Launch the Browser");
    } else if (browser.equals("safari")) {
        System.out.println(browser + ": " + " Launch the Browser");
    } else if (browser.equals("IE")) {
        System.out.println(browser + ": " + " Launch the Browser");
    } else if (browser.equals("opera")) {
        System.out.println(browser + ": " + " Launch the Browser");

    } else if (browser.equals("Edge")) {
        System.out.println(browser + ": " + " Launch the Browser");

    } else {
        System.out.println("Please pass the right browser");
    }
        
Chilt answered 7/2, 2023 at 17:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.