Hacker News

Top stories

Live mirror
30 storiesupdated just nowView source snapshot
  1. I built non-autoregressive decision models with RL a year ago(convaiinnovations.com ↗)
    173comments
  2. AI-generated posters don’t have to be horrible(john.hartnup.uk ↗)
    560comments
  3. A graphical desktop for the ZX Spectrum(github.com/mindbox77 ↗)
    84comments
  4. Human brain is two separate organs, Stanford Medicine-led research finds(stanford.edu ↗)
    199comments
  5. Btrfs/ZFS/bcachefs under workloads classic benchmarks skip(bartosz.fenski.pl ↗)
    1comments
  6. “The Secret Life of Circuits” is here(coredump.cx ↗)
    59comments
  7. Tin: full-text search for Postgres(planetscale.com ↗)
    57comments
  8. Android 17 is the first since 3.x to add new APIs without releasing to the AOSP(grapheneos.social ↗)
    597comments
  9. Show HN: CUA-S1 – A System One Model for Computer Use(github.com/trycua ↗)
    discuss
  10. Supabase (YC S20) Is Hiring for OrioleDB(supabase.link ↗)
    discuss
  11. Black Holes or Black Hole Stars? Astronomers Spar over 'Little Red Dots'(quantamagazine.org ↗)
    23comments
  12. New evidence for hidden chambers beyond Tutankhamun's tomb(nature.com ↗)
    13comments
  13. People who know the most often sound the least certain(vrash.substack.com ↗)
    9comments
  14. GPT-6 Astra Solves a WWI German Radio Cipher(prinzai.com ↗)
    146comments
  15. Almost Never Use AI to Write Anything Substantive(erichgrunewald.substack.com ↗)
    38comments
  16. San Francisco Onion Futures Company(onionfutures.com ↗)
    133comments
  17. Suzanne Ciani's Buchla Cookbook(echo.orpheusinstituut.be ↗)
    discuss
  18. Cloudflare Quick Tunnels(cloudflare.com ↗)
    302comments
  19. If math is more than proof, we need to better celebrate the rest of it(terrytao.wordpress.com ↗)
    207comments
  20. How to Write with an LLM(sockpuppet.org ↗)
    365comments
  21. What Zig felt like, coming from Rust(besok.github.io ↗)
    149comments
  22. You can run Git on object storage if you re-make packfiles(tigrisdata.com ↗)
    29comments
  23. Saving another 100TB of RAM(cloudflare.com ↗)
    93comments
  24. Communication by means of modulated Johnson noise(pnas.org ↗)
    23comments
  25. Asking Authors About Their Own Papers(medium.com/tmlrorg ↗)
    51comments
  26. SDCC – Small Device C Compiler(sourceforge.net ↗)
    26comments
  27. How OpenAI Used Its Own LLMs to Design Its Jalapeño Chip(ieee.org ↗)
    123comments
  28. Adventures in Microcontroller Circuit Debugging(bigmessowires.com ↗)
    1comments
  29. Science Is Open Software(jepedersen.dk ↗)
    57comments
  30. Ray Ozzie and the Optimism of Being Early(reproof.app ↗)
    20comments

Implementing a GPU's programming model on a CPU

280 pointsby 2y agolitherum.blogspot.com
39 comments
2y agoHN ↗

ISPC is great software. It's a shame in many ways Intel didn't put more resources behind it.

2y agoHN ↗

I naively thought ISPC turned into the new DPCPP compiler they’re leaning into with their oneAPI marketing. Are those really different lineages?

2y agoHN ↗

This so-called GPU programming model has existed many decades before the appearance of the first GPUs, but at that time the compilers were not so good like the CUDA compilers, so the burden for a programmer was greater.

As another poster has already mentioned, there exists a compiler for CPUs which has been inspired by CUDA and which has been available for many years: ISPC (Implicit SPMD Program Compiler), at https://github.com/ispc/ispc .

NVIDIA has the very annoying habit of using a lot of terms that are different from those that have been previously used in computer science for decades. The worst is that NVIDIA has not invented new words, but they have frequently reused words that have been widely used with other meanings.

SIMT (Single-Instruction Multiple Thread) is not the worst term coined by NVIDIA, but there was no need for yet another acronym. For instance they could have used SPMD (Single Program, Multiple Data Stream), which dates from 1988, two decades before CUDA.

Moreover, SIMT is the same thing that was called "array of processes" by C.A.R. Hoare in August 1978 (in "Communicating Sequential Processes"), or "replicated parallel" by Occam in 1985 or "PARALLEL DO" by "OpenMP Fortran" in 1997-10 or "parallel for" by "OpenMP C and C++" in 1998-10.

Each so-called CUDA kernel is just the body of a "parallel for" (which is multi-dimensional, like in Fortran).

