Arkavo Hait · 2020–2021
poissonCUDA
A single-GPU NVIDIA CUDA solver for the Poisson equation on a 3D grid — built to measure what parallelising the derivative actually buys you.
- GitHub arkavo/poissonCUDA
- Language CUDA C
- License Apache-2.0
- Status Archived
What it does
The Poisson equation turns up wherever a potential is set by a source distribution — electrostatics, gravitation, steady-state diffusion. Solving it means evaluating partial derivatives across a grid, repeatedly, until the field stops changing. That inner loop is where the time goes, so that is where this project points.
The 3D problem is mapped onto a 2D mesh for parallelisation, since a consumer GPU has a limited core count. Boundary conditions are held on the walls, which means only the interior (X−2)(Y−2)(Z−2) needs computing — the index is defined over that reduced volume and mapped onto the block/thread grid, so blocks × threads ≥ (X−2)(Y−2)(Z−2).
The test case uses a static boundary, but nothing in the solver depends on that choice; any boundary condition works.
How it converges
Interior values start at zero and relax towards the boundary-imposed solution. After each sweep the largest change across the volume is compared against a tolerance; the loop exits once every point moves less than that threshold. The convergence test itself runs on the device, so the field never has to come back to the host mid-solve.
Kernels
Each stage of the sweep is its own kernel rather than one monolithic pass:
| Kernel | Role |
|---|---|
DDX DDY DDZ | Second partial derivative along each axis. |
ADD | Applies the update step to the field. |
ASSIGN | Copies the next state over the current one. |
COMPARE | Reduces the per-point change to a maximum error, on device. |
RESET_CTR | Clears the error accumulator between sweeps. |
Build & run
The source is a single translation unit under src/. Compile with nvcc:
nvcc src/poissonCUDA.cu -o pCUDA
Then invoke with a thread count and optional grid dimensions:
./pCUDA <Threads> [X] [Y] [Z]
Threads is required — the number of threads per block used for the
computation. X, Y and Z are optional and
default to 10 each. If you are unsure, start at
Threads = 1 and work up.
Measured speedup
Convergence time against thread count and grid size, measured on a personal machine. The pattern of interest is not the absolute numbers but how the advantage widens as the volume grows.
| Threads | Grid | Points | Time (s) | vs. 1 thread |
|---|---|---|---|---|
| 1 | 103 | 1 000 | 8.12 | — |
| 1 | 203 | 8 000 | 29.77 | — |
| 1 | 303 | 27 000 | 151.32 | — |
| 2 | 303 | 27 000 | 39.38 | 3.8× |
| 4 | 303 | 27 000 | 20.91 | 7.2× |
| 8 | 303 | 27 000 | 14.89 | 10.2× |
| 16 | 303 | 27 000 | 12.83 | 11.8× |
At 103 the thread count barely matters — 8.12 s down to 7.16 s across the whole sweep, because the problem is too small to hide the overhead. By 303 the same change is worth nearly 12×. Two observations worth recording: the gain continued past 8 threads per block, beyond what the test machine was expected to sustain; and the solver still runs X serially while parallelising Y and Z, so a further axis of parallelisation remains unexploited.
Status
This is an experimental first step towards a single-GPU Poisson environment, and it is archived — it was shelved for want of a machine large enough to make the next round of measurements meaningful. The unfinished roadmap was to lift the shared functions into a header, and to parallelise the remaining X axis.
The approach here — decompose the problem so the device stays saturated, then measure honestly — carried forward into CUDA-METRO, a peer-reviewed GPU Monte Carlo engine for 2D magnetism.
Questions and requests are welcome through the repository.