Live data from Hacker News

Rust SIMD on the GPU

vectorware.com

81–90 of 130 posts

Re: Rust SIMD on the GPU

#81

I've noticed a lot of articles about SIMD on the HN front page. That's cool, but just wondering, is there some reason this is more in focus lately?

SIMD is actually underrated still. Programmers should always be thinking about it. It's a free 4x in a lot of cases

Re: Rust SIMD on the GPU

#82
post #66
post #64

Earlier quoted context omitted.

It'd need some kind of compile-time hardware-feature-detection, yea? That seems probably feasible since proc macros can do essentially anything they like (worryingly).

Only if the end-user is the one compiling the software, on the same very system they'll be running it on. Which is true of GPU shader kernels, due to how GPU drivers work; but isn't generally true of CPU object code (unless you're on Gentoo.) What you'd actually want is a matrix of variant implementations burned into the binary, with runtime (or process-boot-time) hardware detection that swaps symbols out to point to…

If you want the library to perform that selection, you also need the "correct" / most efficient implementation to be independent of your workload. I'm not that familiar with SIMD performance characteristics, but I wouldn't be surprised if that's not always the case.

Re: Rust SIMD on the GPU

#83

I've noticed a lot of articles about SIMD on the HN front page. That's cool, but just wondering, is there some reason this is more in focus lately?

SIMD is actually underrated still. Programmers should always be thinking about it. It's a free 4x in a lot of cases

I did see the article about that too. Don't know about "always" since there are applications like web backends where you're never going to add arrays of floats or something. Even if it's data science stuff, if that's in Python, Numpy is doing the SIMD for you.

Re: Rust SIMD on the GPU

#84
post #71

Earlier quoted context omitted.

64 what? Bits/bytes/something bigger?

When you dig through the CUDA developer docs instead of the promotional materials, you can develop a view of Nvidia GPUs as having 8-128 processing cores, each with 4 hyperthreads, running 32-lane SIMD for almost everything. Where a lane is 32 bits wide. The promotional material likes to label the individual lanes as “cores” because it sounds more impressive. And, it’s not entirely incorrect. Even the dev docs use th…

This is a fantastic explanation, thanks for writing it. It also makes me wonder something: where exactly is the biggest difference between a 32-core x86 CPU (AVX512 basically being 16 32-bit lanes) and (say) an NVIDIA GPU with ~8-16 processing cores? Like why can't the CPU compete against a GPU like that for GPU-y tasks - or can it?

Re: Rust SIMD on the GPU

#85
post #15

My heard hurts - i was stupid enough to think that SIMD was a CPU only thing - I don't understand why it would be ported to GPU - huge kudos to managing to surprise me

It's not really obvious unless you go in depth of the details on modern GPU architecture. GPUs aren't really SIMD, they're SIMT (single instruction multiple thread). The silicon looks a lot like SIMD, but the programming model is different. If you go look at AMD's ISA docs (they're public) you'll see you don't have the equivalent of a __mm256 register like on x86. Each 'thread' just deals with single scalar values li…

> GPUs aren't really SIMD, they're SIMT (single instruction multiple thread)

False. If they were threads they'd have their own PC. They do not - only the warp has a PC.

> You just write plain scalar code and the hardware model dispatches it efficiently to SIMD execution units.

Absolutely not. If you don't write coalesced loads, bank-conflict free, predication-free, cooperative code you will get worse than CPU performance.

Re: Rust SIMD on the GPU

#86

Earlier quoted context omitted.

When you dig through the CUDA developer docs instead of the promotional materials, you can develop a view of Nvidia GPUs as having 8-128 processing cores, each with 4 hyperthreads, running 32-lane SIMD for almost everything. Where a lane is 32 bits wide. The promotional material likes to label the individual lanes as “cores” because it sounds more impressive. And, it’s not entirely incorrect. Even the dev docs use th…

This is a fantastic explanation, thanks for writing it. It also makes me wonder something: where exactly is the biggest difference between a 32-core x86 CPU (AVX512 basically being 16 32-bit lanes) and (say) an NVIDIA GPU with ~8-16 processing cores? Like why can't the CPU compete against a GPU like that for GPU-y tasks - or can it?

Not corysama, but I'll take a stab:

The major one is that there's one layer of indirection that exists on GPUs that doesn't really on CPUs. There's one giant vector register file per for each of these processing cores (that'll be something like 2048 rows x 32 lanes x 32 bits). An individual shader invocation might only need say, 16 rows. While there's hardware for issuing 4 hyperthreads at any given time, there can be a variable number of thread states in the register file. So for the case of each invocation only needing 16 rows, you might be able to fit 128 hyperthread states into processing core. Those four hyperthreads then hardware schedule those 128 states and will execute any that are ready, as well as allocate more from other scheduling hardware as gaps in the register file appear from shaders completing.

Because of this massive amount thread state, you don't depend nearly as much on a cache hierarchy to deal with DRAM latency. There's ostensibly some other thread state sitting around that can be serviced while others wait for the hundreds of cycles of latency to access DRAM.

So the whole model of how you account for the discrepancy between ALU cycle time, and DRAM latency changes versus a CPU. Where a modern CPU spends a lot of area on complex cache hierarchies, speculation, etc, to hide the latency to memory, a GPU focuses on having a lot of thread state around and a lot of ALUs, but balanced ideally, so there's always ALU work to do while other thread states are waiting on memory.

Now, over time, GPUs have gotten more complex hardware, and more complex cache hierarchies to cover the cases that aren't handled well by extremely long access times. But those tend to be very explicit. Additionally, CPU vector files have gotten more similar to GPU cores as architectural features like lane masking/predicates have been added to have the equivalent of CUDA threads in the same warp that take different paths through control flow blocks. That's a lot of what people mean when they say that AVX-512 adds a lot more than just 512-bit registers. The K mask registers let you do a lot of GPU shader tricks to have effective partial residency, and not have to use all the lanes if the data doesn't line up with that.

Re: Rust SIMD on the GPU

#87

Earlier quoted context omitted.

It's not really obvious unless you go in depth of the details on modern GPU architecture. GPUs aren't really SIMD, they're SIMT (single instruction multiple thread). The silicon looks a lot like SIMD, but the programming model is different. If you go look at AMD's ISA docs (they're public) you'll see you don't have the equivalent of a __mm256 register like on x86. Each 'thread' just deals with single scalar values li…

> GPUs aren't really SIMD, they're SIMT (single instruction multiple thread) False. If they were threads they'd have their own PC. They do not - only the warp has a PC. > You just write plain scalar code and the hardware model dispatches it efficiently to SIMD execution units. Absolutely not. If you don't write coalesced loads, bank-conflict free, predication-free, cooperative code you will get worse than CPU perform…

Yes, of course writing naive code assuming each lane in a thread group is a real thread is going to cause problems, but I didn't feel like I needed to go into that level of detail replying to someone just learning about GPU internals. I tried to cover this loosely by mentioning how you need to know how it works for maximum performance.

If you want to get more pedantic you also need to look at your target hardware and their specific micro-architectural quirks and features to get the best performance. AMD specifically benefits a lot from exploiting the scalar unit over the vector unit, you save loads of register file space if you can keep data in SGPRs over VGPRs. There's lots of traps you can fall into where you can load data from buffers into SGPRs but they get promoted to VGPRs because the scalar unit lacks an opcode for like one math operation you did to the value somewhere.

While each lane isn't truly a thread because it doesn't have its own PC the programming model definitely tries to make it seem that way. The threads can terminate at different points too. And again, the ISA isn't a vector ISA. Your register values are scalar.

Re: Rust SIMD on the GPU

#88

Earlier quoted context omitted.

When you dig through the CUDA developer docs instead of the promotional materials, you can develop a view of Nvidia GPUs as having 8-128 processing cores, each with 4 hyperthreads, running 32-lane SIMD for almost everything. Where a lane is 32 bits wide. The promotional material likes to label the individual lanes as “cores” because it sounds more impressive. And, it’s not entirely incorrect. Even the dev docs use th…

This is a fantastic explanation, thanks for writing it. It also makes me wonder something: where exactly is the biggest difference between a 32-core x86 CPU (AVX512 basically being 16 32-bit lanes) and (say) an NVIDIA GPU with ~8-16 processing cores? Like why can't the CPU compete against a GPU like that for GPU-y tasks - or can it?

At risk of over-simplifying, GPUs are wider with limited computational expressiveness and higher memory bandwidth while CPUs are highly expressive computationally (and better connected to I/O) but with lower memory bandwidth. GPUs are less sensitive to memory latency by necessity. Even AVX-512 is highly flexible when inter-mixed with scalar code. GPUs get their very high register width by restricting what the cores are capable of doing efficiently.

Current CPU cores do two AVX-512 operations per cycle. If you can saturate this you’ll often run out of memory bandwidth on CPUs because of lower bandwidth compared to GPUs. In principle, if you bought a 192-core processor you’d have 6,144 GPU-ish cores of 32-bit operations, and they would run at a significantly higher clock rate than a GPU. It would not be competitive with a GPU for the kinds of things GPUs are good at it but it wouldn’t be as far off as you might assume. For some types of code, AVX-512 is unambiguously better.

Horses for courses. GPUs and CPUs were optimized for different things but their capabilities have slowly been converging over time. They all work from the same transistor budgets, the differences are where the tradeoffs are made.

There is a pithy silicon architecture tradeoff trilemma to be made regarding CPUs, GPUs, and barrel processors.

Re: Rust SIMD on the GPU

#89

Earlier quoted context omitted.

When you dig through the CUDA developer docs instead of the promotional materials, you can develop a view of Nvidia GPUs as having 8-128 processing cores, each with 4 hyperthreads, running 32-lane SIMD for almost everything. Where a lane is 32 bits wide. The promotional material likes to label the individual lanes as “cores” because it sounds more impressive. And, it’s not entirely incorrect. Even the dev docs use th…

This is a fantastic explanation, thanks for writing it. It also makes me wonder something: where exactly is the biggest difference between a 32-core x86 CPU (AVX512 basically being 16 32-bit lanes) and (say) an NVIDIA GPU with ~8-16 processing cores? Like why can't the CPU compete against a GPU like that for GPU-y tasks - or can it?

If by processing cores you mean Streaming Multiprocessors (SMs), keep in mind that each SM is 128 threads, so 16 SMs is 2k threads - or 2048 single precision math ops per cycle. The 32 core avx512-enabled CPU is 512 math ops per cycle, if you have one FMA unit per core (or 1k ops/cycle if you have 2 FMA units/core).

Note that modern NVIDA GPUs like the 5090 are actually more like 170 SMs on a chip, or 21,760 flop/cycle, or ~20-40x more ops/cycle than your example CPU.

Put GDDR6 vs DDR5 memory on top of that, and it’s easy to see why the GPU can churn through math so fast … as long as it’s GPU-y. For stuff the GPU does well, the CPU typically can’t compete, the GPU is often more than 10x faster. But GPU-y tasks are a subset, and there are CPU-y things the GPU can’t compete on, despite (or even because of) the thread count discrepancy.

Re: Rust SIMD on the GPU

#90

Earlier quoted context omitted.

Pretty common for Rust to cook things in nightly for a very long time; I wouldn't consider it a bad thing, tbh.

It's been annoying to me as an end user that so many basic things require nightly. I use nightly as my main toolchain, but enabling unstable features makes a project nightly-only, which is undesired for crates that don't already revolve around the unstable feature. I most often encounter unstable features when I reach for a basic common-sense utility method and discover that it's not stable. Like just earlier today I…

[dead]
Post reply on HN