4

The below is the code which I need to optimize and planned that it would be good to move to the switch construct. But I can compare in case. So I planned to make the comparison (len > 3) as the default case.

If I make the comparison part (len > 3) as the default case and add the default as the first in the switch, will it be faster?

Or how can I make the below code as a switch statement?

if ( len > 3 ) {
    // Which will happen more often;
}
else if ( len == 3 ) {
    // Next case which may occur often;

} else if ( len == 2 ) {
    // The next priority case;

} else {
    // And this case occurs rarely;

}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
2vision2
  • 4,933
  • 16
  • 83
  • 164
  • You can't move a range from 4 to the max value into a switch. It needs compile-time case labels. – chris Dec 28 '12 at 07:27
  • @chris Thanks for your reply. But I remember I studied somewhere even if you have more the two else if its better to move to switch? – 2vision2 Dec 28 '12 at 07:31
  • Well, sure it can look better if it's actually a possible move. This one isn't. – chris Dec 28 '12 at 07:35
  • @2vision2 it can be more readable and scalable but that's not related to performance – SomeWittyUsername Dec 28 '12 at 07:35
  • Thanks for all your answers and I would accept @icepack answer as its the best one. – 2vision2 Dec 28 '12 at 08:23
  • @chris if the performance of this really was critical (which I kind of doubt), turning the inequality into a direct (switchable) comparison, is quite possible. – JasonD Dec 28 '12 at 08:29
  • @JasonD, I suppose you could put both the `> 3` and `else` into the `default` case. It's really not worth doing to try to include `> 3` as part of the cases, though. – chris Dec 28 '12 at 08:38
  • @chris actually I wasn't quite thinking of that - I've added an answer to show what I mean, though I agree that it's almost certainly not the right approach in this case. – JasonD Dec 28 '12 at 09:03

6 Answers6

6

Probably not. Both if...else and switch...case are high-level constructs. What slows you down is the branch prediction. The better your prediction is the faster your code will run. You should put your most occurring case in first if, second in the else if and so on, just like you wrote. For switch the result depends on the internal compiler implementation which can reorder the cases despite your own order. The default should be actually reserved for the less occurring situations because rest of the conditions must be checked before falling back to default.

To conclude all this, performance-wise usage of if...else is optimal as long as you set your conditions in the correct order. Regarding switch...case it's compiler specific and depends on the applied optimizations.

Also note that switch...case is more limited than if...else since it supports only simple comparison of values.