The only (but extremely important) innovation brought by CUDA is that the compiler is smart enough so that the programmer does not need to know the structure of the processor, i.e. how many cores it has and how many SIMD lanes each core has. The CUDA compiler distributes automatically the work over the available SIMD lanes and available cores and in most cases the programmer does not care whether two executions of the function that must be executed for each data item are done on two different cores or on two different SIMD lanes of the same core.

This distribution of the work over SIMD lanes and over cores is simple when the SIMD operations are maskable, like in GPUs or in AVX-512 a.k.a. AVX10 or in ARM SVE. When masking is not available, like in AVX2 or Armv8-A, the implementation of conditional statements and expressions is more complicated.

2y agoHN ↗

DirectX 8 added programmable shaders as we mostly know it today in 2000 with directx 9's shader model 2.0 really solidifying things in 2002.

CUDA didn't show up until 2007.

2y agoHN ↗

SIMT and SIMD are different things. It's fortunate that they have different names.

A GPU is a single instruction multiple data machine. That's what the predicated vector operations are. 32 floats at a time, each with a disable bit.

Cuda is a single instruction multiple thread language. You write code in terms of one float and branching on booleans, as if it was a CPU, with some awkward intrinsics for accessing the vector units in the GPU.

That is, the programming model of a GPU ISA and that of Cuda are not the same. The GPU gives you vector instructions. Cuda gives you (mostly) scalar instructions and a compiler that deals with this mismatch, lowering branches to changes in exec mask and so forth.

With my numerical library hat on, I hate this. Programming a simd machine through a simt language means trying to get the compiler to transform the control flow into the thing you could easily write using vector instructions.

With my compiler implementer hat on, I hate this. It gives you two control flow graphs intertwined and a really bad time in register allocation.

It's not totally clear to me why simt won out over writing the vector operations. I'm certainly in the minority opinion here.

2y agoHN ↗

I guess that a lot of people are uncomfortable thinking about vector instructions, and dealing with masks manually? And for vector instructions you need to align things properly, pad the arrays such that they are of the right size, that people are not used to I guess.

2y agoHN ↗

I agree. I work on numerical simulation software that involves very sparse, very irregular matrices. We hardly use SIMD because of the challenges of maintaining predicates, bitmasks, mapping values into registers, and so on. It's not bad if we can work on dense blocks, but dealing with sparsity is a huge headache. Now that we are implementing these methods with CUDA using the SIMT paradigm, that complexity is largely taken care of. We still need to consider how to design algorithms to have parallelism, but we don't have to manage all the minutiae of mapping things into and out of registers.

Modern GPGPUs also have more hardware dedicated to this beyond the SIMD/SIMT models. In NVIDIAs CUDA programming model, besides the group of threads that represents a vector operation (a warp), you also have groups of warps (thread blocks) that are assigned the same processor and can explicitly address a fast, shared memory. Each processor has many registers that are automatically mapped to each thread so that each thread has its own dedicated registers. Scheduling is done in hardware at an instruction level so that you can effectively single cycle context switches between warps. Starting with Volta, it will even assemble vectors from threads in any warps in the same thread block, so lanes that are predicated off in a warp don't have to go to waste - they can take lanes from other warps.

There are many other hardware additions that make this programming model very efficient. Similar to how C and x86 each provide abstractions over the actual micro ops being executed that hides complexity like pipelining, out of order execution, and speculative execution, CUDA and the PTX ISA provide abstractions over complex hardware implementations that specifically benefit this kind of SIMT paradigm.

2y agoHN ↗

I think it boils down to which GPGPU camp you are in, AI or HPC.

AI has relatively simple workflow, less thread divergence, so the SIMT abstractions add very little value. HPC workflow on the other hand is lot more complex. Writing a good simulation program for example, is going to get inhumanly complex with just SIMD.

2y agoHN ↗

It's not totally clear to me why simt won out over writing the vector operations.

From the user side, it is probably simpler to write an algorithm once without vectors, and have a compiler translate it to every vector ISA it supports, rather than to deal with each ISA by hand.

Besides, in many situations, having the algorithm executed sequentially or in parallel is irrelevant to the algorithm itself, so why introduce that concern?

I'm certainly in the minority opinion here.

There are definitely more userland programmers than compiler/numerical library ones.

2y agoHN ↗

I think it's easier to write the kernel in terms of the scalar types if you can avoid the warp level intrinsics. If you need to mix that with the cross lane operations it gets very confusing. In the general case volta requires you to pass the current lane mask into intrinsics, so you get to try to calculate what the compiler will turn your branches into as a bitmap.

So if you need/want to reason partly in terms of warps, I think the complexity is lower to reason wholly in terms of warps. You have to use vector types and that's not wonderful, but in exchange you get predictable control flow out of the machine code.

Argument is a bit moot though, since right now you can't program either vendor hardware using vectors, so you also need to jump the barrier to assembly. None of the GPUs are very easy to program in assembly.

2y agoHN ↗

