Skip to content
HN On Hacker News ↗

Rust SIMD on the GPU

▲ 222 points 115 comments by sagacity 2w ago HN discussion ↗

Pangram verdict · v3.3

We believe that this text is a mix of AI and human-written content.

62 %

AI likelihood · overall

Mixed
30% human-written 70% AI-generated
SEGMENTS · HUMAN 4 of 11
SEGMENTS · AI 5 of 11
WORD COUNT 810
PEAK AI % 97% · §8
Analyzed
Aug 11
backend: pangram/v3.3
Segments scanned
11 windows
avg 74 words each
Distribution
30 / 70%
human / AI fraction
Verdict
Mixed
Pangram v3.3

Article text · 810 words · 11 segments analyzed

Human AI-generated
§1 Human · 4%

VectorWareDispatchesAugust 10, 202612 min readPedantic mode:OffGPU code can now use Rust's portable SIMD. We share the implementation approach and what this unlocks for GPU programming.At VectorWare, we are building the first GPU-native software company. Today, we are excited to announce that we can successfully use Rust's portable SIMD (core::simd) on the GPU. This milestone marks a significant step towards our vision of enabling developers to write complex, high-performance applications that leverage the full power of GPU hardware using familiar Rust abstractions. Parallelism below the thread When we brought Rust threads to the GPU, we mapped each std::thread to a GPU warp. This let us run many concurrent threads on the GPU but did not use the parallel lanes within each thread/warp. On the CPU, the abstraction for parallelism within a thread is SIMD.

§2 AI · 75%

A single instruction operates on several data elements packed into a vector unit: where scalar code adds two numbers, a SIMD add takes two vectors of, say, eight f32 values and produces eight sums at once. This data parallelism is inside a single thread, below the level where the operating system schedules anything. CPU threadSIMD op012N⋯SIMD lanesCPU thread Rust's portable SIMD Historically, writing SIMD in Rust meant reaching for the architecture-specific vendor intrinsics in core::arch, such as _mm256_add_ps on x86-64 or vaddq_f32 on Arm. These intrinsics are specific to a single instruction set, so a program that runs on more than one architecture needs a separate implementation for each. Rust's portable SIMD instead adds a layer of abstraction above these intrinsics. It provides a single generic type Simd<T, N> that represents a vector of N elements of type T. A program writes its arithmetic, comparisons, reductions, and lane shuffles once against Simd and the compiler lowers them to whatever vector instructions the target CPU has.

§3 Human · 13%

At VectorWare, we realized the GPU is just one more piece of vector hardware for portable SIMD to target. As a bonus, portable SIMD lives in core rather than std and it does not even need the std support we brought to the GPU. SIMT is SIMD GPUs execute in a model NVIDIA calls SIMT, or Single Instruction, Multiple Thread.

§4 AI · 80%

A warp issues one instruction, and each of its 32 lanes runs that instruction on its own data. One instruction operating on many data elements is exactly what SIMD means, and the per-lane addressing that SIMT adds does not change that. A warp is a wide vector unit and a portable SIMD vector maps onto that unit directly. CPU thread012N⋯SIMD lanes≈GPU warp012N⋯warp lanes For example, a Simd<i16, 32> gives one i16 element to each of the warp's 32 lanes, and adding two such vectors compiles to a single warp instruction in which every lane adds its element at once.

§5 Human · 11%

CPUlet a: Simd<i16, 32> = [1, 1, 1, ..., 1];let b: Simd<i16, 32> = [2, 2, 2, ..., 2];let c = a + b;compiles tovpaddw %zmm2, %zmm1, %zmm0a0+b0lane 0a1+b1lane 1a2+b2lane 2a31+b31lane 31⋯println!("{c:?}");GPUlet a: Simd<i16, 32> = [1, 1, 1, ..., 1];let b: Simd<i16, 32> = [2, 2, 2, ..., 2];let c = a + b;compiles toadd.s16 %rs3, %rs1, %rs2;a0+b0lane 0a1+b1lane 1a2+b2lane 2a31+b31lane 31⋯println!("{c:?}"); This new mapping completes the parallelism hierarchy from our earlier work.

§6 AI · 93%

On the CPU, a thread contains SIMD lanes, and on the GPU our std::thread is a warp whose hardware lanes play the same role. In both cases, core::simd drives those lanes.

§7 Human · 23%

CPU⋯thread 0012N⋯thread 1012N⋯thread N012N⋯SIMD lanes≈GPU⋯warp 0012N⋯warp 1012N⋯warp N012N⋯warp lanes A world first: core::simd on the GPU As with our earlier posts, this is hard to show visually because the code is ordinary Rust.

§8 AI · 97%

The same core::simd types that lower to x86-64 SIMD on a laptop lower to warp operations on the GPU, with no change to the source. Here we define a small portable SIMD routine and call it from main. It exercises the core features of the model: elementwise arithmetic, a comparison that produces a lane mask, a select driven by that mask, and a horizontal reduction across lanes.

§9 Mixed · 47%

#![feature(portable_simd)] use core::simd::cmp::SimdPartialOrd; use core::simd::num::SimdFloat; use core::simd::{Select, Simd}; // Portable SIMD. This exact function also compiles and runs on the CPU, // where it lowers to x86-64, Arm, or scalar code depending on the target. fn relu_dot(a: Simd<f32, 32>, b: Simd<f32, 32>) -> f32 { // Elementwise multiply: 32 products computed at once.

§10 AI · 84%

let products = a * b; // Per-lane comparison produces a mask, one boolean per lane. let positive = products.simd_gt(Simd::splat(0.0)); // Keep the positive products, replace the rest with zero. let clamped = positive.select(products, Simd::splat(0.0)); // Horizontal add across all lanes down to a single scalar. clamped.reduce_sum() } fn main() { // Two 32-wide vectors, built with ordinary Rust.

§11 Mixed · 62%

let a = Simd::<f32, 32>::splat(2.0); let b = Simd::<f32, 32>::from_array(std::array::from_fn(|i| i as f32 - 16.0)); // Elementwise ops, a comparison mask, a select, and a reduction: // all ordinary portable SIMD, all running on the GPU.