[HN Gopher] The state of SIMD in Rust in 2025
___________________________________________________________________
The state of SIMD in Rust in 2025
Author : ashvardanian
Score : 114 points
Date : 2025-11-05 18:45 UTC (4 hours ago)
(HTM) web link (shnatsel.medium.com)
(TXT) w3m dump (shnatsel.medium.com)
| mdriley wrote:
| > TL;DR: use std::simd if you don't mind nightly, wide if you
| don't need multiversioning, and otherwise pulp or macerator.
|
| This matches the conclusion we reached for Chromium. We _were_
| okay with nightly, so we 're using `std::simd` but trying to
| avoid the least stable APIs. More details:
| https://docs.google.com/document/d/1lh9x43gtqXFh5bP1LeYevWj0...
| vlovich123 wrote:
| Do you compile the whole project with nightly or just specific
| components?
| josephg wrote:
| Why isn't std::simd in stabile yet? Why do so many great features
| seem stuck in the same nightly-forever limbo land - like
| generators?
|
| I'm sure more people than ever are working on the compiler.
| What's going on?
| the__alchemist wrote:
| Would love this. I've heard it's not planned to be in the near
| future. Maybe "perfect is the enemy of good enough"?
| CooCooCaCha wrote:
| Rust doesn't have a BDFL so there's nobody with the power to
| push things through when they're good enough.
|
| And since Rust basically sells itself on high standards
| (zero-cost abstractions, etc.) the devs go back and forth
| until it feels like the solution is handed down from the
| heavens.
| ChadNauseam wrote:
| And somehow it has ended up feeling more pleasant and
| consistent than most languages with a BDFL, even though it
| was designed by committee. I don't really understand how
| that happened, but I appreciate the cautious and
| conservative approach they've taken
| singron wrote:
| There is a GitHub issue that details what's blocking
| stabilization for a each feature. I've read a few recently and
| noticed some patterns:
|
| 1. A high bar for quality in std
|
| 2. Dependencies on other unstable features
|
| 3. Known bugs
|
| 4. Conflicts with other unstable features
|
| It seems anything that affects trait solving is very
| complicated and is more likely to have bugs or combine non-
| trivially with other trait-solving features.
|
| I think there is also some sampling bias. Tons of features get
| stabilized, but you are much more likely to notice a nightly
| feature that is unstable for a long time and complex enough to
| be excited about.
| vlovich123 wrote:
| > Dependencies on other unstable features
|
| AFAIK that's not a blocker for Rust - the std library is
| allowed to use unstable at all times.
| estebank wrote:
| I think they meant on unstable features which might yet
| change their semantics. A stable API relying on unstable
| implementation is common in Rust (? operator, for example),
| but that is entirely dependent on having a good idea of
| what the eventual stable version is going to look like, in
| such a way that the already stable feature won't break in
| any way.
| throwup238 wrote:
| _> It seems anything that affects trait solving is very
| complicated and is more likely to have bugs or combine non-
| trivially with other trait-solving features._
|
| Yep and this is why many features die or linger on forever.
| Getting the trait solving working correctly across types and
| soundly across lifetimes is complicated enough to have killed
| several features previously (like
| specialization/min_specialization). It was the reason async
| trait took so long and why GAT were so important.
| ChadNauseam wrote:
| There really aren't that many people working on the compiler.
| It's mostly volunteers.
|
| The structure is unlike a traditional company. In a traditional
| company, the managers decide the priorities and direct the
| employees what to work on while facilitating that work. While
| there are people with a more managerial type position working
| on rust compiler, their job is not to tell the volunteers what
| to work on (they cannot), but instead to help the volunteers
| accomplish whatever it is they want to do.
|
| I don't know about std::simd specifically, but for many
| features, it's simply a case of "none of the very small number
| of people working on the rust compiler have prioritized it".
|
| I do wish there was a bounty system, where people could say "I
| really want std::simd so I'll pay $5,000 to the rust foundation
| if it gets stabilized". If enough people did that I'm sure they
| could find a way to make it happen. But I think realistically,
| very few people would be willing to put up even a cent for the
| features they want. I hear a lot of people wishing for better
| const generics, but only 27 people have set up a donation to
| boxy (lead of the const generics group
| https://github.com/sponsors/BoxyUwU ).
| Avi-D-coder wrote:
| Usually when I go and read the github and zulip threads the
| reason for paused work comes down to the fact that no one has
| come up with a design that maintains every existing promise the
| compiler has made. The most common ones I see are the feature
| conflicts with safety, semver/encapsulation, interacts weirdly
| with object safety, causes post post-monomorphization errors,
| breaks perfect type class coherence (see haskells unsound
| specialization).
|
| Too many promises have been made.
|
| Rust needs more unsafe opt outs. Ironically simd has this so it
| does not bother me.
| IshKebab wrote:
| I would love generators too but I think the more features they
| add the more interactions with existing features they have to
| deal with, so it's not surprising that its slowing down.
| estebank wrote:
| Generators in particular has been blocked on the
| AsyncIterator trait. There are also open questions around
| consuming those (`for await i in stream`, or just keep to
| `while let Some(i) in stream.next().await`? What about
| parallel iteration? What about pinning obligations? Do that
| as part of desugaring or making it explicit?). It is a shame
| because it is almost orthogonal, but any given decision might
| not be compatible with different approaches for generators.
| The good news is that some people are working on it again.
| JoshTriplett wrote:
| > Why isn't std::simd in stable yet?
|
| Leaving aside any _specific_ blockers:
|
| - It's a massive hard problem, to build a portable abstraction
| layer over the SIMD capabilities of various CPUs.
|
| - It's a massive balance between performance and usability, and
| people care deeply about both.
|
| - It's subject to Rust's stability guarantee for the standard
| library: once we ship it, we can't fix any API issues.
|
| - There are already portable SIMD libraries in the ecosystem,
| which _aren 't_ subject to that stability guarantee as they can
| ship new semver-major versions. (One of these days, I hope we
| have ways to do that for the standard library.)
|
| - Many people already use non-portable SIMD for the 1-3 targets
| they care about, instead.
| vlovich123 wrote:
| > we can't fix any API issues.
|
| Can't APIs be fixed between editions?
| JoshTriplett wrote:
| Partially (with upcoming support for renaming things across
| editions), but it's a pain if the types change (because
| then they're no longer common vocabulary), and all the old
| APIs still have to exist.
| duped wrote:
| std::arch::* intrinsics for SIMD are stable and you can use
| them today. The situation is only slightly worse than C/C++
| because the rust compilers cares a lot about undefined
| behavior, so there's some safe-but-technically-unsafe/annoying
| cfg stuff to make sure the intrinsics are actually emitted as
| you intend.
|
| There is nothing blocking high quality SIMD libraries on stable
| in Rust today. The bar for inclusion in std is just much higher
| than the rest of the ecosystem.
| the__alchemist wrote:
| Of interest, I've written my own core::simd mimic so I don't have
| to make all my libs and programs use nightly. It started as me
| just making my Quaternion and Vec lib (lin-alg) have their own
| SoA SIMD variants (Vec3x16 etc), but I ended up implementing and
| publicly exposing f32x16 etc. Will remove those once core::simd
| is stable. Downside: These are x86 only; no ARM support.
|
| I also added packing and unpacking helpers that assist with
| handling final lane 0 values etc. But there is still some subtly,
| as the article pointed out, compared to using Rayon or non-SIMD
| CPU code related to packing and unpacking. E.g. you should try to
| keep things in their SIMD form throughout the whole pipeline, how
| you pair them with non-SIMD values (Like you might pair [T; 8]
| with f32x8 etc) etc.
| ____tom____ wrote:
| I'm not a rust programmer.
|
| Can't you just make a local copy of the existing package and
| use that? Did you need to re-implement?
| the__alchemist wrote:
| Good question. Probably, but I don't know how and haven't
| tried.
| dzaima wrote:
| The nightly built-in core::simd makes use of a bunch of
| intrinsics to "implement" the SIMD ops (or, rather, directly
| delegate the implementation to LLVM which you otherwise
| cannot do from plain Rust), which are as much if not more
| volatile than core::simd itself (and also nightly-only).
| vlovich123 wrote:
| > or, rather, directly delegate the implementation to LLVM
| which you otherwise cannot do from plain Rust
|
| I thought the intrinsic specifically were available in
| plain safe rust and the alignment required intrinsics were
| allowed in unsafe rust. I'm not sure I understand this
| "direct to llvm dispatch" argument or how that isn't
| accessible to stable Rust today.
| dzaima wrote:
| You can indeed use intrinsics to make a SIMD library in
| plain safe stable rust today to some extent; that just
| isn't what core::simd does; rather, on the Rust-side it's
| all target-agnostic and LLVM (or whatever other backend)
| handles deciding how to lower any given op to the target
| architecture.
|
| e.g. all core::simd addition ends up invoking the single
| function [1] which is then directly handled by rustc. But
| these architecture-agnostic intrinsics are unstable[2]
| (as they're only there as a building block for
| core::simd), and you can't manually use
| "#[rustc_intrinsic]" & co in stable rust either.
|
| [1]: https://github.com/rust-
| lang/rust/blob/b01cc1cf01ed12adb2595...
|
| [2]: https://github.com/rust-
| lang/rust/blob/b01cc1cf01ed12adb2595...
| the__alchemist wrote:
| This is what I ended up doing as a stopgap.
| bencyoung wrote:
| Odd that c# has a better stable SIMD story than Rust! It has both
| generic vector types across a range of sizes and a good set of
| intrinsics across most of the common instruction sets
| kelnos wrote:
| Why would that be odd? C# is an older and mature language
| backed by a corporation, while Rust is younger and has been run
| by a small group of volunteers for years now.
| booi wrote:
| not just any corporation.. the largest software corporation
| on the planet
| Arch-TK wrote:
| not just any largest software corporation, one of my two
| least favourite largest software corporations on the
| planet.
| jiehong wrote:
| C# is blessed on that front. Java's SIMD state is still sad,
| and golang is not as great either.
| ashf023 wrote:
| Yeah, golang is a particular nightmare for SIMD. You have to
| write plan 9 assembly, look up what they renamed every
| instruction to, and then sometimes find that the compiler
| doesn't actually support that instruction, even though it's
| part of an ISA they broadly support. Go assembly functions
| are also not allowed to use the register-based calling
| convention, so all arguments are passed on the stack, and the
| compiler will never inline it. So without compiler support I
| don't believe there's any way to do something like intrinsics
| even. Fortunately compiler support for intrinsics seems to be
| on its way! https://github.com/golang/go/issues/73787
| exyi wrote:
| C# portable SIMD is very nice indeed, but it's also not usable
| without unsafety. On the other hand, Rust compiler (LLVM) has a
| fairly competent autovectorizer, so you may be able to simply
| write loops the right way instead of the fancy API.
| buybackoff wrote:
| Unsafety means different things. In C#, SIMD is possible via
| `ref`s, which maintains GC safety (no GC holes), but removes
| bounds safety (array length check). The API is called
| appropriately Vector.Load _Unsafe_
| IshKebab wrote:
| > Fortunately, this problem only exists on x86.
|
| Also RISC-V, where you can't even probe for extension support in
| user space unfortunately.
| raphlinus wrote:
| It's not strictly x86 either, the other case you care about is
| fp16 support on ARM. But it is included in the M1 target, so
| really only on other ARM.
| dzaima wrote:
| Linux of course does have an interface for RISC-V extension
| probing via hwprobe. And there's a C interface[1] for probing
| that's OS-agnostic (though it's rather new).
|
| [1]: https://github.com/riscv-non-isa/riscv-c-api-
| doc/blob/main/s...
| jtrueb wrote:
| simd was one I thought we needed. Then, i started benchmarking
| using iter with chunks and a nested if statement to check the
| chunk size. If it was necessary to do more, it was typically time
| to drop down to asm rather than worry about another layer in
| between the code and the machine.
| b33j0r wrote:
| This is the most surprising comment to me. It's that bad? I
| haven't benchmarked it myself.
|
| Zig has @Vector. This is a builtin, so it gets resolved at
| comptime. Is the problem with Rust here too much abstraction?
| oasisaimlessly wrote:
| I think you misinterpreted GP; he's saying that with some
| hints (explicit chunking with a branch on the chunk size),
| the compiler's auto-vectorization can handle the rest,
| inferring SIMD instructions in a manner that's 'good enough'.
| waffletower wrote:
| I am torn -- while I love the bitter critique of std::simd's
| nightly builds (why bother with any public release if it is never
| stable?), I cringed at the critique of "(c)urrently things are
| well fleshed out for i32, i64, f32, and f64 types". f64 and i64
| go a long way for most numerical applications -- the OP seemed
| snowflaky to me with that entitled concern.
| zamadatix wrote:
| Not supporting other types (particularly smaller ones) can be
| quite limiting on portable SIMD, especially when it doesn't
| support AVX512 either, but those are certainly a good core
| group - just not the whole story. Regardless, I'm not sure how
| painting the OP as an entitled snowflake helps anything over
| just asking the question.
| taeric wrote:
| I'm curious on the uptake of SIMD and other assembly level usage
| through high level code? I'd assume most is done either by people
| writing very low level code that directly manages the data, or by
| using very high level libraries that are prescriptive on what
| data they work with?
|
| How many people are writing somewhat bog standard RUST/C and
| expect optimal assembly to be created?
| zamadatix wrote:
| It's really only comparable to assembly level usage in the SIMD
| intrinsics style cases. Portable SIMD, like std::simd, is no
| more assembly level usage than calling math functions from the
| standard library.
|
| Usually one only bothers with the intrinsic level stuff for the
| use cases you're saying. E.g. video encoders/decoders needing
| hyper-optimized, per architecture loops for the heavy lifting
| where relying on the high level SIMD abstractions can leave
| cycles on the table over directly targeting specific
| architectures. If you're just processing a lot of data in bulk
| with no real time requirements, high level portable SIMD is
| usually more than good enough.
| taeric wrote:
| My understanding was that the difficulty with the intrinsics
| was more in how restrictive they are in what data they take
| in. That is, if you are trying to be very controlling of the
| SIMD instructions getting used, you have backed yourself into
| caring about the data that the CPU directly understands.
|
| To that end, even "calling math functions" is something that
| a surprising number of developers don't do. Certainly not
| with the standard high level data types that people often try
| to write their software into. No?
| justahuman74 wrote:
| Somewhat related, does rust handle the riscv vector extension in
| a similar way to simd?
| dzaima wrote:
| Scalable vectors as in RVV & SVE aren't available in rust
| corrently; see https://github.com/rust-lang/rust/issues/145052
|
| (that said autovectorization should work, and fixed-width SIMD
| should map to RVV as best as possible, though of course missing
| out on perf if ran on wider-than-minimum hardware not on a
| native build)
| andyferris wrote:
| Regarding autovectorization:
|
| > The other drawback of this method is that the optimizer won't
| even touch anything involving floats (f32 and f64 types). It's
| not permitted to change any observable outputs of the program,
| and reordering float operations may alter the result due to
| precision loss. (There is a way to tell the compiler not to worry
| about precision loss, but it's currently nightly-only).
|
| Ah - this makes a lot of sense. I've had zero trouble getting
| excellent performance out of Julia using autovectorization (from
| LLVM) so I was wondering why this was such a "thing" in Rust. I
| wonder if that nightly feature is a per-crate setting or what?
| Arch-TK wrote:
| It's not something you seem to be able to just enable globally.
| From what I gather this is what is being referenced:
|
| https://doc.rust-lang.org/std/intrinsics/index.html
|
| Specifically the *_fast intrinsics.
| bobmcnamara wrote:
| We used to tweak our scalar product simulator code to match the
| SIMD arithmetic order so we could hash the outputs for tests.
|
| I wonder if it could autovec the simd-ordered code.
| vlovich123 wrote:
| Does Julia ignore the problem of floating point not being
| associative, commutative nor distributive?
|
| The reason it's a thing is from LLVM and I'm not sure you can
| "language design" your way out of this problem as it seems
| intrinsic to IEEE 754.
| CashWasabi wrote:
| I really dislike those articles that are language focused. Why
| not try to share them in a way that is language agnostic?
___________________________________________________________________
(page generated 2025-11-05 23:00 UTC)