SIMT and SIMD are different things.

was anything said against that?

the comment said SIMT is same as SPMD

2y agoHN ↗

Subtle difference. A parallel_for can have asynchronous threads. They can all divergen and run independent instructions (due to if statements etc)

SIMT means multiple processors all executing the exact same instruction. One instruction decoder, but like 64 execution pipelines.

2y agoHN ↗

The same is true for CUDA/OpenCL kernels. They can include conditionals and they can execute independent instructions.

The difference is that because the software "kernels" (i.e. software threads) may be mapped by the compiler either on hardware threads or on hardware SIMD lanes, and this mapping is not controlled by the programmer, the divergent instructions will cause inefficient (serial) execution when they happen to be executed on SIMD lanes of the same core, so they must be avoided.

This however is only an optimization problem, if the speed would be irrelevant all the kernels could execute divergent instructions.

The reason for the existence of CUDA is to mask for the programmer the existence of the SIMD lanes and to allow programming as if the software threads would map only to hardware threads. Nevertheless, for optimum performance the programmer must be aware that this abstraction is not really true, so the programs should be written with awareness of the limitations introduced by SIMD.

2y agoHN ↗

Nevertheless, for optimum performance

On conventional SIMT implementations (pre-Volta), the programmer also has to be aware of it to not cause deadlocks in the atomics across different lanes in the same warp.

On NV Volta onwards, each SIMT lane has its own instruction pointer with opportunistic reconvergence when possible.

2y agoHN ↗

I don't think so. I was quite interested in getting emulated GPUs to work for CI workers to be able to test CUDA stuff without needing actual GPU passthrough, but all I could find in this space were abandoned, early stage hobby projects on GitHub.

2y agoHN ↗

No, QEMU only supports up to AVX2. Based on the time it took to implement AVX and AVX2, I expect it would be about a month of full time work to implement AVX512F, AVX512BW and AVX512VL (it would not be much simpler to implement only one of them).

2y agoHN ↗

You mean llvmpipe. You can run OpenCL programs with it.

2y agoHN ↗

In addition to ISPC, some of this is also done in software fallback implementations of GPU APIs. In the open source world we have SwiftShader and Lavapipe, and on Windows we have WARP[1].

It's sad to me that Larrabee didn't catch on, as that might have been a path to a good parallel computer, one that has efficient parallel throughput like a GPU, but also agility more like a CPU, so you don't need to batch things into huge dispatches and wait RPC-like latencies for them to complete. Apparently the main thing that sunk it was power consumption.

[1]: https://learn.microsoft.com/en-us/windows/win32/direct3darti...

2y agoHN ↗

The recent "AI chip" proposals (Tenstorrent, Esperanto Technologies etc.) seem to be quite similar to the old Larrabee design, except based on RISC-V as opposed to x86. So we might see that happen after all.

2y agoHN ↗

Yes, I've got my eye on those and am hopeful. Do you know of any meaty technical description of the programming model? All I've been able to find so far is fairly high level marketing material. At least for Tenstorrent Jim Keller has promised that the software stack will be open sourced, something I'm looking forward to.

2y agoHN ↗

I helped make a really cursed RISC-V version of this for a class project last year! The idea was to first compile each program to WASM using clang, and lower the WASM back to C but this time with all opcodes implemented in terms of the RISC-V vector intrinsics. That was a hack to be sure, but a surprisingly elegant one since 1. WASM's structured control flow maps really well to lane masking 2. Stack and local values easily use "structure of arrays" layout 3. Heap values easily use "array of structures" layout

It never went anywhere but the code is still online if anyone wants to stare directly at the madness: https://gitlab.com/samsartor/wasm2simt

2y agoHN ↗

This is cool! If you wrote a blog post going detail about the insights, I'd read it

2y agoHN ↗

The linked github page has a link to a paper.

2y agoHN ↗

I don't know that I'd ever use this in production but this is really really cool. Nice work.

2y agoHN ↗

This is in contrast to SIMD, or "single instruction multiple data," where the programmer explicitly uses vector types and operations in their program. The SIMD approach is suited for when you have a single program that has to process a lot of data, whereas SIMT is suited for when you have many programs and each one operates on its own data

This statement is comparing the SIMT model to SIMD. Can anyone explain the last part about SIMT being better for many programs operating on its own data? Are they just saying you can have individual “threads” executing independently (via predication/masks and such)?

2y agoHN ↗

SIMT let's a scheduler get clever about memory accesses, SIMD can practically only access memory linearly (scatter gather can do better but it's still usually quite linear) whereas SIMT can be much smarter in terms of having lots of similar bits of work going on in ways that use the bandwidth maximally and don't overlap.

2y agoHN ↗

Yes, but not all thread in the block need to. As long as you fill a cache line you’re good.

2y agoHN ↗

Which is to be expected, given that AVX is what survived from Larrabe.