4 ms·
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 diffic
by winternewt 17d ago
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.
- cachvico 17d agoI'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.
- craftkiller 17d agoHere's an example of removing a branch that was posted to HN a little over a month ago: https://www.greyblake.com/blog/branchless-rust/ https://www.greyblake.com/blog/branchless-rust/
- metabagel 17d agoOK, but the code with the branch is easier to understand.
- jamiejquinn 17d agoGenerally agree. As with many optimisations, branchless code can easily be less obvious than the branchy equivalent.
- robby_w_g 17d agoYeah, I don't buy the premise that branchless code is intrinsically easier to understand. Maybe OP's point is that adding unnecessary branches makes code harder to read? But that's generally the case for any unnecessary code.
- winternewt 17d agoI see stuff along the lines of: if (x == 0) { return y; } y += 25*x; return y; and skipping the if just makes the function shorter and simpler, while also not involving the CPU branch prediction. Another one that doesn't necessarily skip all branching but at least drops one - and more importantly makes the code simpler and easy to verify, is removing the if statement in code like if (count == 0) { return; } for (int i = 0; i != count; i++) { puts("hello"); }
- GuB-42 17d agoBoth of your examples are optimized by the compiler (gcc 16.1 -O3). In the first case, the compiler removes the first if/return In the second case, if you don't have the first if/return the compiler will add it. That's because it will actually convert your loop into a do/while, with the test in the end, because it is more efficient. But it has to handle the count == 0 special case first, so it will do that early return even if it is not explicitly there. That's the kind of optimization modern compilers are good at.
- cestith 17d agoThat's a really nice example. Thanks.
- metabagel 17d agoFrom the article: ===== Should you go branchless? Most of the time, no. Branchless code is harder to read and easier to get wrong. Besides, compilers know a lot of tricks and already do a lot of this work for us. Only when a profiler points at a hot loop, and the loop contains a branch on unpredictable data this technique can pay off big.
- corps_and_code 17d agoMaybe they mean rather than: if (thingThatIsTrue): // a bunch of logic here... else: // different logic here... they mean: if (thingThatIsTrue): return doThisWhenTrue() return dothisWhenFalse() Just a simple example. I'm not sure if this is what you consider "obfuscating" the branches. Logically the same, but a bit more linear to understand? Edit: I am bad at formatting comments here.
- taink 17d agoPutting two spaces before the line formats is as code. Example: No space before start of line. One space before start of line. Two spaces before start of line. Thus, you can put multiple lines of code with indentation as well as long as you put two spaces at the start of the line: int main() { return 0; } int main() { return 0; } See https://news.ycombinator.com/formatdoc https://news.ycombinator.com/formatdoc
- corps_and_code 17d agoOh perfect, thanks!
- cestith 17d agoFlow control is not always necessary. Other times it can be minimized. The point is not to never branch, but to avoid unnecessary ones. It's not applicable to every situation, but one way to do this is some very basic fuzzy logic. You do a little math and then either choose a single branch at the end, or sometimes avoid a branch altogether. https://www.geeksforgeeks.org/artificial-intelligence/fuzzy-logic-introduction/ https://www.geeksforgeeks.org/artificial-intelligence/fuzzy-... Another way to avoid some branches is to have specialized routines, maybe with multiple dispatch, rather than more general methods with a bunch of checks within them for slightly different situations. A classic performance hack for critical sections is loop unrolling.
- bryanrasmussen 17d agowell here is a branching strategy I often see, pseudocode, and often this is a really stupid example as I do not have the time to come up with a good one: if Val === "A" then Do funcA() else if Val === "B" then and so forth for lots of values, or using a switch statement or similar branching instead of Object functions = { "A": funcA() {does what funcA does}, "B": funcB() {does what funcB does} etc. etc. } runnableFunction = functions[val]; runnableFunction(); Actually writing it I remember now someone who did this, a junior who had to update a validation function for XML invoices based on their root namespaces, which there could be a large number of these, and so she wrote out switch namespace == "somenamespace" { validatingscheme = "someschema"; doPreliminaryFunctionToDetermineifshouldvalidate(); } I can't remember all the details as this was almost 20 years ago, however while it was true that one branched on the schema, it made much more sense to look up what one was supposed to do based on the rule for branching and then just execute that one action rather than writing a bunch of branching logic. So to make it more concrete: Once branching rules becomes sufficiently complex prefer query for what you should do rather than branching on edit: note again, not real code, but should be understandable and translatable into real code to understand what is being said easily enough. on 2nd edit: this is also just basically one of the things I prefer instead of getting a lot of branching logic. I have never seen any stats on any benefit to this model than just having a bunch of branching statements, but I feel that the benefit is there nonetheless.
- NegativeLatency 17d agoThere's stuff like the "Command Pattern"/dispatching/subclasses etc that can make this nice, although it's not always a good fit. Like imagine you have a few different classes of things A,B,C so instead of checking if the thing you're handling is an A,B,C you have like a shared interface across all and can call Thing.do_it or whatever. Still branching conditionally but it's passing it off to language features instead of code you have to write.
- owebmaster 17d agoThere are multiple ways to avoid branches. An early return, a lookup table are two that I use regularly and consider a code smell when the AI uses many if clauses or switches.
- hermitdev 17d agoWhat 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 (...) { ... } }
- 1718627440 17d agoThe latter example, sounds like something trivially done by the compiler. I mean I would sometimes, adhere to it, but only if the loops afterwards become substantially different. If I would just repeat most of the loop body, I would prefer the former.
- hermitdev 15d ago> The latter example, sounds like something trivially done by the compiler. Taken by itself? Absolutely. In the middle of 50-deep templatized call stack? Maybe not. Compilers have their limitations (memory, runtime, other cost budgeting algorithms), and the more complex the code, the hard the optimizer has to work. But, yeah. Most of the gains from branchless code is going to come from this like numerical tools, vectorization and loop unrolling, things of that nature. It was an admittedly contrived example.
- BeetleB 17d agoProbably by using various convenience functions. A common pattern in an old C++ job I had: People writing for loops, coupled with if conditionals, for things that could just be done by chaining functions in the algorithm library. Don't do a for loop, check for a condition, and break. Use find_if.
- reaperducer 17d agoif you share stuff everybody knows you risk coming off as arrogant. Or stupid, like all those vloggers posting "ZOMG! Go all in with these secret hidden weird trick iPhone life hacks to level up!" that are just regurgitating what's in the manual. As we used to say, RTFM: https://support.apple.com/en-us/docs/iphone https://support.apple.com/en-us/docs/iphone
- cestith 17d agoA (hopefully interesting) aside about avoiding branches is if you don't need an exact answer but need your code to make a decision based on an approximation over some known range, you can employ a basic fuzzy logic method. Serially add, subtract, or multiply to adjust a value by a handful of weighted inputs then use that value instead of branching repeatedly to choose the right action. You might branch once based on the final value where it would have otherwise been a larger tree of decisions. In fortuitous situations, you may avoid branching altogether.
- metabagel 17d agoIn what context can you avoid branches?
- lscharen 17d agoMaybe something (contrived) like this providing no-op defaults? total = calculateOrderTotal(user.order); if (user.isPremiumMember) { total = total * 0.9; // 10% discount versus total = calculateOrderTotal(user.order); discount = calculateDiscount(user); // Returns 0.9 or 1.0 total = total * discount;
- metabagel 17d agoOK, or maybe... total = calculateOrderTotal(user.order); total = total * user.discount;
- 1718627440 17d agoOr: total = calculateOrderTotal(user.order); total *= user.discount; or: return calculateOrderTotal(user.order) * user.discount;
- Narishma 17d agoDidn't you just shift the branch to the calculateDiscount() function?
- 1718627440 17d agoNot necessarily. return 1 - user.isPremiumMember * 0.1; would also cut it.
- lscharen 15d agoNo, because the point is that the branch version does not touch "total" when the branch is taken, but the non-branch version will always multiply "total" by something.
- 17d ago
- happytoexplain 17d ago>if you share stuff everybody knows you risk coming off as arrogant I have always felt like my bar for publishing something (even just to internal wikis/channels) is too high due to being overly self-conscious. I think we should try not to validate that feeling by implying that there is a non-negligible number of readers who will think you have a personality flaw because you wrote down your personal collection of tips in a public place, or that those people deserve consideration in the first place. There is no such thing as "the things everybody knows". There are just too many things. Even a list of basic tips is probably going to contain one thing I didn't know or perhaps forgot. Write-ups like this are where most of my practical knowledge comes from, not RTFM (which I do).