7 ms·
C++26 Shipped a SIMD Library Nobody Asked For
- feelamee 5mo ago> The pattern is clear: every major project that actually needs portable SIMD in production chose a third-party library or a different language of course they chose third-party library, because C++26 is just only published and don't have wide support/adoption experience. > And the most damning data point might be EVE itself — a committee member looked at std::simd, decided it wasn’t good enough, and built his own library. It's just a manipulation. First commit in eve[1] was published in 2018. There was no any std::simd in standard at that time. > Nobody waited for std::simd. By the time it ships in C++26, these libraries will have a decade of production battle-testing, real user feedback, and cross-platform coverage that std::simd can’t match on day one. Manipulation 2. No library have decade "of production battle-testing, real user feedback, and cross-platform coverage" on day one. So, why their authors created them? > Including <experimental/simd> pulls in deeply nested template machinery — simd.h, simd_x86.h, simd_builtin.h, and friends. A trivial function computing sin on a SIMD vector takes about 2.2 seconds to compile. The equivalent scalar for-loop? 0.2 seconds. Would be more interesting if you compare this with precompiled headers and C++20 modules. > The std::simd version? It emits actual vsqrtps + vmulps because the optimizer can’t perform algebraic simplification through opaque template function calls: opaque template function calls? What is this? Of course there is 1000 examples when compiler can do better job with scalar loop. And there is 1000 examples when it can't. But, for some reason people do write simd manually. Probably because they want predictable code generation - no massive slowdown on another compiler/another compiler version/another cpu/another one line of loop changed. > sqrt(x) * sqrt(x) what compiler would generate with manual simd intrinsincs? I doubt the same as scalar mul. > The frustrating part is that the problems are well-understood. SIMD programmers have been asking for the same things for years, and none of them are in std::simd. Show me your proposal with critique of std::simd, if you asking for them. Or at least someone other proposal. How you can understand that someone asking? Too many emotional statements in, too little technical details. [1] https://github.com/jfalcou/eve https://github.com/jfalcou/eve
- magicalhippo 5mo agoThe linked[1] "six reasons to use std::simd" was just what I needed after a long week. Hilarious! [1]: https://github.com/NoNaeAbC/std_simd https://github.com/NoNaeAbC/std_simd
- AlotOfReading 5mo agoThat certainly convinced me. When I was doing my taxes recently and had to watch those forced loading animations, I kept asking myself "why can't my compiler do this?" Thanks to std::simd, now it can!
- mgaunard 5mo agoisn't that just QoI issues? There's a reason why the libstdc++ folks labelled their implementation as experimental.
- boring-human 5mo agoIt should have been "eight reasons to use std::simd". Inefficient.
- Rendello 5mo agoThis is the first time I've seen a classic for loop called a "boomer loop", but apparently this isn't even the first instance (not the first definition): http://boomer-loop.urbanup.com/18229646 http://boomer-loop.urbanup.com/18229646
- jandrewrogers 5mo agoI have written a lot of SIMD for both x86 and ARM over many years and many microarchitectures. Every abstraction, including autovectorization, is universally pretty poor outside of narrow cases because they don’t (and mostly can’t) capture what is possible with intrinsics and their rather extreme variation across microarchitectures. If I want good results, I have to write intrinsics. No library can optimally generate non-trivial SIMD code. Neither can the compiler. Portability just amplifies this gap. I think a legitimate criticism is that it is unclear who std::simd is for. People that don’t use SIMD today are unlikely to use std::simd tomorrow. At the same time, this does nothing for people that use SIMD for serious work. Who is expected to use this? The intrinsics are not difficult but you do have to learn how the hardware works. This is true even if you are using a library. A good software engineer should have a rough understanding of this regardless.
- mgaunard 5mo agoFor me the main issue is that if you're serious about SIMD, you need to use a state-of-the-art library and can't rely on some standard library whose quality is variable, unreliable, and which is by design always behind.
- jandrewrogers 5mo agoFor some algorithms you have to compromise the data layout for compatibility across the widest number of microarchitectures by nerfing the performance on advanced SIMD microarchitectures working on the same data structures. There really isn’t a way to square that circle. You can make it portable or you can make it optimal, and the performance gap across those two implementations can be vast. In the 15-20 years I’ve been doing it, I’ve seen zero evidence that there is a solution to this tradeoff. And people that are using SIMD are people that care about state-of-the-art performance, so portability takes a distant back seat.
- mattip 5mo agoNumPy has a whole dispatch mechanism to deal with the tradeoffs. The main problem is code bloat: how many microarchitectures are you going to support with dispatch at runtime?
- mgaunard 5mo agoI made the first proposal to the C++ standard committee to introduce SIMD in 2011, before Matthias Kretz got involved with his own version (which is what became std::simd). This was based on what eventually became Eve (mentioned in the article). Back then, it was rejected, for the same arguments that people are making today, such as not mapping to SVE well, having a separate way to express control flow etc. There was a real alternative being considered at the time: integrating ISPC-like semantics natively in the language. Then that died out (I'm not sure why), and SIMD became trendy, so the committee was more open to doing something to show that they were keeping up with the times.
- AlotOfReading 5mo agoTrying to abstract over SVE with a SIMD library is a bit of a fool's errand. The intended programming model is just too different from traditional ISAs, and there are algorithms that are nearly impossible to write efficiently for it. All the ones I've seen wrap it up as a bastardized fixed length ISA, and even ARM's own guidance basically recommends that approach. Frankly, the length agnostic stuff is a mistake that I hope hardware designers will eventually see the light on, like delay slots.
- stephbook 5mo agoI'm no C++ dev, but as an outsider, it sure reads like the whole "int is variable length" mistake again.
- pjmlp 5mo agoThat abstraction is occasionally usable in low level systems code, that is why Go, Rust, D and C# support it as well. Also to note that is C not C++.
- IshKebab 5mo agoIn a way it's worse because at least with int you're not really expecting to run the same binary on architectures with different int lengths, and also for several decades there have only been two realistic options (32 or 64), which makes it easy to deal with. With RVV (and SVE I assume) there are a wider range of realistic options - at least 128, 256 and 512. The RVV spec allows up to 65536! Also it's totally reasonable to want a single binary to work with all of them so then you're into compiling parts of your code multiple times with runtime dispatch which is a right pain. I haven't looked into how Highway does it but I don't really know you you write length-agnostic code in high level languages. It's easy in assembly, but it sucks if you have to do it in assembly.
- countWSS 5mo agoGCC already solved it: https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html The operations behave like C++ valarrays. Addition is defined as the addition of the corresponding elements of the operands. For example, in the code below, each of the 4 elements in a is added to the corresponding 4 elements in b and the resulting vector is stored in c.
- groundzeros2015 5mo agoThanks!
- Someone 5mo agoAnd these are also available in clang. https://clang.llvm.org/docs/LanguageExtensions.html#vectors-and-extended-vectors https://clang.llvm.org/docs/LanguageExtensions.html#vectors-...: “Vectors and Extended Vectors Supports the GCC, OpenCL, AltiVec, NEON, SVE and RVV vector extensions”
- wahern 5mo agoThose type attributes are also used for the x86 intrinsics API, and they override default C behaviors like promotions and presumptions around aliasing (ironically they make type punning easier, though maybe it was just the few use cases I explored, and this isn't an area where I have alot of experience). C23 also gained the _BitInt type, which discards all the old promotion rules, which should help autovectorization. I think ISPC is still the proper way to go. But these days everybody wants One Language to Rule Them All along with standard libraries for doing everything out-of-the-box. And while in principle ISPC's approach could be stitched into C or C++ in a fairly clean manner (perhaps with well-defined and enforced segregation of constructs to minimize complexity), it's just not gonna happen: C++ is too enamored with constructing libraries through deeply complex templated types (hammer, nail, yada yada), and C is just too conservative (though if GCC or clang went the distance with a full implementation, there's a good chance the C committee would adopt it).
- plasticeagle 5mo agoNobody should read that AI slop article. Nobody. Maybe there's an interesting story in there, it's certainly possible. But the "author" could not be bothered to write it, and so why should we waster our time reading it?
- usrnm 5mo agoI read it and found it interesting
- pjmlp 5mo agoI love people praise Claude for doing their work, every day on HN, while at the same time complaining about AI in articles.
- cenamus 5mo agoWho says these are the same people?
- pjmlp 5mo agoStatistics.
- bakugo 5mo agoGlad to see the classic goomba fallacy in action even here on HN.
- jjmarr 5mo agoI praise Claude and hate AI articles because I could've asked Claude to dumb down the debate if I wanted. Articles should be high information density and summarizable with Claude.
- pjmlp 5mo agoSome would argue code should be the product of craftsmanship and vibe coding has no place in it.
- ori_b 5mo agoSlop.
- jcranmer 5mo agoIf you thought std::simd was a library nobody asked for, just wait until you hear about <linalg>. I feel like half the people looking forward to that think they're just going to get standard C++ bindings to LAPACK, when instead they're probably going to get an unoptimized, slapdash implementation of LAPACK written by people who aren't good at BLAS. As for SIMD itself, designing a good SIMD library is difficult because there are several different SIMD approaches and some of them work poorly for certain use cases. For example, you can take an HPC-ish approach of "vectorize this loop" (à la #pragma omp simd) and have the compiler take care of a fairly mechanical transformation. Or you can take an opposite approach of treating a 128-bit SIMD vector as a fundamental data type in your language. Which approach is better depends on your use case.
- pwdisswordfishq 5mo agoJust wait until you hear about std::hive. The work of one obsessive author, who never gave a good explanation for why the thing needed to be in the standard library instead of an external one. The committee was apathetic about the proposal and kept bringing up various trivial issues, in a clear attempt to stall him, but he refused to take the hint. So eventually they relented. Outside coverage I have seen so far seems to be to the tune of "WTF is this weird thing?" and quickly glosses over it. I wonder if it's going to end up like the export keyword.
- tialaramex 5mo agoI feel like std::hive fits right in to the C++ stdlib group of collections The least stupid is std::vector which is just the typical O(1) amortized growable array type found in most modern languages, with a mediocre API. 8/10 could do better. std::array is just the built-in array type C++ should have but doesn't. This shouldn't be a library type, that's embarrassing. std::deque looks like you're getting something like Rust's VecDeque but you aren't, it's a weird hybrid optimisation which presumably made sense on some 1980s hardware. I asked STL once to explain what it's even for and they didn't know. [[For reference STL is the name of the guy in charge of Microsoft's implementation of the C++ standard library, Microsoft also calls that library STL for reasons we needn't address]] std::list is the extrusive doubly linked list. This type makes sense in a DSA class. Why is it in the C++ standard library? I dunno, maybe C++ is intended only as a teaching language? std::forward_list is the extrusive singly linked list. You know, for a different seminar in that same DSA class. You might want the intrusive linked list, you don't want this. std::map and std::set are probably red-black trees. OK, you might need those and for some reason not care about the details (which aren't specified here) std::multimap and std::multiset are even less obviously useful. I have never seem them used in real software. Why are they in the standard library? std::unordered_all_of_the_above_maps_and_sets look like the simplistic hash table you'd be shown in an intro DSA class either taught by somebody who doesn't know the subject well or aiming to cover the basics and get back to their research. This will perform poorly on any hardware with features like a cache. The C++ stdlib carries broken garbage basically indefinitely. C++ doesn't have the same library stability promise that Rust has, but in practice stuff that nobody cares about is never removed.
- fithisux 5mo agoWhy not just writing inline assembly is not enough? You optimize for a specific target. The problem is that you cannot be cross-platform. Sure. But that is why software is incremental. I write for my HW, not yours. You can write for yours. Make folders with implemntations x86_v1 x86_v2 arm64 riscv64 ... ... ... and include
- Pannoniae 5mo agosadly inline assembly is still at the ergonomics of "one compiler doesn't support it in x64 mode" and "you can choose between the readable syntax (which is a black box to the compiler) and the unreadable syntax (which can specify I/O/clobber regs)"
- msla 5mo agoOK, is there a horrible speed penalty for writing your SIMD in pure assembly functions and then calling those functions? If you're writing assembly anyway, just drop the "inline" part.
- Pannoniae 5mo agoSure, if you're willing to write a large enough chunk that you can eat the cost of not inlining it. If you just write a small leaf function or two, it will probably be a wash or perform worse.
- deleted 5mo ago[deleted]
- zombot 5mo agoThe article's point in a nutshell: > The problem is that std::simd in 2026 is the 2012 solution arriving after the world moved on. The committee spent a decade polishing a library-based approach while compilers solved the easy cases automatically and ISPC solved the hard cases with language-level support. I find it interesting that the C++ committee would make that kind of mistake. Shouldn't they know better?
- tialaramex 5mo ago> I find it interesting that the C++ committee would make that kind of mistake. Shouldn't they know better? The main reason why people attend WG21 meetings is to get their pet features into the C++ language or the associated standard library. To some extent you can further that goal by shooting down other people's suggestions, especially if they would conflict ‡ C++ is a vast sprawling language. There are no genuine "C++ experts" for the same reason there aren't any people who know all of mathematics. There are a lot of people who are experts on some corner of the language or its libraries, and some who know a little bit about almost everything but no overarching experts. ‡ A good way to do this work would try to have such rivals all work together to improve the language, a sort of "yes, and" collaborative approach but although this has occasionally been able to work in C++ the whole WG21 structure works against it, in particular they vote to achieve consensus, which is not what the word "consensus" means and rewards appeasing haters much more than it does finding out what the problems are and working to fix them.
- fg137 5mo ago(having no first hand experience with WG21) Many people think there are a lot of problems with C++ committee and the standards process. Some would claim that the ISO governance model doesn't work at all. There is a lot of drama that the outside world has no idea about, because much of the discussion is behind closed doors. You can look it up (e.g. on blogs and /r/cpp), but I am not linking to anything because lots of content is very biased and hard to verify. Something that is not controversial and worth a read is https://thephd.dev/embed-the-details https://thephd.dev/embed-the-details and you can see the point. I do feel the TC39 (the group behind the ECMAScript/JavaScript standard) seems more practical and effective. There are disagreements and dramas but not nearly as bad as with C++.
- raverbashing 5mo agosigh C++ sits on that weird abstraction level where it wants to be a higher level language but it keeps grinding their gears on stuff like pointer sizes, pointer arithmetic or vector sizes and at the same time wants to keep being C compatible and needs that interface with the lower level world Now compare with how numpy does things: you care about the data size but not the implementation. Still, I didn't expect less (of a crap fest) from the C++ committee as presented here
- secondcoming 5mo agonumpy is a python wrapper over a C library written by people who have ground those gears
- raverbashing 5mo agoYes but not all of them It would be easy to push complexity up at the level of Numpy/Pytorch/Tensorflow but it mostly gets hidden (also a lot of it relies on LAPACK which is Fortran - which kinda works with SIMD better than C/C++)
- ozgrakkurt 5mo agoJust write inline asm for x86 and aarch64 (if you care about that) and not care about the rest. Is it even useful to do simd on other processors? Compiler optimizing even the code around the simd code based on the semantics of arithmetic or other things sounds silly after writing some of this kind of code
- camel-cdr 5mo agoSo you "just" write 4 assembly implementations?
- meling 5mo agoCurious if people here have looked at the upcoming SIMD support in Go: https://go.dev/doc/go1.26#simd https://go.dev/doc/go1.26#simd Currently experimental, but looks like the first Intel arch will arrive in the next release in about 3 months. They are also going to support a portable layer. Wondering what people here think about the approach the Go team is taking; I think they would appreciate more eyeballs on their design. (I’m not competent in this space (yet))…
- camel-cdr 5mo agoLooks like that isn't a portable SIMD abstraction, but more similar to adding architecture-specific SIMD intrinsics support to go, with nicer syntax.
- meling 5mo agoSorry, I didn’t explicitly link to the issue for the portal layer. Here is the issue discussing the portal simd package: https://github.com/golang/go/issues/78902 https://github.com/golang/go/issues/78902
- aphillips801 5mo agoI did a lot of experimentation with the Go1.26 experiment. It's easy to use and produces good code but only supports x86 ATM. (See https://andrewwphillips.github.io/blog/go1p26.html#simd-experiment https://andrewwphillips.github.io/blog/go1p26.html#simd-expe...) I think there will be a "portable" wrapper that will also support other architectures (arm, riscv, even wasm) in the future based on the Highway C++ library.
- aphillips801 5mo agoAlso here is an example of some Go and the Asm code generated: https://godbolt.org/z/n8hKhc7rY https://godbolt.org/z/n8hKhc7rY (click the recompile button if you don't see the Asm code)
- Aardwolf 5mo agoI'd actually rather just have the compiler give some guarantees on producing SIMD code when you write regular C++ code doing sums, multiplications, etc... in a particular way. And perhaps add a few more operators/keywords to the language for modern CPU instructions (we got things like popcount, countl_zero and fma, but what about e.g. pext, pdep, aes, ...)
- Sharlin 5mo agoThe point about the optimizer only seeing "opaque templates and function calls" makes little sense. First off, templates are the opposite of opaque due to the fundamental requirement that the implementation be visible to every translation unit using a template. This makes any function calls trivially inlinable. Second, and the reason for the above requirement, templates are compiled by monomorphization – making a distinct, separately optimizable copy of each concrete instantiation of a template. By the time the compiler backend sees the intermediate representation, there’s nothing about templates left. There are of course reasons why highly abstracted template code may be difficult to optimize, for instance if function call chains are so deep that the inliner gives up. There are also legitimate reasons why a fully language-based solution might beat a library-based one. But one of the points of adding a library to the std is that the standard library is allowed to cheat as much as it wants. It can be deeply integrated to the compiler and implemented entirely using compiler magic if necessary. std::simd may be too little, too late for many reasons, but I doubt any of them is that the compiler can’t see through the code.
- himata4113 5mo agoit's just a compiler hint just like all other hints, parity with other languages.
- DannyBee 5mo agoCompiler guy here - yes, the optimizer claim is,in fact, just totally wrong. The claim is it can't be constant folded, scheduled, etc because it doesn't turn into simd instructions. At the bottom, as you say, it will turn into code. That code is almost certainly builtins or uses the vector extensions or whatever , not raw asm statements. compilers can and do turn these into ir level simd instructions.
- adrian17 5mo agoI agree with you, but just a small nit: > First off, templates are the opposite of opaque due to the fundamental requirement that the implementation be visible to every translation unit using a template. That's not strictly true, you can have an implementation hidden in a separate TU, as long as that TU instantiates the template for all template arguments that the users are going to use.
- sscaryterry 5mo agoUnnecessarily negative article. Lets not forget how awful C++98 was for years. Standardisation doesn't mean useful.
- fg137 5mo agoHmm. I think you missed the point.
- sscaryterry 5mo agoNo, I didn't. The whole premise is contained in the title "Nobody asked for".
- whizzter 5mo agoAgreed, fixed with vectors needs to be a language feature, better compile times and would solve issues for most people. Personally, I think that like Clang way to adding GLSL like vectors and semantics would've gone a long way. SVE might be an elegant design, but in reality there are probably a multiple factor of game and other 3d code being written that needs vectors compared to other fields, and there limited vector sizes aren't really a problem. And honestly, considering the story of AVX512.. with 512 bit vectors being removed from mainstream by Intel, do we really really need longer ones despite it being from a "scalable design"?
- camel-cdr 5mo agoIn GPUs GLSL like types compile down to what basically is variable length SIMD. A vec4 doesn't get compiled to a SIMD vector with four floats, but rather to four SIMD vectors, each containing N FP32 elements (usually 32 or 64). Look at what this simple shader compiles down to on RGA: https://godbolt.org/z/4GrfY61vf https://godbolt.org/z/4GrfY61vf
- whizzter 5mo agoRight, and AVX512 would thus be more relevant if ISPC-like features was mainstreamed in CPU bound C++ compilers.
- adrian_b 5mo agoIntel has been forced to reintroduce 512-bit vectors in the mainstream, because of the competition from AMD. Starting with the Intel Nova Lake CPUs, around the end of this year, all future AMD and Intel CPUs will provide 512-bit vectors, like also the current AMD Zen 5 and Zen 4 CPUs. The 512-bit vector length is more convenient than other lengths, because on the AMD and Intel CPUs it coincides with the length of a cache line. Because of this, it is easier to optimize simultaneously for the best cache usage. For GPUs, which favor throughput over latency, 1024-bit and 2048-bit vector register widths are frequently used. For CPUs it is unlikely that widths greater than 512-bit would be useful, as the vector operations that should be done on CPUs are those for which the high latency of using a GPU is undesirable.
- himata4113 5mo agoSomething everyone is missing is that this is just feature parity with other langauges, this is not c++ specific. They added it because other languages have these compiler hints which are then (usually) used in llvm as opaque types for 'smarter' optimizations. Hand rolled code will still be better, but there are very niche instances where you want llvm to know this information, especially important when you don't care about performance, but you care about data integrity and obfuscation.
- StilesCrisis 5mo ago> When Google needed portable SIMD for production image and video codecs, they built Highway — not std::simd. Sure, they left the committee years ago. I am not trying to claim any sort of direct causality, but it sure seems like this is a case where Google's presence on the committee might have prevented shipping boondoggles like this. Modules is another case where I think Google's feedback might have been able to steer the ship in a better direction.
- raverbashing 5mo agoThey probably left the committee because it keeps going in circles rather than solving the issues of the language
- StilesCrisis 5mo agoYes. The straw that broke the camel's back was the complete refusal to break ABI, locking in bad implementations forever. e.g. unordered_map dramatically underperforms when compared against modern swiss tables, but the committee won't do anything about it. Not to mention the committee's head-in-the-sand policy-based approaches to safety, vs Google's much broader-scoped Carbon effort. More context: https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/difficulties_improving_cpp.md https://github.com/carbon-language/carbon-lang/blob/trunk/do...
- StilesCrisis 5mo ago"The Default Width Problem" -- this section seems confused and definitely reeks of LLM authorship. It's comparing -march=native against std::simd and complaining that std::simd<T,8> breaks portability with pre-Haswell. This is a real issue, but -march=native is no better! It bakes in the SIMD width at compile-time as well, so that binary also won't run on a pre-Haswell machine. It's a real issue but neither side solves it. You need runtime dispatch (a la Google Highway) to solve this.
- Validark 5mo agoIf we want to improve cross-platform SIMD, in my opinion we should start by supporting more operations in LLVM IR. Like vector expansion (currently we only have expandload), runtime-known shuffle vectors, pdep/pext operations. Also, let's stop with the "vector length agnostic" types being the sole option for SVE extensions. I'd rather write an optimized routine for a 16-byte machine I'm targeting and be able to upgrade it in 5 years than have "agnostic" code that wants to pretend like it would work amazingly on all platforms, but the machine I optimized it for is theoretical. I'm fine with recompiling my code, I do it every day. If I have an algorithm that's truly vector length agnostic, I can make the vector length a constant in my code that can change based on the compile target. https://github.com/llvm/llvm-project/issues/113422 https://github.com/llvm/llvm-project/issues/113422 https://github.com/llvm/llvm-project/issues/172857 https://github.com/llvm/llvm-project/issues/172857
- camel-cdr 5mo ago> Also, let's stop with the "vector length agnostic" types being the sole option for SVE extensions They aren't, see the `arm_sve_vector_bits` attribute. > I'm fine with recompiling my code, I do it every day Then you can do that. > If I have an algorithm that's truly vector length agnostic, I can make the vector length a constant in my code that can change based on the compile target. You can do that, but why not simply write it in a vector-length-agnostic way? IMO the better approach is to start thinking about SIMD optimizations in a VLA way, and specialize on the vector length, when that becomes advantageous. Doing it this way is better even if you end up not writing VLA code, because you though about the scalability problem. Many libraries currently don't scale beyond 128-bit, not because they couldn't make efficient use of >128-bit, but because the library was architect around 128-bit and changing that amounts to almost a full rewrite. So now you are stuck wasting 3/4th of your ALUs running 128-bit SSE on Zen5.
- brcmthrowaway 5mo agoWhy did you use an LLM to write this?
- daemin 5mo agoOne thing I will point out is that the code in the article is compiled with `-march=native` and `-ffast-math`, meaning that they're really only compiling for the exact same machine they are running on and no other. This seems like it is mainly applicable to places which can easily recompile code for the exact known hardware that they run on, such as HFT and some scientific computing. Places which compile code to distribute for people to run on a variety of processors and platforms (or that require floating point code to be consistent between them), i.e. games and applications, will still be targetting a low end baseline architecture and therefore have a different outcome. I can say that in this space we are only now reaching the point where we can start compiling for AVX2, as we can expect the lowest end-user processor to support it.