Live data from Hacker News

Using SIMD for Parallel Processing in Rust

nrempel.com

31–40 of 43 posts

Re: Using SIMD for Parallel Processing in Rust

#31
post #22

Zig actually has a very nice abstraction for SIMD in the form of vector programming. The size of the vector is agnostic to the underlying cpu architecture. The compiler or LLVM will generate code for using SIMD128, 256, or 512 registers. And you are just programming straight vectors.

I do generally like their approach. It's especially well suited given how easy comptime allows metaprogramming against the target register size.

I wish it had a few more builtins for commonly supported operations without me having to write inline assembly (e.g., runtime LUTs are basically untenable for implementing something like bolt [0] without inline asm), but otherwise the abstraction level is about where I'd like it to be. I usually prefer it to gcc intrinsics, fully inline asm, and other such shenanigans.

[0] https://arxiv.org/abs/1706.10283

Re: Using SIMD for Parallel Processing in Rust

#32
post #22

Zig actually has a very nice abstraction for SIMD in the form of vector programming. The size of the vector is agnostic to the underlying cpu architecture. The compiler or LLVM will generate code for using SIMD128, 256, or 512 registers. And you are just programming straight vectors.

Rust has that too, with nalgebra if you want arbitrary-sized tensors as scientific computing wants, or with glam and similar crates if your needs are more modest as in graphics. In all cases they're SIMD-accelerated.

Re: Using SIMD for Parallel Processing in Rust

#33

The interesting question for me is whether Rust makes it easier for the compiler to extract SIMD parallelism automatically given the restrictions imposed by its type system.

The main thing I can think of that would help here is the fact that Rust has stricter alignment requirements than C++ does. Any live reference can more or less be assumed to point to validly-aligned memory at all times, which isn't true in C++.

As to whether LLVM actually takes advantage of this effectively, I don't know. I know that we do supply the necessary attributes to LLVM in most cases, but I haven't looked at the individual transform and optimization passes to see whether they take advantage of this (e.g. emitting movdqa vs. falling back to movdqu).

Re: Using SIMD for Parallel Processing in Rust

#34
post #22

Zig actually has a very nice abstraction for SIMD in the form of vector programming. The size of the vector is agnostic to the underlying cpu architecture. The compiler or LLVM will generate code for using SIMD128, 256, or 512 registers. And you are just programming straight vectors.

But zig lacks the intrinsics support, and not ever single simd spec is exposed on the abstraction.

Re: Using SIMD for Parallel Processing in Rust

#35

If you like SIMD and would like to dabble in it, I can strongly recommend trying it out in C# via its platform-agnostic SIMD abstraction. It is very accessible especially if you already know a little bit of C or C++, and compiles to very competent codegen for AdvSimd, SSE2/4.2/AVX1/2/AVX512, WASM's Packed SIMD and, in .NET 9, SVE1/2: https://github.com/dotnet/runtime/blob/main/docs/coding-guid... Here's an example of…

I implemented a sorting network in C# with AVX2 intrinsics. https://github.com/zvrba/SortingNetworks

Re: Using SIMD for Parallel Processing in Rust

#37
post #9
post #2

This is cool that simd primitives exist in the std lib of rust. I've wanted wanted to mess around a bit more with simd in python but I don't think that native support exists. Or your have to go down to C/C++ bindings to actually mess around with it (last I checked at least, please correct me if I'm wrong).

I feel like most languages could use simd in the standard library. We have all this power in the vector units of our CPUs that compilers struggle to use but yet we also don't make it easy to do manually

C# is the language that is doing this exact thing, with the next two close options being Swift and, from my understanding, Mojo.

Without easy to use SIMD abstraction, many* of .NET's CoreLib functions would have been significantly slower.

* UTF-8 validation, text encoding/decoding, conversion to/from hex bytes, copying data, zeroing, various checksum and hash functions, text/element counting, searching, advanced text search with multiple algorithms under SearchValues type used by Regex engine, etc.

Re: Using SIMD for Parallel Processing in Rust

#38
post #35

If you like SIMD and would like to dabble in it, I can strongly recommend trying it out in C# via its platform-agnostic SIMD abstraction. It is very accessible especially if you already know a little bit of C or C++, and compiles to very competent codegen for AdvSimd, SSE2/4.2/AVX1/2/AVX512, WASM's Packed SIMD and, in .NET 9, SVE1/2: https://github.com/dotnet/runtime/blob/main/docs/coding-guid... Here's an example of…

I implemented a sorting network in C# with AVX2 intrinsics. https://github.com/zvrba/SortingNetworks

It's a nice piece of work! If you're interested, .NET's compiler has improved significantly since 3.1, in particular, around structs and pre-existing intrinsics (which are no longer needed to be used directly in most situations - pretty much all code prefers to use plain methods on VectorXXX whenever possible). Also note the use of AggressiveOptimization attribute which disables tiered compilation and forces the static initialization checks your readme refers to - removing AO allows the compiler to bake statics directly into codegen through tiered compilation as upon reaching Tier 1 the value of such readonly statics will be known. For trivially constructed values, it is better to not store such in fields but rather construct them in place via e.g. expression-bodied properties like 'Vector128 MASK => Vector128.Create((byte)0x80)`. I don't remember exactly whether this was introduced in Core 3.1 or 5, but today the use of `AggressiveOptimization` flag is discouraged unless you do need to bypass DynamicPGO.

You also noted the lack of ability to express numeric properties of T within generic context. This was indeed true, and this limitation was eventually addressed by generic math feature. There are INumber, IBinaryInteger and others to constrain the T on, which bring the comparison operators you were looking for.

In general, the knowledge around vectorized code has substantially improved within the community, and it is used quite more liberally nowadays by those who are aware of it.

Re: Using SIMD for Parallel Processing in Rust

#39
post #9

Earlier quoted context omitted.

I feel like most languages could use simd in the standard library. We have all this power in the vector units of our CPUs that compilers struggle to use but yet we also don't make it easy to do manually

C# is the language that is doing this exact thing, with the next two close options being Swift and, from my understanding, Mojo. Without easy to use SIMD abstraction, many* of .NET's CoreLib functions would have been significantly slower. * UTF-8 validation, text encoding/decoding, conversion to/from hex bytes, copying data, zeroing, various checksum and hash functions, text/element counting, searching, advanced text…

D as well.

Re: Using SIMD for Parallel Processing in Rust

#40

Minor nit: RISC-V Vector isn't SIMD. It's actually like ARM's Scalable Vector Extension. Unlike traditional SIMD the code is agnostic to the register width and different hardware can run the same code with different widths. There is also a traditional SIMD extension (P I think?) but it isn't finished. Most focus has been on the vector extension. I am wondering how and if Rust will support these vector processing exte…

> RISC-V Vector isn't SIMD Isn't SIMD a subset of vector processors? To that matter, can anybody here provide a proper and useful distinction between the two, that is SIMD and vector ISAs? You imply it's because it's vector length agnostic, but you could take e.g. the SSE encoding, and apart from a few instructions, make it operate on SIMD registers of any length. Wouldn't that also be vector length agnostic, as long…

I agree that SIMD and vector are basically interchangeable at a certain level.

There is still a difference in the binutils, because SSE4 and AVX2 and AVX-512 have different instruction encodings per length.

But yes, it is possible to write VL-agnostic code for both SIMD and vector, and indeed the same user code written with Highway works on both SIMD and RISC-V.

Post reply on HN