SomeWittyUsername
  • 18,025
  • 3
  • 42
  • 85
  • thanks for your reply. there is a case where switch will select the correct case at first instance. I read this somewhere. But am not sure. Some kindda sortin used in swith I guess. correct me if am wrong. – 2vision2 Dec 28 '12 at 07:36
  • The running code exams the first `case`, after that the second and so on. So you can "sort" the cases by the order of occurrence, from high to low – SomeWittyUsername Dec 28 '12 at 07:38
  • No am talking about how switch internally works.Its sure that it is much faster than else if ladder. and also it is faster because it will select the case by using sorting techniques internally? – 2vision2 Dec 28 '12 at 07:44
  • C Standard doesn't specify how the `switch` should be implemented. Though it's almost certainly parsed into a construct similar to a long `if..else` – SomeWittyUsername Dec 28 '12 at 07:52
  • Anyhow thnaks... I just wanna know how switch internally implemented or works. I read some where I couldnt find the source where i read. Sorry to trouble you more.\ – 2vision2 Dec 28 '12 at 07:54
  • 1
    AFAIK, some compilers actually "implement" a `switch` with enough cases as a binary search, so it can be better than a purely linear `if ... else` combo. – Angew is no longer proud of SO Dec 28 '12 at 08:32
  • @Angew Interesting remark. It should be very possible since an argument to the `switch` is an integer. But this raises another question - is it really an optimization? Binary search will result in `O(logn)` search time for every option but this completely ignores branch prediction thus arranging cases by order of occurrence might yield better results. Interesting topic, though – SomeWittyUsername Dec 28 '12 at 08:41
  • @icepack I was told by a friend who did some research into optimisation as part of his Master's thesis. Apparenty, *at that time,* there was research showing that it paid off. But I have no idea how valid it is now. – Angew is no longer proud of SO Dec 28 '12 at 08:55
  • @Angew it's probably very specific to a use-case. – SomeWittyUsername Dec 28 '12 at 09:00
  • @Angew this is what I read "binary search". Interesting. Could you please add some links if possible? – 2vision2 Dec 28 '12 at 09:12
  • @Angew to add some more to ypur points its better to give case in some sorted way to make switch work faster?? – 2vision2 Dec 28 '12 at 09:30
  • @Angew I am planning to make this as another question. What say? – 2vision2 Dec 28 '12 at 09:34
  • 1
    @2vision2 Go for it. There are probably some insights people can provide. You can also try on http://cs.stackexchange.com – SomeWittyUsername Dec 28 '12 at 09:35
  • @icepack http://stackoverflow.com/questions/14067547/how-switch-case-statement-implemented-or-works-internally/14067829#14067829 Posted here and found some interesting links too... jus have a look. Thanks. – 2vision2 Dec 28 '12 at 10:18
  • @Angew started a discussion here. http://stackoverflow.com/questions/14067547/how-switch-case-statement-implemented-or-works-internally/14067829#14067829 – 2vision2 Dec 28 '12 at 10:19
  • This answer is simply wrong. Generally speaking, the order you put the cases in a switch is irrelevant; the first thing the compiler does is sort the cases, and process them in sorted order. (But it depends on the compiler. Most modern compilers have options which allow them to use profiler output to reorganize the code. In such cases, it's completely irrelevant how you write it; the compiler will do the right thing.) – James Kanze Dec 28 '12 at 10:40
  • @JamesKanze I partially agree and have edited the answer. However you contradict yourself - either the order is irrelevant or it depends on a compiler, but not both. I presume in the simplest case for a reference compiler without any optimization the processing order would be just linear. – SomeWittyUsername Dec 28 '12 at 10:48
  • @icepack Even without optimization, every compiler I've worked on starts by sorting the elements in a switch, in order to find the max and the min, and to decide whether the cases are dense or not. So the order in a switch will have no effect what so ever. For the `if`/`else` version, it might if you don't optimize. But if performance is important, why wouldn't you optimize. And with profiler output, the compiler will choose whatever is best for the actual observed frequencies. – James Kanze Dec 28 '12 at 10:56
  • @JamesKanze There are many cases where sorting will only worsen the performance so I wouldn't be so categorical – SomeWittyUsername Dec 28 '12 at 17:07
  • @icepack You need to sort in order to determine whether the cases are dense or not. You need the cases sorted if you're going to use a jump table. And you need the cases sorted if you're going to do a binary search. In every compiler I've ever worked on, the first thing you do is sort the cases. – James Kanze Dec 28 '12 at 22:10
3

Although you've accepted what is probably the best answer, I wanted to provide an alternative.

Note that the standard caveat applies - optimisation isn't optimisation unless you've profiled your code.

However if you are encountering poor performance relating to branches, you can reduce or eliminate them. That your code has one or more inequality comparisons is not an obstacle - you can reduce your cases down to a set of direct equalities, and if necessary use that to index a table, rather than branch at all.

void doSomething(int len)
{
    static const char* str[] =
    {   "%2d > 3\n",
        "%2d < 2\n",
        "%2d = 2\n",
        "%2d = 3\n"
    };

    int m1 = (len-2)>>31;
    int m2 = (len-4)>>31;

    int r = (len & m2 & ~m1) + !!m1;

    printf(str[r],len); 
}

Note that this codes makes several assumptions which may not hold in practice, but as we're making the wild assumption that this even needs optimising in the first place...

Also, note that better optimisations may be possible with more knowledge about the actual range and type of the input parameter, and indeed what the actual actions taken need to be.

JasonD
  • 16,464
  • 2
  • 29
  • 44
  • Interesting, I see what you meant now. I wasn't thinking much beyond the switch syntax. How unbecoming of a programmer! – chris Dec 28 '12 at 09:11
  • hi Jason. Thnaks a lot for your answer. And its interesting though. It encourages learners like me to visit SO often.. – 2vision2 Dec 28 '12 at 09:16
  • It's interesting that you can avoid any branching, but at what cost. Given the amount of additional operations, _and_ the fact that one condition is far more frequent than the others, a good compiler will arrange that one to do no branching (assuming that a correctly predicted branch really is expensive), so all the above really achieves is obfuscation. – James Kanze Dec 28 '12 at 10:53
  • @JamesKanze As I said at the top, it isn't optimisation unless you've profiled. That said, I've certainly worked with compilers for which this would be a win. But assuming either case without profiling is folly. – JasonD Dec 28 '12 at 11:06
2

You can't move comparisons into a switch statement... it uses single checks for its selections.. i.e.,

switch (len) {

    case 1:
        // Do case 1 stuff here
    break;

    case 2:
        // Do case 2 stuff here
    break;

    case 3:
        // Do case 3 stuff here
    break;
}

Use breaks to prevent the case statements from running into each other. Read more here.

Your code is as 'optimized' as it will get in its current state...

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
iKlsR
  • 2,642
  • 6
  • 27
  • 45
1

If you are worried about speed, the truth is that your if...else or switch...case statements won't have a real impact of your application speed unless you have hundreds of them. The places where you lose speed is in your iterations or loops. To answer your question specifically, you cannot convert your if...else statement to a switch...case statement with the default appearing first; but with that said, if you did convert to a switch...case then you will that they run at the same speed (difference is too minute to be picked up by conventional benchmarking tools).

xpda
  • 15,585
  • 8
  • 51
  • 82
JustKash
  • 687
  • 2
  • 13
  • 28
  • 3
    if `if else` or `switch case` in a loop, it will have a very real impact – SomeWittyUsername Dec 28 '12 at 07:44
  • I think it would depend on the size of the loop. But in this case, there is only four conditions to check; the difference would have many decimal places and is not really an area that needs to have the developer's attention. – JustKash Dec 28 '12 at 07:51
1

The only way you're going to know is to benchmark it with your compiler. If performance is an issue, you should use the option to provide the compiler with profiler output, and let it decide; it will generally find the best solution. (Note that even on a specific architecture, like Intel, the best solution in terms of machine instructions may vary from one processor to the next.)

In your case, the switch would probably look like:

switch ( len ) {
case 2:
    //  ...
    break;

case 3:
    //  ...
    break;

default:
    if ( len > 3 ) {
        // ...
    } else {
        // ...
    }
}

With only two effective cases, the compiler doesn't have much to work with. A typical implementation (without extreme optimization) would do bounds checking, then a table lookup for the two explicit cases. Any decent compiler will then pick up that the comparison in your default case corresponds to one of the bounds checks it has already done, and not duplicate it. But with only two cases, the jump table will probably not make a significant difference compared to the two comparisons, especially as you'll be out of bounds in the most frequent case.

Until you have actual profiler information that this is a bottleneck in your code, I wouldn't worry about it. Once you have that information, you can profile different variants to see which is faster, but I suspect that if you use maximum optimization and feed profiling information back into the compiler, there will be no difference.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
0

You can use a range in a case:

switch (len) {
  case 3 ... INT_MAX:
    // ...
    break;
  case 2:
    // ...
    break;
  default:
    // ...
    break;
 }

But that is an extension provided by GCC...

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
piwi
  • 5,136
  • 2
  • 24
  • 48
  • It says that this is not standard in here: is that still the case? http://stackoverflow.com/questions/5924681/are-elipses-in-case-statements-standard-c-c – Caribou Dec 28 '12 at 07:42
  • it's a nice construct :) I just hadn't ever seen it and was curious – Caribou Dec 28 '12 at 07:43
  • @Caribou well as your link indicates, compiling with the -pedantic option does warn about the "non-standard-ness" (gcc 4.7.2); too bad it's not in the standard though – piwi Dec 28 '12 at 07:53
  • I agree actually - I wonder if the compiler can do any kind of optimization using this? or whether it becomes the same as the "if..else" code. One for my coffee break maybe :) – Caribou Dec 28 '12 at 07:57