switch vs if

I am interested to hear opinions about what the preferred way would be to implement the following logic and why:

switch
if ( dateFmt < 4 ) {
switch ( dateFmt ) {
case 0: fmt = "$1$ $2$ $3$"; break;
case 1: fmt = "$1$, $2$ $3$"; break;
case 2: fmt = "$1$ $2$ $3$."; break;
}
}


if
if ( dateFmt < 4 ) {
if (dateFmt == 0) {fmt = "$1$ $2$ $3$";}
else if (dateFmt == 1) {fmt = "$1$, $2$ $3$";}
else if (dateFmt == 2) {fmt = "$1$ $2$ $3$.";}
}


Performance and memory wise, which one is better?
  • You might want to test in the simulator for yourself, but for small numbers of conditions it might not make a difference. For a large number of conditions (like 10-20 or higher), it makes a huge difference: the switch statement might consume hundreds more bytes then the if statement. I think once you have more than a certain number of cases, switch statements are implemented using a dictionary which has a huge memory overhead.

    I saved a lot of memory in my app by replacing very large switch statements with if statements. Without doing that, my app wouldn't even have worked on older CIQ1 devices with less memory (without ripping out a lot of core functionality.)

    Also, you can display disassembled bytecode by adding -g to your compiler options, if you want to compare the code that's generated.
  • You might want to test in the simulator for yourself, but for small numbers of conditions it might not make a difference. For a large number of conditions (like 10-20 or higher), it makes a huge difference: the switch statement might consume hundreds more bytes then the if statement. I think once you have more than a certain number of cases, switch statements are implemented using a dictionary which has a huge memory overhead.

    I saved a lot of memory in my app by replacing very large switch statements with if statements. Without doing that, my app wouldn't even have worked on older CIQ1 devices with less memory (without ripping out a lot of core functionality.)


    That is interesting, this is exactly the hidden facts I am looking for. I was looking at changing my ifs to switches, but now I am glad I asked. I try to always keep code at its minimum, and also give preference to performance (speed) and low memory.
  • The switch statement performance wise is going to be better the larger your set of if-else conditions grows because each condition might need to be evaluated. So application need might be important when evaluating when to use this. For ANT or other implementations where the if-else statement might be called constantly to evaluate data pages and the if-else block sees cases spread equally over all conditions the switch might provide a noticeable improvement due to less evaluations of if conditions. That being said, for small number of items it probably doesn't matter but readability and maintenance wise a switch statement might be easier to maintain. I have not researched the memory overhead since this hasn't presented an issue in my use so far.
  • In other languages, every switch-statement is compiled into a bunch of if-statements, internally. But, there is possibly another difference:
    The layer will become higher and higher with every else if else if, which slows down the execution.
    It is recommended, to reduce nesting by using a return rather then else.
  • In other languages, every switch-statement is compiled into a bunch of if-statements, internally. But, there is possibly another difference:
    The layer will become higher and higher with every else if else if, which slows down the execution.
    It is recommended, to reduce nesting by using a return rather then else.


    The Connect IQ guys would need to elaborate but if the language optimizes the switch statement to use a jump table given scenarios with large if-else blocks and large number of evaluations that might be hit farther down the if-else block, there could be a noticeable performance difference. If we are talking less than 5 items it probably doesn't matter. If it doesn't implement a jump table it probably doesn't matter from a performance standpoint either.
  • Yes it does depend on your use-case. And in some other languages, switch statements are converted into jump tables.

    In my case some of my large switch statements are used at initialization time only, to parse complex (and free-form) configuration. I had a few options:
    - Leave the switch statements in, accept that CIQ1 devices will never work unless I rip out functionality
    - Convert the switch statements to if statements, and sacrifice performance at init time

    It's a similar trade-off with using enums/constants vs hard-coded constants Enums and constants take up lots of memory in Monkey C. (Similar to the amount of size for a regular variable.) In some languages, especially those without reflection, enums take 0 memory. I could:
    - Leave (dozens) of enums in my code, and once again take up memory unnecessarily (as much as 100-200 bytes)
    - Convert enums to hard-coded constants (with comments) which makes my code harder to maintain, but allows my app to run on older devices.
  • I like the discussion so far. Would be great to know what is the end result in compiled Monkey C: Jump table vs if else?
  • If you pass in the "-g" option to the compiler, you can see the disassembled code and compare the two situations yourself. (Eclipse > Window > Preferences > Connect IQ > compiler)

    I am 100% sure that when you have more than a few cases, switch uses much memory than the corresponding if.

  • In the case of the code posted, I think you are asking the wrong question. Something like that shouldn't need to do this very often at all.. maybe once on application startup and again every time the user changes settings (the AppBase.onSettingsChanged() callback is invoked). If you are doing this check more frequently than that, you're wasting cycles.. No matter how much you optimize that bit of code you can't do better than to only call it when you need to.

    If you are asking the general question about which is better in terms of performance and code size, then you'd really have to look at the instructions generated for each and compare them. I haven't done this yet (I will before this post is over), but given how the MonkeyC switch works (it is documented to allow object instances, constants, and the instanceof expression as case values, and it compares for equality), I am fairly certain it is just lipstick on a traditional if statement.

    Okay, so here is the debug output generated for the switch you posted...

    BUILD: # if(dateFmt<4){switch(dateFmt){case0:fmt="$1$ $2$ $3$";break;case1:fmt="$1$, $2$ $3$";break;case2:fmt="$1$ $2$ $3$.";break;}}
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_8:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_8_if:
    BUILD: lgetv 0
    BUILD: spush dateFmt
    BUILD: getv
    BUILD: ipush 4
    BUILD: lt
    BUILD: bf @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_8_else
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_27_start:
    BUILD: # switch(dateFmt){case0:fmt="$1$ $2$ $3$";break;case1:fmt="$1$, $2$ $3$";break;case2:fmt="$1$ $2$ $3$.";break;}
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_switchExpBegin:
    BUILD: lgetv 0
    BUILD: spush dateFmt
    BUILD: getv
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_switchExpEnd:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_switchCaseBegin:
    BUILD: dup 0
    BUILD: spush equals
    BUILD: getv
    BUILD: dup 0
    BUILD: dup 2
    BUILD: ipush 0
    BUILD: invokem 2
    BUILD: bf @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_caseExpBegin_1
    BUILD: popv
    BUILD: popv
    BUILD: goto @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_caseCodeBegin_0
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_caseExpBegin_1:
    BUILD: dup 0
    BUILD: dup 2
    BUILD: ipush 1
    BUILD: invokem 2
    BUILD: bf @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_caseExpBegin_2
    BUILD: popv
    BUILD: popv
    BUILD: goto @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_caseCodeBegin_1
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_caseExpBegin_2:
    BUILD: dup 0
    BUILD: dup 2
    BUILD: ipush 2
    BUILD: invokem 2
    BUILD: bf @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_defaultBegin
    BUILD: popv
    BUILD: popv
    BUILD: goto @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_caseCodeBegin_2
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_defaultBegin:
    BUILD: popv
    BUILD: popv
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_defaultEnd:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_defaultCodeBegin:
    BUILD: goto @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_switchCaseEnd
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_defaultCodeEnd:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_caseCodeBegin_0:
    BUILD: # fmt="$1$ $2$ $3$";
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_140_24:
    BUILD: news @str_1___2___3__1337593282
    BUILD: lputv 1
    BUILD: # break;
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_140_45:
    BUILD: goto @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_switchCaseEnd
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_caseCodeEnd_0:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_caseCodeBegin_1:
    BUILD: # fmt="$1$, $2$ $3$";
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_141_24:
    BUILD: news @str_1____2___3__784014432
    BUILD: lputv 1
    BUILD: # break;
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_141_46:
    BUILD: goto @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_switchCaseEnd
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_caseCodeEnd_1:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_caseCodeBegin_2:
    BUILD: # fmt="$1$ $2$ $3$.";
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_142_24:
    BUILD: news @str_1___2___3___1484281172
    BUILD: lputv 1
    BUILD: # break;
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_142_46:
    BUILD: goto @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_switchCaseEnd
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_caseCodeEnd_2:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_12_switchCaseEnd:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_27_stop:
    BUILD: goto @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_8_end
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_8_else:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_8_end:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_133_26_stop:


    As far as I can tell, there are 57 instructions and 30 labels in the generated code. Here is the debug output for the if...
  • Here is the debug output for the if...

    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_136_8:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_12:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_12_if:
    BUILD: lgetv 0
    BUILD: spush dateFmt
    BUILD: getv
    BUILD: ipush 4
    BUILD: lt
    BUILD: bf @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_12_else
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_31_start:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_16:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_16_if:
    BUILD: lgetv 0
    BUILD: spush dateFmt
    BUILD: getv
    BUILD: ipush 0
    BUILD: eq
    BUILD: bf @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_16_else
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_34_start:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_35:
    BUILD: news @str_1___2___3__1337593282
    BUILD: lputv 1
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_34_stop:
    BUILD: goto @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_16_end
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_16_else:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_140_21_if:
    BUILD: lgetv 0
    BUILD: spush dateFmt
    BUILD: getv
    BUILD: ipush 1
    BUILD: eq
    BUILD: bf @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_140_21_else
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_140_39_start:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_140_40:
    BUILD: news @str_1____2___3__784014432
    BUILD: lputv 1
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_140_39_stop:
    BUILD: goto @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_140_21_end
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_140_21_else:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_141_21_if:
    BUILD: lgetv 0
    BUILD: spush dateFmt
    BUILD: getv
    BUILD: ipush 2
    BUILD: eq
    BUILD: bf @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_141_21_else
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_141_39_start:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_141_40:
    BUILD: news @str_1___2___3___1484281172
    BUILD: lputv 1
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_141_39_stop:
    BUILD: goto @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_141_21_end
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_141_21_else:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_141_21_end:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_140_21_end:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_139_16_end:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_31_stop:
    BUILD: goto @Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_12_end
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_12_else:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_138_12_end:
    BUILD: Q_eclipse_unstable_workspace_ZZZ_source_ZZZApp_mc_133_26_stop:


    As far as I can tell there are 39 instructions and 27 labels in that. So the number of instructions for if is about 30% less than for switch. If I had to do this using one of those techniques, for this particular case, I'd use the if. If you look at it further, it appears that the switch avoids making repeated calls to getv (which can apparently be expensive), so it may have advantages for large numbers of cases.