15 ms·
Everyone should know SIMD
- qurren 3mo agoI just do gcc -O3 and get SIMD without having to learn it
- forrestthewoods 3mo agoauto-vectorization is not nearly as good as you would hope it to be. The best SIMD optimizations likely require changing your data format from AoS to SoA.
- raegis 3mo agoWhat are AoS and SoA?
- nylonstrung 3mo agoArray of Structs and Struct of Arrays
- Georgelemental 3mo agoArray of Structs and Struct of Arrays https://en.wikipedia.org/wiki/AoS_and_SoA https://en.wikipedia.org/wiki/AoS_and_SoA
- Rendello 3mo agoA good introduction to SoA (for anyone curious) are the two most famous Data-Oriented Design talks by Mike Acton (game engine dev) [1] and Andrew Kelley (Zig lead dev) [2] respectively. I read a book book about DoD [3] really which confused me at first with all its talk about database table design (in a book about a high-performance C++ game engine?), but when it finally clicked it was amazing. The point is that you want to think hard about your access patterns and what could constitute good "primary keys", then model it accordingly. SoA ends up being useful a lot of the time, because having your data in homogeneous arrays/vectors is great for cache locality and branch elimination. Even without SIMD you can get huge speedups from that, but that's also where your compiler (or you as a programmer) can get incredible SIMD gains. SoA is not a silver bullet as it may not align well with your access patterns, but it can great to add to your toolkit. --- Mike Acton: Data-Oriented Design and C++: https://www.youtube.com/watch?v=rX0ItVEVjHc https://www.youtube.com/watch?v=rX0ItVEVjHc Andrew Kelley: A Practical Guide to Applying Data Oriented Design: https://www.youtube.com/watch?v=IroPQ150F6c https://www.youtube.com/watch?v=IroPQ150F6c Richard Fabian: Data-Oriented Design: https://www.dataorienteddesign.com/dodbook/ https://www.dataorienteddesign.com/dodbook/
- formerly_proven 3mo agoAnd -march=native or at least -march=x86-64-v3 or similar, alternatively identifying relevant functions and manually invoking FMV and uarch specialization via target_clones. Plus non-integer code can generally not be autovectorized in normal-math mode since FP is non-commutative.
- nylonstrung 3mo agoThe one feature in Jonathan Blow's Jai language I really envy is a a single keyword to switch AoS to SoA and visa-versa at comptime
- mbStavola 3mo agoDidn't he drop this feature years ago?
- Joker_vD 3mo agoWell, then I just prompt Claude and get SIMD without having to learn it /s
- ethin 3mo agoEither this or you have to do special tricks like pairwise tree reductions and hand-unroll certain portions of loops.
- pjmlp 2mo agoWhile C++ may be reaching levels of Algol 68, PL/I complexity, with C++26 reflection you can do automatically. See https://github.com/cern-nextgen/reflmempp https://github.com/cern-nextgen/reflmempp
- forrestthewoods 2mo agoThat’s a research project. So no.
- exDM69 2mo ago> The best SIMD optimizations likely require changing your data format from AoS to SoA. We do have gather load instructions in SIMD instruction sets these days (AVX2 and newer), so AoS vs SoA is not nearly as important as it was once. Scatter stores are also available but only in newer CPUs.
- inigyou 2mo agoIt remains extremely important. Gather loads are much more expensive than sequential loads, for obvious reasons.
- ashton314 3mo agoIn the article, Mitchel mentions how this doesn’t always work. In fact, as someone who’s worked in compiler development, I can say it’s a small miracle when it does work.
- mitchellh 3mo agoCase-in-point, the example in my own post doesn't auto-vectorize with LLVM or GCC at highest optimization levels. Basically, compilers will never auto-vectorize loops with an early loop break afaik.
- yunnpp 3mo agoYou need to let the compiler know that there are at least 4 or 8 elements to process. This may require padding data and/or having a second loop after the main one that processes the remainder <4 or <8 elements. You start the post with: > There is an opportunity to use SIMD. SIMD turns those into this: > > for (8 byte chunk in bytes) { /* ... */ } If you actually wrote that loop, there is a good chance the compiler (gcc specifically) will auto-vectorize. In any case, the more manual SIMD optimizations I have seen require reworking the data altogether, not just processing N elements at a time. For example, instead of packing two 4-vectors into two registers to do a dot product, pack the XXXXs, YYYYs, etc. into 4 vectors and compute 4 dot products for the price of one. That not only requires having 4 vectors to process, but also thinking how exactly they are packed in registers. I don't know why qurren is downvoted. You really should see if you can get the compiler to auto-vectorize first (possibly padding data structures and loops) before you write anything by hand.
- spider-mario 2mo ago> You really should see if you can get the compiler to auto-vectorize first (possibly padding data structures and loops) before you write anything by hand. Counterpoint: https://pharr.org/matt/blog/2018/04/18/ispc-origins https://pharr.org/matt/blog/2018/04/18/ispc-origins > I think that the fatal flaw with the approach the compiler team was trying to make work was best diagnosed by T. Foley, who’s full of great insights about this stuff: auto-vectorization is not a programming model. > The problem with an auto-vectorizer is that as long as vectorization can fail (and it will), then if you’re a programmer who actually cares about what code the compiler generates for your program, you must come to deeply understand the auto-vectorizer. Then, when it fails to vectorize code you want to be vectorized, you can either poke it in the right ways or change your program in the right ways so that it works for you again. This is a horrible way to program; it’s all alchemy and guesswork and you need to become deeply specialized about the nuances of a single compiler’s implementation—something you wouldn’t otherwise need to care about one bit. > And God help you when they release a new version of the compiler with changes to the auto-vectorizer’s implementation. > With a proper programming model, then the programmer learns the model (which is hopefully fairly clean), one or more compilers implement it, the generated code is predictable (no performance cliffs), and everyone’s happy.
- jandrewrogers 3mo agoMost scalar-to-SIMD conversion requires changing the design of data structures and algorithms to be effective. Compilers are required to exactly reproduce the specified data structures in a deterministic way for obvious reasons. Even if compilers were clever enough to transform your data structures and algorithms for SIMD (they're not), the data structures are a contract that can't be unilaterally modified.
- llm_nerd 3mo agoI have no idea why you're being downvoted. HN has a fetish for SIMD, but if you are hand-rolling SIMD and you aren't writing an explicit acceleration library, you're doing it wrong. Like, 100% of the time. Every modern language has a vectorization optimizing compiler, and through some fairly straightforward techniques this is automagic. And contrary to the various replies, unless you screwed something up compilers are really good at vectorizing on whatever hardware you're targeting, including SVE.
- saagarjha 2mo agoCompilers are really good but really good is not actually that useful in cases where you need SIMD
- llm_nerd 2mo agoI mean, utter bullshit. If you "need SIMD" you know exactly the programming pattern to guarantee SIMD from the compiler. And the single and only people who "need SIMD" know these rules. It is only the hobbyist "SIMD is neat" community that upvotes these ridiculous articles.
- saagarjha 2mo agoWhen I need SIMD I rarely use the compiler to help
- spider-mario 2mo ago> HN has a fetish for SIMD, but if you are hand-rolling SIMD and you aren't writing an explicit acceleration library, you're doing it wrong. Like, 100% of the time. What if the “explicit acceleration library” for what you need to do doesn’t exist?
- Archit3ch 2mo agoOr it exists and is not optimal for your use case?
- wrl 3mo agoi was having a conversation with a friend recently about simd in zig (which i have recently picked up and been having a pretty good time with). i find that simd writes decently well, though there's a few weird things: - some builtins purport to work on simd vectors but actually just unpack the vectors and do their work per-element (e.g. running `@sin()` on a `@Vector(4, f32)` will unpack the vector, run `@sin()` 4 times, and then pack it back into a vector). - a lot of `std.math` is scalar-only (some functions support vectors, though, and i've got a pr open for one of them and plan to do more). - i'm certainly missing some intrinsics that i get from xmmintrin.h (rcp, rsqrt, few others). in general though i'm finding it pretty capable. mitchell, i know you hang around some of these comments sometimes – i noticed that in ghostty you bring in some c++ libs to do the simd heavy lifting for you. any plans to port that to zig? anything missing from the language or libs that's preventing it?
- mitchellh 3mo ago> mitchell, i know you hang around some of these comments sometimes hi im here > i noticed that in ghostty you bring in some c++ libs to do the simd heavy lifting for you. any plans to port that to zig? anything missing from the language or libs that's preventing it? No plans to port it. For others, this is referencing highway: https://github.com/google/highway https://github.com/google/highway The major limitation of Zig's vectors is that they're compile-time only. So if you're building redistributed software that compiles for a baseline CPU target, it won't be as optimized as it could be for YOUR possible machine. Highway compiles our SIMD modules for different hardware configurations and at startup does a CPUID fingerprint to figure out which to load. That way even baseline has AVX512 etc. implementations, and we just activate the right one at runtime. We only use Highway for our hottest hot paths that we feel benefit from that specialization. No plans to port that (although, I spent hundreds of dollars and slop-forked it into Zig with the help of this good boy GPT and it worked great actually, but I didn't want to maintain it).
- wrl 3mo agoahaaa, yeah, i don't personally do any runtime switching but i hear that as a deal-breaker from other folks. it's interesting – i've found that zig tends to extend my vectors to the native width of the platform and then operate on them there. e.g. i had a `@Vector(2, f32)` that i was using as a demo and the generated assembly was promoting it to 256 bits and using avx2 instructions on it!
- eska 3mo agoI don’t know zig syntax, but wouldn’t it be possible to put this common pattern into a macro and simplify it to mostly a lambda on V?
- dnautics 3mo agono macros in zig, but yes you could metaprogram it. types are first class values at compile time so you could do that sort of specialization if you wanted.
- hnal943 3mo agoHere's a helpful video about leveraging SIMD to solve a concrete performance problem for the dev team that made the game The Witness by Casey Muratori: https://www.youtube.com/watch?v=Ge3aKEmZcqY https://www.youtube.com/watch?v=Ge3aKEmZcqY
- crabmusket 2mo agoIt's a great talk, I just wish there was a good focused textual version of it, as it is a very long video to recommend to others. Very worth it, but a big investment. It's a great example of what I think of as vertical integration for performance. As you go through the talk you can understand why all these abstractions exist and why they have to be so generic. But when you have a specific use case, you can vertically integrate from the problem definition all the way down to SIMD and reap big rewards.
- inigyou 2mo agoBe the change you want to see. Post a transcript on your own website.
- crabmusket 2mo agoYes, I will do that when I have time one of these years. I did mean to caveat that in my post but forgot. I mainly wanted to make the vertical integration point.
- inigyou 2mo agoDo it today or you'll end up never doing it. Make sure to link back to the source, of course, and don't pass it off as your own words.
- zahlman 2mo agoDoesn't YouTube provide one automatically?
- 2mo ago
- pton_xd 3mo ago"More importantly, when this loop matters enough for me to care about a 5x speedup, I want the vectorization to be explicit and predictable. I don't want an unrelated code change or compiler update to quietly turn it back into a scalar loop." Only tangentially related but this is by far the most painful part about optimizing code for JIT compilers like V8. Even changing a constant from 1 to 1.0 somewhere else can change the optimizations performed and lead to an unexpected performance decrease.
- pjmlp 3mo agoProving the point that the compilers aren't deterministic as some folks argue. This is especially painful with dynamic languages, like in JavaScript's case. However JIT also have positives hence their widespread use.
- Rendello 3mo agoI like SIMD, but before super-optimizing your code with SIMD and the like, really consider your data structures and access patterns. I've been singing Data-Oriented Design's praises, so I'll just collect all my comments here [1], but I think it's a good approach to optimization. I played around with SIMD in my old code (in Zig), but my approach to modelling datastructures was so antithetical to optimization, it was like putting high-performance racing tires on a lemon with a broken engine. It was the root-of-all-evil-type-premature-optimization, because I wasn't measuring performance, and I wasn't thinking about where the allocations were, etc. Now, I try to model my data as if it were SQL tables, see what my potential "primary keys" could be, and build my data structures around my access patterns. For example, I used to model trees as structs pointing to other structs on the heap: struct Tree { tag: TreeTag, children: Vec<&Tree> } Now my tree has all the bad characteristics of a linked list (* n nodes * m children), all the fragmentation of multiple heap vectors (* n nodes), and terrible set-up / tear-down time (in this case, Drop alone was taking up a good chunk of runtime). But a tree can be represented a million ways, and can always be linearized. So now I really consider my access/insert patterns of the tree, whether it's really a tree or some other sort of graph, whether I can store it in a Vec or a Struct of Vecs, etc. Since really looking at things through their access patterns and "primary keys", my code has been much faster and simpler. This has the added effect that a lot of your data ends up in homogeneous arrays / vecs, which means that the compiler can do its SIMD magic, the CPU can read it from your L1 cache a million times faster, etc. And then when you need to drop down into SIMD yourself, you can write some awesome branchless code. 1. https://hn.algolia.com/?dateRange=all&page=0&prefix=true&query=Data-Oriented%20Design%20author%3ARendello&sort=byPopularity&type=all https://hn.algolia.com/?dateRange=all&page=0&prefix=true&que...
- tarnith 3mo agoYeah, data layout/cache aware layouts are really key if you really want to unlock making something that ends up in a hot loop fast with SIMD. Also, avoiding allocations or vtable lookups or a lot of indirection in the part of the code that's actually "hot" is really important. Vectors (in C++) at least aren't necessarily the best fit either, if you end up doing anything that can call an allocation unexpectedly.
- derf_ 3mo agoTo bolster the argument, even if you do not plan to write the SIMD yourself or will "just get AI to do it", it is important to know what can be fast in SIMD (and on what hardware). That allows you to design your algorithms and structure your code so that the SIMD is possible. Internalizing things like how data dependencies matter, how expensive it is to increase the width of your vector elements (and how to avoid the need), how to turn conditions and branches into masks, or simply things like "division does not exist" becomes a lot easier when you have spent at least some time trying to use SIMD yourself.
- tstack 3mo ago> what can be fast I think this doesn't get talked about enough. If your input is a big run of data that is being checked/transformed in one shot, it works well. But, if you're likely to have to make a decision on several bytes of the input, SIMD will be the same or slower than the scalar method. It's not a magic "go fast" button.
- morganherlocker 2mo agoI think the big miss is that the former is achievable far more often than people imagine, which leads to settling for the later, aka pessimization. Reducing your allocations from millions per run to handfuls per run during initialization is effectively a "go fast" button, and is generally more reliable. It is very common for teams to spend big effort getting a 2-3x speedup on allocation-heavy code by disabling branches when a 100-1000x speedup can be had if restructuring allocations is a strategy under consideration (even before the extra 4-8x you might see if you go all the way to hand-tuned SIMD).
- inigyou 2mo agoThat is not necessarily true. simdjson exists. But it's far from simple. I have been told SIMD is good for data-parallel loops, like the GPU is, and that is true, but it can also be used piecemeal, unlike the GPU. Because it is just the CPU, you can read 32 unaligned bytes, scan for the index of the first space, and take a branch based on that.
- kristianp 3mo agoTangentially for Go programming, the last time I looked at optimising some Go code with SIMD there were a few different options available, but they were either not maintained any more or had incomplete support and required first writing your function in C++ with intrinsics and generating assembly, then converting it to go assembly with a tool [1]. I never got my function to work in go despite the C++ code working fine. In short, not really a production ready option for Go. This was a year or two ago, though. Edit, there's now an experimental official library at https://go.dev/pkg/simd/archsimd/ https://go.dev/pkg/simd/archsimd/ see https://go.dev/doc/go1.26#simd https://go.dev/doc/go1.26#simd and at https://github.com/golang/go/issues/78902 https://github.com/golang/go/issues/78902 so things have moved since I tried it last. [1] https://github.com/minio/c2goasm https://github.com/minio/c2goasm
- nasretdinov 2mo agoIt actually works really well in the last couple Go versions with GOEXPERIMENT=simd. You do get a similar speedup (if not higher, since SIMD also eliminates the penalty for bounds checking and other things Go runtime does.
- waffletower 3mo agoI was hand-rolling NEON SIMD 15 years ago, and in many cases the compiler (clang/llvm) simply out optimized me. I kept the attempts that were better than what the compiler could already do. That was ARM NEON, quite new at the time, not SSE, and again that was 15 years ago that the compiler could already beat me much of the time. I hate the naive cult coder adage concerning premature optimization, but this might be a situation where you peruse your compiler output before you start writing code in a manner the compiler can for you. In Clang, auto-vectorization is enabled by default at optimization levels -O2 and -O3.
- arijun 3mo ago> Every developer should… most importantly, not be scared of SIMD Seems like he should be recommending fearless_simd [1], the Rust crate by Raph Levian and the folks at Linebender :) More seriously, if you’re looking to add SIMD to your Rust code, that’s the package to start with. [1] https://crates.io/crates/fearless_simd https://crates.io/crates/fearless_simd
- andix 3mo ago99% of developers should just ignore SIMD. Most projects have a lot of low hanging fruit to increase performance, and still nobody finds the time to solve them.
- favorited 3mo agoThat doesn't mean you should default to a slower implementation for new code. If you do, you're just creating more low-hanging fruit that nobody will find time to solve.
- andix 2mo agoIn most cases you should skip SIMD also for new code. Often a non-SIMD version is required for compatibility, just stick with that.
- pzo 2mo agoin many cases often its faster just to switch from debug to release - compilers are good to vectorise many loops. Worth to give it a try before rewriting clean loop/code into SIMD/NEON.
- tcfhgj 2mo agoStop using electron
- Culonavirus 2mo agoNever. You will have to pry it from my cold, dead hands.
- histiq 3mo ago[flagged]
- deleted 3mo ago[deleted]
- pjmlp 3mo agoNot really, not as SIMD remains an esoteric art from packing matrix and vector operations in endless opcodes. I rather let the compiler auto vectorise itself, or with AI help.
- mamcx 3mo agoIs interesting that this is how array langs work. I bet will be easy to turn into a lib.
- gblargg 3mo agoMy compiler knows SIMD. However, knowing the limitations of SIMD might help avoid a calculation that can't be optimized to use it.
- Jtarii 2mo agoYour compiler can vectorise trivial things, once your code gets complicated it will no longer vectorise.
- tmtvl 2mo agoOnce the code is complicated you don't need to make it more complicated lest it becomes unmaintainable.
- spider-mario 2mo agoIt really doesn’t take much complexity at all for autovectorisation to fail.
- ktimespi 3mo agoThe biggest barrier I faced learning SIMD is the weird naming convention for intrinsics. Once I got past that, it was fairly simple. mcyoung's articles were also super helpful
- Rendello 3mo agoOne of my favourite articles is "SIMD-friendly algorithms for substring searching" by Wojciech Muła [2]. If you were unfamiliar with SIMD and just jumped into the code, it'd be incomprehensible due to the intrinsics, but the generic algorithm description at the top is pretty simple if you take some time understand it. It blew my mind once I understood what was happening, because it's quite clever but one of those "I could've thought of that" algorithms. There was some pretty good discussion on it last year (feat. ripgrep). [2] 1. http://0x80.pl/notesen/2016-11-28-simd-strfind.html http://0x80.pl/notesen/2016-11-28-simd-strfind.html 2. https://news.ycombinator.com/item?id=44274001 https://news.ycombinator.com/item?id=44274001
- losvedir 3mo agoThis is an interesting article. I don't really work with low level enough languages for this to matter (unless - does this ever show up in Javascript somehow?). I guess I don't understand the "reduce" step. It seems like you have to be careful not to "undo" all the benefit from SIMD. Sure, it can compare 8 values in parallel, but then if you have to look at each of the 8 answers in turn you're back to where you began. Is the `@reduce()` function in the example a special Vector one that tells you if all the values are true or not in one "step"?
- wrs 3mo agoYes. "`@reduce(.And, ...)` combines every boolean using `and` and returns a single boolean." If it's true (the common case here) then you proceed to look at the next 8 bytes. If it's false, you apply a @bitcast (turn the booleans into bits) and @ctz (find the first 0) to get the index of where it was false.
- saagarjha 2mo agoIt can show up in JavaScript if you write your code in a way that lets the browser engine lower it to SIMD under the hood
- boricj 3mo agoEveryone doesn't need to know SIMD. Mechanical sympathy is an important passive perk for software architects to cut down the number of reworks down the line, but I would rate benchmarking and being able to identify bottlenecks as more important everyday skills. I'm working on a voxel space renderer homebrew for the PlayStation. I only have so many cycles to spend on rendering before it becomes a slideshow, so I count them in my hot rendering loop and parallelize work as much stuff as I can, even across memory load stalls from main RAM. I've worked on a basic network accessory card with a STM32 MCU that is extremely overkill for what it needs to do. We haven't bothered making any performance or memory optimizations whatsoever, writing plain C++ almost as if we were on server-class hardware because we had such egregious margins. The first question to ask is not whether something can leverage SIMD, it's whether the performance requirements are met or not (although it's far too easy to not care when it's not your hardware that's struggling...).
- to11mtm 3mo ago> Mechanical sympathy is an important passive perk for software architects to cut down the number of reworks down the line We might be talking on different levels but when it comes to, on an opposite end of 'Should I use SIMD'... a databases a level of mechanical sympathy at a 'base' level is still important. e.x. row-by-row updates vs batching or bad logic where a 21k entry in clause forgot about unicode rules on columns and breaks an index [0]... is still super important. [0] - That one is real, thanks lazy bodyshop having their people use copilot and yet, we get the same billable hours, nothing is done faster, and management is too stupid to pay attention...
- saagarjha 2mo agoIt’s worth knowing SIMD for the purpose of knowing when it’s not worth using
- pjmlp 2mo ago> ....it's whether the performance requirements are met or not ... This is what so many people miss when doing micro-benchmarks of language X vs Y, sure Y might win out in execution speed, however if X delivers within the performance requirements and has a lower development cost, it wins out while being slower than Y. Naturally taken to the extreme, when it isn't our hardware is how we end up with Electron apps.
- abratabia 3mo ago[flagged]
- magarnicle 3mo agoEveryone should know about SIMD, so you can know when to ask your good friend Al, who knows how to do it, to use it for you in the right places.
- ivanjermakov 3mo agoI respect (and fear a little) those who intentionally utilize SIMD in their implementations, but I believe it's a bit too much of a semantic shift for 2-5x performance gain. Good news is that modern compilers are more than capable of emitting SIMD code even if original source is nothing but. Most software's poor performance would be fixed long before SIMD comes into play. Reduce obvious DB/server round trips, bad data structure/cache locality, poor algorithm complexity, doing redundant computations, etc.
- jgalt212 3mo ago> Reduce obvious DB/server round trips In my mental model of optimization, the above and SIMD are basically the same thing.
- mitchellh 3mo ago> Good news is that modern compilers are more than capable of emitting SIMD code even if original source is nothing but. They're really not (I have a whole section on it in the blog post). This example in the post doesn't auto-vectorize, for example. And its a pretty big part of the overall throughput for plain text runs (ascii or unicode). Really, the point of that section is that almost nothing auto-vectorizes, backed up by LLVM docs and published research. Instead, writing 12 lines for a 5x gain is way easier than crossing your fingers and hope someone else pays your bills. Bigger picture, the real point is that this stuff isn't complicated. You wouldn't copy and paste 100 lines because you hope the compiler "lifts this into a for loop", you just write the for loop cause you know how and its simple. Similarly, the common case of "process N values in parallel" is very simple. Write a dozen lines of code you're comfortable with. No need to pray the compiler people saved your bacon.
- Jtarii 3mo ago>Good news is that modern compilers are more than capable of emitting SIMD code even if original source is nothing but. This is only true if you are intentionally writing code that the compiler can easily vectorise. Which is not most code.
- applfanboysbgon 3mo agoThere is nothing to fear or respect about using SIMD. Don't lionize learning. You can learn too, if only you allow yourself to.
- guess_who_is 3mo agoSIMD does not pay, speaking from 20yr exp in the field in various semis.
- bigwhite 3mo agoThe Go language has long lacked official support for SIMD instructions, which means it has been at a disadvantage in terms of performance optimization. In recent years, with Go 1.26, an experimental version of the SIMD/ArchSIMD packages was introduced for AMD64 architecture. With Go 1.27, a portable version of the SIMD package was also added. Now, we can fully utilize native SIMD instructions to optimize go program performance. - https://pkg.go.dev/simd/archsimd@go1.26.5 https://pkg.go.dev/simd/archsimd@go1.26.5
- pjmlp 2mo agoI think too many people get dismissive of reaching out to Assembly in other languages, while in C and C++, having to reach out to Assembly to do exactly the same is seen as an advantage versus other languages. https://github.com/kelindar/simd https://github.com/kelindar/simd https://github.com/viant/vec https://github.com/viant/vec However, having it officially supported is definitely much more convenient.
- kiaansaraiya 3mo agoI'd slightly rephrase the title to "everyone should know when SIMD didn't happen." Modern compliers are extremely good at vectorization until they suddenly aren't, an they'll often fall back to scalar code because if assumptions or a single-data dependent branch. Learning to check the compliers optimization reports is arguably more valuable.
- alberth 2mo agoThat’s exactly what happened: https://xcancel.com/mitchellh/status/2079672171321081908#m https://xcancel.com/mitchellh/status/2079672171321081908#m
- Joker_vD 2mo agoYou know, it's always funny to read takes like "A broken compiler forcing you to write explicit SIMD instead of trusting auto-vectorization and coming out 20-30% faster is the best argument I've seen for reading your own generated assembly occasionally instead of assuming the compiler has you covered" because you can quite easily imagine an alternative one like "A broken compiler revealing that the auto-vectorization actually already accounts for 50% of total speed up of O3, and manual reimplementation and code restructuring provided only additional 20% in some scenarios is the best argument I've seen for almost never bothering with hand-crafting assembly anymore".
- imtringued 2mo agoYeah it's strange how they brushed away that the compiler reached 77% of the hand crafted performance without even trying.
- spockz 2mo agoIs there some way to write unit tests for cases where you know vectorisation should have been applied? I guess micro benchmarks should cover the performance part. We have ArchUnit to cover code structures, it would be nice if something similar exists for generated assembly.
- Jtarii 3mo ago[flagged]
- jihadjihad 2mo agos/site/industry/g
- applfanboysbgon 2mo agoNo kidding. I saw no less than five "the compiler will do everything for me, I trust in the magic" comments before I gave up reading through the thread.
- dang 2mo ago"Please don't sneer, including at the rest of the community." It's reliably a marker of bad comments and worse threads. https://news.ycombinator.com/newsguidelines.html https://news.ycombinator.com/newsguidelines.html
- itsbczurstupid 2mo ago[dead]
- taylodl 3mo agoI think more developers would use SIMD if there were macros handling the details.
- snvzz 2mo agoEasier than ever, with RISC-V Vector (RVV), which is part of RVA23.
- saagarjha 2mo agoI don’t think any of this really reaches to the level of individual SIMD implementations in hardware
- throwatdem12311 2mo agoLove Mitchell’s writing and he’s one of the few people in the industry that I truly admire. But you need a better color scheme for your light theme my guy. Greys on greys on whites with bright pinks and light blues…it’s really hard to just read.
- cowsandmilk 2mo agoI always must ask, why isn’t your compiler doing this for you? I know they often aren’t because I’ve seen speed ups from writing SIMD or using the vector functions in MKL, but this is something I really think the compilers should do for us in the simple case.
- saagarjha 2mo agoIt’s really hard to make this work in general
- jwgarber 2mo agoThe last few days I've been using AVX-512 to optimize matrix operations in a bioinformatics project, and it's great! The bottleneck in most applications is reading the large dataset from memory, so rather than doing it multiple times to compute multiple operations you can do everything in one pass (fused kernel) with AVX registers. 5x speedups are quite common. I've been doing it with manual intrinsics, but the wide crate also makes common operations completely trivial. Highly recommend checking it out. https://docs.rs/wide/latest/wide/ https://docs.rs/wide/latest/wide/
- lz400 2mo agoIsn't the better abstraction here to use a higher level library in the style of pandas/polars that will operate as vectors, compose and feel readable and inuitive, while (almost?) maxing out SIMD?
- saagarjha 2mo agoMost code cannot be expressed this way unfortunately
- lz400 2mo agoSure, but we're not talking about most code, we're talking about "code that would benefit from SIMD"
- spider-mario 2mo agoThat can easily cause you to traverse your data several times when once would be enough. Let’s say you wanted to compute “mean of array divided by max in absolute value”. NumPy-like: mean = np.mean(array) maximum = np.max(np.abs(array) return mean / maximum As far as I’m aware, that will be evaluated as three traversals. Highway: HWY_FULL(float) d; using V = decltype(hn::Zero(d)); V sum = hn::Zero(d); V max = hn::Zero(d); hn::Foreach(d, values, N, hn::Zero(d), [&](auto d, auto v) HWY_ATTR { sum = hn::Add(sum, v); max = hn::Max(max, hn::Abs(v)); }); const float mean = hn::ReduceSum(d, sum) / N; return mean / hn::ReduceMax(d, max); Just one pass. When the actual computation is made so much faster by SIMD, memory bandwidth starts to be a significant bottleneck.
- lz400 2mo agoThanks, that's insightful. I haven't thought about it that way but in retrospect it's clear. In practice I think we've all seen that the numpy style construction is quick to write and performs (and reads) much better than "dumb loops", but if you really want to optimize your code, the highway version will give you more control (and will read similar to the dumb loop with some decorations). I guess just more tools in the toolbox!
- rao-v 2mo agoIt distresses me that we don’t have a language that can do a best effort parallelization of arbitrary loop like code across SIMD, multiple threads, multiple cores and GPU with a small directive. I don’t need it to be optimal, just … handy as an option! The last time I brought this up here, folks offered a bunch of options that don’t quite do this, and the best candidate was this 15 year old compiler project that is Intel specific! https://ispc.github.io/ https://ispc.github.io/ Could some programming language nerd build this? (While you are at it give me a clear idiomatic way to pay the cost to switch from array of structs to struct of arrays)
- saagarjha 2mo agoThe problem is you need both a PL nerd and a performance nerd and while that group has some overlap so these people are not as uncommon as you’d think the task is pretty hard so you need a lot of people on it, with a bunch of funding, etc. Usually it’s just cheaper to rewrite all your code by that point and so these efforts fail
- rao-v 2mo agoIt seems like such a tempting gap though. The sort of thing you’d think in 2015 would be an obvious capability of 2026 languages!
- cobbzilla 2mo agoIf you’re processing N things at a time and your “scalar tail” is N-1 why can’t you put in a dummy value for the last entry, run one last SIMD iteration, and discard the dummy return value?
- saagarjha 2mo agoYou can, but usually the code may have problems with this. For example, storing out of bounds is often going to give you a bad time. Some platforms that are all SIMD all the time will support masked operations for this kind of thing.
- spider-mario 2mo agoThere is some discussion of the possible options here: https://github.com/google/highway#strip-mining-loops https://github.com/google/highway#strip-mining-loops
- tbrownaw 2mo agoMost of my stuff isn't CPU-bound. Most of it is in managed languages, like Bash and C# and SQL and Python and Yaml and .tsx . It's been at least a decade for me since SIMD was more than an implementation detail handled by the runtime. And even then it was "how do I avoid preventing the runtime from vectorizing this".
- ceautery 2mo agoIt seems like somewhere in there he could have explained the acronym.
- _jackdk_ 2mo agoSingle Instruction, Multiple Data.
- peterashford 2mo agoI've been using the Vector API in Java to get some massive speedups for flowfield generation. There's no guessing with that approach - if the hardware supports SIMD, you get it.
- pjmlp 2mo agoNow with Valhala finally getting merged, he can hope the end of preview releases for the Vector API is coming to an end, probably it will take at least until Java 28, though.
- Culonavirus 2mo agoOr... You know... Tell your favorite agent to "SIMD this" (:
- teo_zero 2mo agoGood article! I just wouldn't start off with bold sentences as > SIMD can be simple to understand and > writing SIMD is just about as easy as a for loop and then the first example requires 12 lines to replace one line of scalar code. Be honest and say SIMD is hard but the results are worth it! (Another nitpick: if this article is for newbies, don't use SIMD-only words and concpts before explaining them. Step 5 is good: scalar tails are mentioned and described. Step 1 is bad: nobody is supposed to know what broadcast mean.)
- amelius 2mo agoIt's all relative though. The rules of playing bridge (the card game) are much harder than understanding the rules around SIMD instructions.
- Sesse__ 2mo agoGF2P8AFFINE has entered the chat. (I've both played bridge actively and written SIMD code professionally, bridge rules are way simpler. Actually playing good bridge is probably harder.)
- atoav 2mo agoThis is probably one of the biggest sins in technological teaching. Sure it is crucially important to take away the fear of a topic. But you don't do so by saying it is simple, you do so by showing it is simple. And it turns out sometimes you cannot show it is simple, because it is in fact very complex. But every complex topic is made up of smaller, simpler ones. Good teachers then manage to find a good order of those smaller parts that makes the steep hill climbable. Then you only need to convince people it is actually worth climbing.
- FartyMcFarter 2mo agoTrue on all counts. Not sure why you're getting downvoted.
- 2mo ago
- d5lt5 2mo agoRemember that bug with Intel Skylakes [0]? When an application used AVX, it slowed down everything else on that node. It was by far not easy to debug why some applications randomly suffered perf hits on a new hardware being rolled out in Azure. [0] https://arxiv.org/abs/1901.04982?utm_source=chatgpt.com https://arxiv.org/abs/1901.04982?utm_source=chatgpt.com
- p_l 2mo agoIt was an early AVX-512 unit that resulted in down lock when you used more than a few instructions in short time, as well as resulted in a Linus rant. Later CPUs (updated skylake xeons and later, AMD Zen 4 and newer) don't have the issue
- adrian_b 2mo agoThe Intel server CPUs Skylake Server, Cascade Lake and Cooper Lake, which had bad frequency/voltage management are now ancient history and very few of them have been used as workstation CPUs by individual users. AMD Zen 4 and Zen 5, and also those Intel CPUs with AVX-512 support starting with Ice Lake, behave much better and there is no reason to avoid AVX-512, which has much better energy efficiency than the alternatives.
- d5lt5 2mo ago"Those who cannot remember the past are condemned to repeat it."
- lucideer 2mo ago> Every developer should know at least that much SIMD. > This [...] applies to any programming language. Support for SIMD instructions varies by programming language This is a very pedantic nitpick because this article is good (& getting SIMD support across more languages would also be good), but the "every programmer should know" line feels a bit odd when neither of the 2 most popular languages natively support SIMD.
- lionkor 2mo agoI would venture as far as to say that the most popular languages might not be the most used by software engineers, as in, people this kind of article would be aimed at.
- lucideer 2mo agoI'm not fully sure what this means but perhaps you're right about Python given its use by data engineers, academics, SREs (though they're mainly Go these days ... which also lacks fully stable native SIMD support). Otherwise though I'd say the rest are used almost exclusively by software engineers. Unless I'm misunderstanding you.
- akhenakh 2mo agoStill hidden behind an experimental flag but SIMD is fully functional in Go: https://pkg.go.dev/simd/archsimd https://pkg.go.dev/simd/archsimd
- avaer 2mo agoI agree in spirit, but most serious cases of this class of problem have moved to accelerated kernels (e.g. GPU). Which has some commonality but is different enough that a lot of these learnings don't translate. There might be a narrow class of problem where: - SISD is the bottleneck - Compiler won't autovectorize - Data is small or weird enough, or the environment constrained enough that running it on an accelerator is not feasible But that seems an increasingly small scope for something "everyone should know".
- hashmal 2mo agoor when you have stronger real-time constraints than "send it to the GPU and hope it comes back fast enough" (e.g. real-time audio processing)
- exDM69 2mo agoThe example code in this article is using Zig's portable SIMD features. Similar features are available for C/C++ (GCC/Clang extension) [0], (nightly) Rust [1] and C++26 [2]. All of these provide a similar set of features and you can use normal arithmetic operations (+, -, *, etc) for SIMD vectors. Together with templates/generics you can also write code that can deal with any vector width. These get compiled to LLVM vector types and will generally give you pretty good generated code. This is a very good way of writing basic SIMD code and has the benefit that your code can be compiled to multiple instruction sets. I've been working on a project that can compile down to SSE2, AVX2, AVX-512 and NEON, with just a change of compiler options. Somewhat surprisingly I get the best performance by using 2x the native vector width (ie. f32x16 = 512 bits on 256 bit AVX2), which is kinda like unrolling the loop once. There are some caveats, though. You will need to keep an eye on the generated assembly code to make sure you're on the happy path. You will inevitably need to drop down to ISA specific intrinsics every now and then (for that fast reciprocal square root with `__mm_rsqrt_ps` etc). As an example I needed to do a gather load from an array of fp16's on AVX2, which does not do 16 bit loads. Rust's `Simd::gather_select` takes 64 bit usize as the index but AVX2 doesn't do 64 bit indices. But as long as I did all the index arithmetic in 32 bits and cast to usize at the last second, the compiler did what I wanted. But you need to kinda know what is available in the ISA to stay on the happy path. Not really an issue with arithmetic. I'm sure that an experienced SIMD programmer can get better performance by writing intrinsics manually (say 5-20% better) but I'm already at 3-6x better than the scalar implementation I started with. And you'd have to write (and benchmark) the code for each ISA separately, meaning that you'd spend at least five times more time with it (and have 5x more code to maintain). [0] https://gcc.gnu.org/onlinedocs/gcc-4.6.1/gcc/Vector-Extensions.html https://gcc.gnu.org/onlinedocs/gcc-4.6.1/gcc/Vector-Extensio... [1] https://doc.rust-lang.org/nightly/std/simd/index.html https://doc.rust-lang.org/nightly/std/simd/index.html [2] https://en.cppreference.com/cpp/numeric/simd https://en.cppreference.com/cpp/numeric/simd
- e4m2 2mo agoFYI you linked to a really old version of the GCC documentation. Google apparently loves those old docs, so they often show up near the top of search results despite being ancient. For posterity, here's the latest version: https://gcc.gnu.org/onlinedocs/gcc-16.1.0/gcc/Vector-Extensions.html https://gcc.gnu.org/onlinedocs/gcc-16.1.0/gcc/Vector-Extensi....
- CraigJPerry 2mo ago>> you'll begin to naturally decompose every for loop into these five steps Does zig have auto vectorisation? I'm thinking that if you write the code in a vector friendly way, then the compiler can do the boiler plate for you https://llvm.org/docs/Vectorizers.html https://llvm.org/docs/Vectorizers.html https://inside.java/2025/08/16/jvmls-hotspot-auto-vectorization/ https://inside.java/2025/08/16/jvmls-hotspot-auto-vectorizat...
- cubefox 2mo agoThe author of this post seems to be unaware that many popular programming languages don't even support writing explicit SIMD instructions, which undermines its main thesis.
- sevenzero 2mo agoCode should at all points be easy to reason about. Is SIMD easy to reason about? No.
- penguin_booze 2mo agoDoes anyone know of a good hands-on introductory and practical tutorial on SIMD? I know that SIMD doesn't imply particular instruction set but a pattern of concurrent data transformation. I guess what I'm looking for is something that addresses the common idioms in SIMD. For example, want to do concurrent data look up? This is how you do it in SIMD; this is how you search; this is how to prepare your data in a manner conducive to SIMD operations; these are the data types you typically find in programming languages, like __mm128; so on and so forth.
- junon 2mo ago> I know that SIMD doesn't imply particular instruction set but a pattern of concurrent data transformation. Eh. It's both. You don't have one without the other. SIMD is pretty much entirely extensions to base processor ISA; there might be a few architectures that have SIMD as a basic part of them (GPUs are one of them, taken to an extreme order), but for the most part you have to know which instruction set you're working with. SIMD is basically "pack multiple data points into a single CPU register, then to another, and perform some operation on them as if you ran that op on all of the pairwise data points individually". Some ops are binary (arithmetic, bitwise, etc) and some are unary. Some have "gates" whereby you can do a comparison, the boolean output of which is stored in a bit packed integer. Then you can run conditional instructions after that that only perform the instruction if the bit in that variable is set, creating "constant time" SIMD instructions with what amounts to branching. Really depends on the instruction set's capabilities. AVX512 is far and away one of the most extensive extensions, with a ton of super niche instructions meant for enterprise number crunching. It has weird stuff, like swapping bytes, collating them, doing all sorts of weird manipulations. But the number of x86 CPUs that support that instruction set are small. You can't emit code that has e.g. AVX512 and just run it on a CPU that doesn't have that extension. You get a CPU exception and it crashes the process (or, if this is in kernel/driver land, your machine). So they're kind of tied together. Anyway if you want to see a list of them, Intel's SIMD intrinsics site has always been really nice to browse IMO. https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html https://www.intel.com/content/www/us/en/docs/intrinsics-guid...
- MomsAVoxell 2mo agoI started learning multi-platform (x86 + ARM) SIMD last year by writing an audio synthesizer: https://github.com/seclorum/SIMDSynth https://github.com/seclorum/SIMDSynth It has been a very rewarding experience, and the synth architecture - multitimbral polyphonic - provides a great stream of data for applying SIMD principles, i.e. multiple streams going through the same process. Has been pretty hard to debug, though. I found myself wishing I had some sort of simulator to help me understand the state of things in each pipe. I suppose I should spend some time investigating SIMD tools next time I get into this - but I fear it'll require a lot more investment. If anyone has any tips, I'm all ears ..
- Daffrin 2mo ago[flagged]
- sgt 2mo agoIs SIMD in rust still pretty bad?
- Archit3ch 2mo agoIn some cases the ergonomics are worse than C, which is a feat in 2026. Portable SIMD is (perma?) nightly. Requires 'unsafe' everywhere. To skip bounds-checking, you typically need to switch your writing style from loops to iterators. No JIT, so you need multiversioning and/or target-cpu=native. Since Rust doesn't bring a compiler, you need to predict all your target architectures in advance. Cannot inspect @code_llvm/@code_native at the function level like Julia, you need to compile the entire app. Prioritizes Floating-Point strictness over --ffast-math. It's a genuine win for safety, but that's overkill in some domains (e.g. graphics, games, audio).
- sgt 2mo agoYes seems like too much work or hassle. I'm kinda looking at Java as a Rust replacement, and Zig as a C replacement. This might just be the ultimate combo.
- dsauerbrun 2mo agoAnd here I am pooping out simple typescript... These articles always make me feel like I'm wasting my talents working on products that don't really have the need to leverage any understanding of what's happening under the hood at the CPU instructions level.
- corysama 2mo agoWriting a Gameboy emulator is a great cure for that ;)
- lifthrasiir 2mo agoI think an even better advice is that everyone should know array programming, because you generally need that mindset for SIMD optimizations as (packed) SIMD-specific techniques are surprisingly rare. And array programming gives you a generally performant code even without SIMD because it is much easier to auto-vectorize.
- subset 2mo agoI'm no fan of closed-source languages, and lord knows MATLAB has its warts. But I can't deny that it was pretty seamless to write efficient vectorised code for numerical simulations at uni. I don't have much experience with it, but my understanding is that Julia is the closest thing to a more modern and expressive language that has similar vectorisation capabilities.
- disgruntledphd2 2mo agoR and Numpy are also pretty good at this (and Julia was inspired by both of these and Matlab). In fact, I'm reasonably certain that R (known as S in the 70's) was the first real language designed around this concept.
- MarceColl 2mo agoI guess it depends on what real language means to you, but APL[1] existed before and it's fully array based. [1]: https://en.wikipedia.org/wiki/APL_%28programming_language%29 https://en.wikipedia.org/wiki/APL_%28programming_language%29
- disgruntledphd2 2mo agoWhen I writing the comment I was like, maybe APL was first? Nah, no-one will claim that. So congratulations on nerd-sniping me ;)
- subset 2mo agoI learned R for statistical programming at uni, where there was less emphasis on vectorised computation, so it didn't jump to mind, but yes! I've never quite felt like numpy 'clicked' for me in the same way, though. It always felt a bit bolted on, which I suppose it is, as a library (though @ operator overloading etc makes things somewhat nicer now).
- z0ltan 2mo ago[dead]
- dupontcyborg 2mo agowe make extensive use of simd at work, usually through highway: https://github.com/google/highway https://github.com/google/highway imo this is one of the greatest libs ever written. it handles dynamic dispatching of correct simd instructions / lane widths for various hardware with just one simd loop written (handling NEON/AVX/AVX2/AVX512/extensions) with comparable performance to handwritten native intrinsics
- foo-bar-baz529 2mo agoThe vast majority of developers have 0 need for learning SIMD. Why mislead them, and make them feel like to be a "real" developer they have to know it?
- ch4s3 2mo agoIt seems like its useful to be aware of at the very least. Surely every developer has written a hot loop that adds or compares simple terms. Knowing that a compiler COULD in theory optimize this for the target CPU architecture is useful in many cases.
- simondotau 2mo agoThe parent comment is now YouTube famous, and apparently represents the entire Hacker News zeitgeist. Reading replies to this article, it seems... like a slight exaggeration? https://www.youtube.com/watch?v=4nJ2tEPD4-k&t=48s https://www.youtube.com/watch?v=4nJ2tEPD4-k&t=48s
- clear-octopus 2mo ago[dead]
- fithisux 2mo agoI enjoyed it. Great is the fact that examples are given.
- mwsherman 2mo agoI recommend starting with SWAR [1] before SIMD. Our registers are typically 64 bits, and one can try out SIMD patterns without taking a dependency on particular hardware. This will only be effective if the data you’re working on is smaller than 64 bits. If you’re working with bytes, for example, you might get 8x parallelism. [1] https://en.wikipedia.org/wiki/SWAR https://en.wikipedia.org/wiki/SWAR
- jmull 2mo agoHere's some important accompanying advice: * If you aren't profiling, don't bother Otherwise, you are going to be wasting plenty of time on "optimizations" that don't actually do anything useful. Just come up with at least a few test that represent important cases and time them. Dig in, see where the time is being spent, and focus on areas where significant time is spent, especially ones that look ripe for optimization. Also: * Think about laying out your data in ways that accommodate your access patterns and are cache-friendly. SIMD would typically follow from this, not be a starting point on its own.
- arendtio 2mo agoEveryone should know SIMD, okay, but please don't introduce that kind of code into my codebase for a linear 5x improvement. If you work on a performance-critical application, that's fine, but most of the time we use some interpreted / VM / garbage-collected slow environment that performs so much slower than just using hardware directly, and we are fine with it. Under those circumstances, it is much better to have code that every developer can understand than to introduce a 5x improvement, with code that only the person who wrote it understands. I mean, e.g. for Go, this simplicity was one of the explicit design goals. For C / Rust / Zig, it might be a different story, but I hope you choose those languages only if they fit your use-case.
- deleted 2mo ago[deleted]
- rurban 2mo agoThere is no need to learn SIMD. Just use proper vector_size attributes. Even without the fragile auto-vectorizer, the compiler uses SIMD then. Even with -O0