Trimming spaces from strings faster with SVE on an Amazon Graviton 3 processor
lemire.me
lemire.me
When I was looking at something related to this recently I noticed gcc doing a pretty cool optimisation [0], where it converted the whitespace chars into a bitfield and leveraged the `bt` instruction. It seems quite sensitive to the char string though and falls back to worse code easily (e.g add "\f\v" to the string) but it's easy to manually write the code so that gcc always generates the same.
And yes if you want to play with SVE, Amazon's own ARM chip is the best one available now, maybe the only one in fact, for the general public.
This means there is no need for continually updating the instruction set with longer and longer versions of the same instructions. Hopefully this leads to a more stable base and wider adoption, we’ll see how that works in practice though!
That's not to say scalable instructions are a bad idea. It certainly makes it easier to make small CPUs which support all the software written for vector instructions without having to emulate 256 or 512 bit wide execution units.
Also, I think it is supposed to be easier for compilers to autovectorize SVE than, say, neon. Whether that's the case in practice I don't know. Is there something specific you see that makes it more difficult than with SIMD-style, fixed operation width instructions? Or you are referring to something else with "compiler support"?
That said, I don't know if this particular routine is something the author came up with while working on some other problem, or if it's just a neat idea that he came up with and wrote a short blog post about.
Are the curly braces in the initialization some new C++ syntax? I haven't been keeping up with C++ developments in the last decade or so.
Based on the code that follows I would expect
char* init_out = out;I'm a fan of the type of optimization and exploration that Lemire often publishes, it's really cool and impressive. I just have a pet peeve about people lumping together C and C++ as if they're interchangable when they are (more clearly than ever, and growing further apart) not.
[1]: https://en.cppreference.com/w/cpp/language/list_initializati...
I studied Econ in college and been a python guy for last 8 years but have no knowledge of how the machine actually interacts with my code. Is there a formal name for understanding that?
Of course, it can be learned on your own too. But generally this path is harder because it's hard to know what to study. The CS program will expose you to breadth of concepts.
It's hard for me to answer, because I was learning everything I could on the internet before undergrad, following each tiny micro-architectural tweak in minute detail. It makes it hard to remember what things college really provided & what was self-taught.
EDIT: Link to course in OpenCourseware if you want to virtually take it: https://ocw.mit.edu/courses/6-004-computation-structures-spr...
If you’re interested in learning more about microarchitectural optimization I find that getting a basic understanding of how a modern processor has evolved and then reading posts like these (looking things up when necessary) can get you mostly up to speed with the area. Most people don’t use these details in their everyday code so if you’re just looking to make your code fast learning how to measure algorithmic complexity and use a profiler is likely to be much more valuable.
To get into the right mindset for SIMD optimization, play Zachtronic games. You have a big pile of odd-shaped tools that take some input and output a result a few cycles later, you have limits on how many instructions can run at once, but maybe you can do something like a load each cycle for free. Run your benchmark, count the cycles, make a small tweak and test again.
Low level string operations happen so often under the hood in any application that 10x more code and complexity in an implementation is worth it if it increases efficiency.
When I was at Google there was a "rules of thumb" page that described how much of your time X performance was worth. I always looked at that before micro-optimizing, but also always came out ahead. I remember a coworker and I redesigning one of our subsystems late on some Friday evening; the rules of thumb said that the performance improvement we predicted, at our scale, was worth a month of SWE time. We did it in 2 hours. So we came out ahead, and our system worked better. (I joked that my colleague and I would be taking 2 extra weeks of vacation.)
TL;DR performance matters everywhere. The one user using an interactive tool on their workstation will appreciate their day not being wasted by random pauses. The person out and about on their phone will appreciate the additional battery life. And your company's bean counters will be quite happy to hear that your cloud bill or data center expense forecast for next year is down. Finally, it's fun! Truly a win/win. Make it fast!
This was a very easy change; we just made a new main.go and an RPC to send the data to aggregate. The system was designed internally to be logically isolated across that boundary, so we just stuck in the RPC and then the other side of the boundary could be another data center.
In the end, I think we saved a few terabytes of RAM-years. Not a big deal, but it was something.
You're more-or-less programming in assembly here using these intrinsics, using C for goodies like for loops, and at the moment that's about as good as you can do while scalable autovectorization is still WIP/NIH in most compilers, so it's not really surprising or noteworthy. For experienced SIMD programmers, this is the standard approach to getting data parallelism out of many "boring" algorithms.