anon-3988
a few seconds ago
Since the biggest one is only 11110 (5 bits), can't you use a lookup table of 32 characters by masking the last 3 bits?
a few seconds ago
Since the biggest one is only 11110 (5 bits), can't you use a lookup table of 32 characters by masking the last 3 bits?
7 hours ago
I’ve seen many examples of a similar phenomenon. Modern compilers are impressively good at generating bit-twiddling micro-optimizations from idiomatic code. They are also good at larger scale structural macro-optimization. However, there is a No Man’s Land for compiler optimization between micro-optimization and macro-optimization where the effectiveness of compiler optimizations are much less reliable.
Intuitively I understand why it is a hard problem. Micro-optimizations have deterministic properties that are simple enough that optimality is all but certain. Macro-optimization heuristics create incidental minor pessimization effects that are buried below the noise floor by major optimization effects on average.
In the middle are optimizations that are too complex to guarantee effective optimization but too small in effect to offset any incidental pessimization. It is easy to inadvertently make things worse in these cases. Most cases of surprising performance variances I see fall into this gap.
It is also where the codegen from different compilers seems to disagree the most, which lends evidence to the idea that the “correct” codegen is far from obvious to a compiler.
3 hours ago
It's a hard problem because the cost model is incredibly complicated.
For example: you decide to unroll/inline/vectorize code has unpredictable effect on performance.
7 minutes ago
Or worse - it has perfectly predictable effects on the performance...so long as you know details (core counts, cache sizes, etc.) of the CPU model and dataset which your code will be run on, and the TLB/cache/fabric/memory usage pattern of other jobs which might be running at the same time.
6 hours ago
> also good at larger scale structural macro-optimization
Can u give some examples?
Perhaps you mean small granular choices which occur widely throughout the code?
5 hours ago
Automatic unrolling + inlining + constant hoisting (+ vectorisation)? I'd call that macro, but maybe op had something different in mind.
7 hours ago
If you wanna really see this at work on a whole other extreme, try compiling code for an N64 game. It's no surprise that optimizations for modern-day x86_64 and Arm64 processors with a lot of cache would not generalize well to a MIPS CPU with a cache that must be manipulated at the software layer and abysmal RDRAM latency, but the exact mechanics of it are interesting.
KazeEmmanuar did a great job analyzing exactly this so we don't have to!
a few seconds ago
Exactly what I was going to post. Optimizations like loop unrolling slow down the N64 because keeping the code size small is the most important factor. I think even compilers of the time got this wrong, not just modern ones.
3 hours ago
I suspect over time as memory latency, bandwidth and branch prediction have become increasingly the hot aspect of CPUs that optimisations put into compilers before now often end up improving for the wrong thing. Maintaining sequential reads of memory and ahead of the calculation is now critical for peak performance as is the branch being correctly predicted. Saving calculation time on the ports of the CPU however much less impactful as we mostly struggle to shuttle the work into them to achieve peak performance.
Branch predictors are a lot bigger and more capable now than they were and there is every chance when the optimisation for jump tables was tested against the equivalent branch code it outperformed it, but on more recent CPUs the situation reverses.
7 hours ago
I think some people misunderstand how compiler optimizations work. it's not magic, which makes each piece of code faster, it's just a bunch of deterministic code transformation rules, which usually make code faster (considering large set of use-cases), but it's not proven that they always do so. So, by writing performant code one should always keep this in mind.
5 hours ago
There are many optimizations that are guaranteed to make code run faster, but even then, that does not mean you should use them.
There can be competition between optimizations. At a given moment, the preconditions for both optimization O1 and optimization O2 may hold, but if you then apply O1, the precondition for O2 may no longer hold and vice versa.
In such cases, the best solution is to pick the better of O1 and O2.
5 hours ago
One could even say that compiler optimizations are a set of deterministic code transformation rules that happen to make SPEC run faster. But programmers are not wrong to expect better from their compilers.
7 hours ago
ARM64 has many registers but I believe the lack of suitably large immediate values and, apparently, compilers that are willing to use them all across functions, puts it at a disadvantage here. Assuming we want the return value in eax and the leading count comes in cl, this can be done branchlessly and data-lessly on x86 as follows:
mov eax, 0x00043201
test cl, 8
setz al
shl cl, 2
shr eax, cl
and eax, 15
Something similar may be possible on ARM64, but I suspect it will definitely be more than 19 bytes ;-)6 hours ago
Shouldn't your snippet be using lzcnt? I can't see how this would result in the desired lookup.
for Zen5 rustc creates the following:
utf8_sequence_length_lookup:
shl edi, 24
mov ecx, 274945
not edi
lzcnt eax, edi
shl al, 2
shrx rax, rcx, rax
and al, 7
ret
https://rust.godbolt.org/z/hz1eKjnaG7 hours ago
It's a simple rule of thumb to avoid branching in performance-critical code. Branchless code is pretty predictable, but if branching takes place, one can no longer reason so easy about code performance, since compiler optimizations and CPU branch prediction may be involved.
5 hours ago
But reality is not that simple. Branchless with unfortunate data dependencies can be a lot worse than predictable branching code.
an hour ago
That's why I said it's a rule of thumb. There are always exceptions where branching does code faster.
3 hours ago
This is a good reminder that optimization without measurement is stupidity.
It’s also a reminder that compilers aren’t magic and optimizers aren’t all-knowing.
People have a tendency to ascribe magical properties to anything they don’t understand. Compilers and optimizers more so.
Measure before and after any optimization. You’d be surprised at what does and doesn’t improve efficiency.
Today’s CPUs are more complex than a human can understand.
Measure twice, cut once.
an hour ago
Worse is the cargo cult of "write this way because it is faster", without any measurements.
Only because it used to be true in the past, someone told it was so during a coffee break, or it appeared in some computer related article.
an hour ago
Agreed. As an embedded software engineer new to the field I’ve seen senior engineers with over ten years of experience complain when division by a compile time constant (even by a power of two) is used in code instead of multiplication or bit shifting on our cortex-m7 mcu running at 600MHz