‹ BackHN Continuity

Thread

Small programming tricks

681 points · 294 comments · signa11

  1. winternewt · · focus · HN ↗
    Only a few of these are actual programming tricks. The problem with sharing them is that they'll typically seem obvious to you, since you know them. It's difficult to know what is actually unknown to other people, and if you share stuff everybody knows you risk coming off as arrogant.

    Here's one that I think more people should know: avoid branches. If I can do the same thing without an if statement and even a logical expression, the code typically both becomes easier to understand for people and easier to run for the CPU.

    1. cachvico · · focus · HN ↗
      I'm struggling to comprehend how branches can be avoided (or why one would want to, as they are the cornerstone of programming). I can only think how to obfuscate them, which is rarely useful.
      1. hermitdev · · focus · HN ↗
        What people refer to when they say "branchless code" is something very particular, and it refers to not triggering the CPU's branch prediction. That is, don't make the CPU have to guess which fork in the code you're going to take. This is usually accomplished in one of two ways: either bit twiddling hacks or specialized instructions that do not affect the CPU's branch prediction, such as the 'cmov' family in x86. If you search for `examples of branchless code using conditional moves` using your search engine of choice, you'll find numerous examples.

        A trivial example is actually written with a branch in C/C++, but relies on compiler optimizations to kick in. If you compile a ternary operator in C/C++ (and probably rust, C# and other languages) such as in:

           int min_branchless(int a, int b) {
                return a < b ? a : b; // Often emits cmov with -O2
           }
        
        With gcc/clang a -O2, one would expect the compiler to emit the following assembly:

            cmp edi, esi
            cmovle eax, edi   ; select a if a <= b
            ret
        
        There's numerical tricks for other operations/comparisons, and compilers know a lot of them. But, I just suggest compiling your code and configuring your compiler to emit the generated assembly with references to the code it was generated from (you should be able to get it to emit source line references in the assembly). You'll likely be surprised at the optimizations applied at -02, and utterly confused by what you find at -03.

        edit: Also, it doesn't mean to never branch, but to minimize branching, especially in tight loops. Branch outside loops, not inside, for instance.

        e.g. don't do:

            for (...) {
                if (condition independent of loop variable) { 
                  ...
                } else {
                  ...
                }
            }
        
        do:

            if (condition independent of loop variable) { 
                for (...) {
                  ...
                }
            } else {
                for (...) {
                  ...
                }
            }
        1. tialaramex · · focus · HN ↗
          Rust is an expression language and so it doesn't have "the ternary operator"† you can use conditionals like if anywhere in your expression anyway.

          If you want to tell the Rust compiler that you're certain a branch predictor can't help here [be very sure, most often humans are wrong which is why historically these "I know better than the branch predictor" features get ignored by optimisers] you can core::hint::select_unpredictable(condition, a, b) rather than using a dedicated operator.

          † That's not its actual name, some languages have an operator with three operands which does something else, such as fused multiply-add so in a multi-lingual context better to say explicitly you mean the ternary conditional operator.

Open on Hacker News to reply ↗

Unofficial Hacker News client; not affiliated with Y Combinator.