Hacker News new | ask | show | jobs
by vortico 2264 days ago
Just a reminder that in most compiled and JIT optimized languages with switches, this

  switch (c) {
    case 4: return 30;
    case 5: return 70;
    default: return 0;
  }
compiles to the same instructions and therefore same performance as

  if (c == 4) {
    return 30;
  }
  else if (c == 5) {
    return 70;
  }
  else {
    return 0;
  }
As a side note, it's easier to see accidental fallthroughs if you write switch statements like this.

  switch (c) {
    case 4: {
      return 30;
    } break;
    case 5: {
      return 70;
    } break;
    default: {
      return 0;
    } break;
But in my opinion, the `if` statement is more clear in both cases, and only one level of indentation instead of 2.
3 comments

Huh. To my eyes, the switch version is "obviously" much more clear! It's significantly more compact, with less visual noise you have to mentally ignore. And having the possible inputs and outputs lined up vertically (especially with nothing else between them) makes it easy to understand the logic. Plus, using a switch indicates at a glance that the variable being tested is the same in all cases, whereas with an if-else-if chain there could be any weird expression hiding in one of the conditions.

Eye of the beholder, indeed.

Perhaps a better comparison to the switch statement is

  if (c == 4) return 30;
  if (c == 5) return 70;
  return 0;
Yep, that's a big improvement in my eyes, making it comparable from a readability perspective. In my opinion, that leaves more minor pros and cons of each approach, depending on the language. For example, in C and C++, as kazinator mentioned elsewhere in the thread, GCC and Clang now have an option to require all fallthroughs to be explicitly annotated, which effectively removes one of the main downsides of switch. And switch is better at detecting duplicate cases, as well as checking for exhaustivity if you're matching against enum variants. On the other hand, if you change the example so that the cases don't return (perhaps they assign to a variable instead), with switch you would need to add a `break;` after each one, increasing visual noise. If statements don't have that problem. Or if you change the example so that one of the values `c` is compared to is not a constant, some languages (C, C++, Java but not JavaScript, PHP) don't allow that with switch statements...
That's only true if there are <= 3 "clusters" of switch case values, else compilers do smarter things. See https://youtu.be/gMqSinyL8uk
Is this what you mean by more clusters? Looks like clang compiles the two functions to roughly the same "tree search" code. https://godbolt.org/z/7M_3PU
Are you sure? I'd guess that if the cases are an interval of consecutive integers it may do a "jmp [c]" sometime.
You're correct. Many compilers will emit jump tables if the domain is dense enough. Also, even if compiling to a bunch of conditional jumps, they'll usually emit them to require O(log N) conditional jumps (equivalent to a binary tree of nested ifs) instead of the O(N) linear arrangement suggested by the GP.
Clang compiles both switch statements and `if` statements to the same tree search code. E.g. https://godbolt.org/z/7M_3PU My point is that the optimizations you're describing for switch statements are also applied to a bunch of `if` blocks, at least in LLVM-based languages with an LLVM version from the last several years.
Sure, I didn't mean to imply that compilers never (or commonly didn't) detect that long if-chains could be optimized to jump tables, lookup tables, or if-trees. I was just answering the more narrow question the GGP was asking: if switch statements were always lowered to if-chains and never compiled to jump tables.
Ah I see, gotcha.
Looks the same to me. https://godbolt.org/z/CfhD5k Switches were invented mostly because they were an easy hint to the compiler to reduce to a tree search, but because compilers are smart today, they're obsolete unless you just prefer their syntax.

Even if the cases are consecutive, most compiler optimizers are smart enough to determine that the ASTs are equivalent.

> they're obsolete unless you just prefer their syntax.

This is a good point.

Sometimes I prefer the (ugly) syntax of switches than that of conditionals. The reason is that a chain of conditionals is non-symmetrical (there's no condition for the first "case"), while a switch is formally invariant to permutation of the cases. I have even seen shit like this:

    if (false) ;
    else if (c == 1) { first case ; }
    else if (c == 2) { second case; }
    ...
just to preserve the symmetry along the cases.
Are you talking about the `else` being non-symmetric? You don't need it at all. I do this.

  if (c == 1) { first case ; }
  if (c == 2) { second case; }
...and hereby I give back my programming license and go back to civil life.
This might result in another fallthrough scenario though, wouldn't it?
No, because `c` can't be 1 and 2 at the same time.