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.

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:

KernelRole
DDX DDY DDZSecond partial derivative along each axis.
ADDApplies the update step to the field.
ASSIGNCopies the next state over the current one.
COMPAREReduces the per-point change to a maximum error, on device.
RESET_CTRClears 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.

ThreadsGridPointsTime (s)vs. 1 thread
11031 0008.12
12038 00029.77
130327 000151.32
230327 00039.383.8×
430327 00020.917.2×
830327 00014.8910.2×
1630327 00012.8311.8×
10 20 50 100 200 1 2 4 8 16 Threads per block Time (s), log
303 — 27 000 points 203 — 8 000 points 103 — 1 000 points

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.

Successor
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.