If being branchless is important property of the algorithm then it is better to enforce it. Or at least test for it. If his GCC version will get an update and it will stop producing assembly that he wants no-one will ever know. Which brings us back to regular discussion: C ( and C++ ) does not match hardware anymore. There is no real control over important properties of generated code. Programmers need tools to contr…
Every C and C++ compiler has supported inline asm for decades, so that's what you should use if you really need to control the assembly output. The fact that you can switch between the two within the same function is one of the selling points of both languages.
Beautiful branchless binary search
131–140 of 198 posts
Re: Beautiful branchless binary search
#132“Those spikes for std::lower_bound are on powers of two, where it is somehow much slower. I looked into it a little bit but can’t come up with an easy explanation. The Clang version has the same spikes even though it compiles to very different assembly.” I saw this and immediately went “oh, those look like Intel hardware”. Intel uses 12-bit memory port quick addressing in their hardware, resulting in an issue known a…
Re: Beautiful branchless binary search
#133I asked GPT-4 to explain it to me, and asked if it could identify the algorithm. It didn't do a good job here (returned name of function). But when I told it that it was also called "Shar's algorithm", it immediately identified the author as Andrei Alexandrescu who apparently goes by the pseudonym Shar as well. > Yes, with the name "Shar's algorithm," I can confirm that this branchless binary search algorithm is inde…
Pretty amazing that he did that when he was two. At that age I was still struggling with BASIC. Maybe he had help from GPT. (The actual Leonard E Shar seems to have his name on a single paper, which is not about this, but about a pipelined processor, interesting in its own right.)
https://en.wikipedia.org/wiki/Andrei_Alexandrescu#Child_prod...
Re: Beautiful branchless binary search
#134Earlier quoted context omitted.
Modded down, I guess the average hackernews web dev can't do branchless either. It's easy, just run all the instructions from all branches, and select the output you want based on conditionals.
> and select the output you want based on conditionals. I thought you said it would be branchless.
Re: Beautiful branchless binary search
#135Earlier quoted context omitted.
Yeah, it's mildly annoying there's no (at least to my knowledge?) compiler hint like the '__builtin_expect' ones to tell compilers that the values are very unlikely going to be predictable with enough accuracy to allow the branch predictors to be useful in general, and to use a cmov instead of the traditional branching instructions because of this.
clang has __builtin_unpredictable [0] [0] https://clang.llvm.org/docs/LanguageExtensions.html#builtin-...
Re: Beautiful branchless binary search
#136Earlier quoted context omitted.
This is one area where Profile-Guided Optimization (PGO) can help a lot! With PGO, you run your program on some sample input and it logs info like how many times each side of a branch was taken. From there, you can recompile your code. If the compiler sees that one side of the branch dominates, it can emit code to prioritize that branch. However if the branch counts are approximately even and the branch is hard to pr…
I’m always surprised by rust’s performance for this reason. The compiler outputs huge binaries, chock full of bounds checks and the like. But performance doesn’t seem to suffer at all from it. On the contrary - I ported some well optimized C to rust and it ran faster. I can only assume the compiler is marking all the bounds checks as unlikely to fail, and correctly predicted branches must be more or less free in mode…
Re: Beautiful branchless binary search
#137Earlier quoted context omitted.
I’d be surprised if SIMD + branch to switch algorithms beats branchless binary search for data in cache. Were you comparing with that? Not saying it isn’t possible, god knows CPUs surprise in all kinds of wonderful ways. It’s not the outcome I would have guessed though. 4x AVX2 vectors of 32bit integers is 32 integers. So log2(32) = 5, 5 cmov instructions or a largely unpredictable branch + simd instructions and eith…
I haven't tested this either, but why would the branch be "largely unpredictable"? It's always going to evaluate to false except once, and the one time it evaluates to true the rest of the search will be finished within the branch.
Re: Beautiful branchless binary search
#138Earlier quoted context omitted.
Trying to figure this out myself. So cache line is 64 bytes. Ignoring pointers you can fit eight 8bit data/key values, or sixteen 4bit data/key values. Pointers are entirely implicit (thx user below) This would save you 3 or 4 memory reads respectively. The odds of this strategy helping past the first few layers seems unlikely. So for 100k elements binary tree, this should be a 21% or 31% performance improvement resp…
You don't store data nodes, or pointers. You store the keys. If you need to store data, just store them at the same index in another array. The pointers are wholly implicit . E.g. assuming you let the index 0 be empty, the left pointer for a node k is at 2k and the right node is at 2k+1. In terms of benefit here, the main benefit appears to be that since you always know where the children will be, you can trade bandw…
Re: Beautiful branchless binary search
#139Earlier quoted context omitted.
This is one area where Profile-Guided Optimization (PGO) can help a lot! With PGO, you run your program on some sample input and it logs info like how many times each side of a branch was taken. From there, you can recompile your code. If the compiler sees that one side of the branch dominates, it can emit code to prioritize that branch. However if the branch counts are approximately even and the branch is hard to pr…
I’m always surprised by rust’s performance for this reason. The compiler outputs huge binaries, chock full of bounds checks and the like. But performance doesn’t seem to suffer at all from it. On the contrary - I ported some well optimized C to rust and it ran faster. I can only assume the compiler is marking all the bounds checks as unlikely to fail, and correctly predicted branches must be more or less free in mode…
Re: Beautiful branchless binary search
#140Earlier quoted context omitted.
Wasn't C created to avoid matching hardware? I.e. not caring about word size, number of registers, instruction set, etc. I thought the whole point of writing programs in C was originally being able to write portable software (and a portable OS) that could be executed at different machines? It only became specialized in giving machine-specific instructions when other languages took over.
> Wasn't C created to avoid matching hardware? Not exactly. C code from the early Research Unix days often makes very specific assumptions of how the hardware behaves. As a starter, C was created in an age when 36-bit mainframes still ruled the world, yet it decided to only use 8-bit integers as its base word size, not 6-bit - because the PDP-11 is a 16-bit machine. More appropriately, you can say C was created to av…
Of course, this makes it surprising that relying on 2s complement is undefined rather than unspecified. As well as other examples like left shifting 2^31.
From this story, it sounds like the distinction between undefined and unspecified behavior is newer than I thought, and perhaps more about optimization from the beginning?