forked from 1b-agent/fieldcraft-upstream
lbm-gpu: stream in place with the AA pattern
The collide and stream passes each moved the whole population field through two buffers (304 bytes per cell update). The AA pattern (Bailey et al. 2009; Wittmann et al. 2013) keeps one buffer: even steps collide each cell in place with its directions swapped, odd steps read a cell's incoming populations at its neighbours and write the outgoing ones where the next step reads them, so each thread writes the addresses it read and streaming happens inside the collision (152 bytes). A cell's coordinates are computed once and neighbours wrap with a compare and an add. The per-link wall rules gather their sources before a scatter writes them, so no link overwrites another's post-collision value, and the momentum exchange is taken in the gather of the step whose forces are read back. Options::streaming picks the layout; Auto, what a solve uses, streams in place, which measured faster on every device tried: 1.5x on an RTX 4090, 1.85x on the GB10 and 1.45x on an M4 Max at periodic 128^3. Options::profile times each kernel with timestamp queries for the throughput test, which now runs both layouts. The verification tests run in both layouts, with two new ones comparing fields, mass, forces and heat with the CPU after odd and even step counts across submission boundaries. Part of #562.
This commit is contained in:
1 parent
74dce59c7d
commit
60fd0998f5
7 files changed
+698
-304
No files matched your search
@@ -33,7 +33,7 @@ crates in strictly lower layers.
|
||||
| L5 | `fieldcraft-io` | `.fieldcraft` files (docs/formats/fieldcraft.md): text model definition, binary mesh, geometry references with content hashes, the journal; atomic saves; autosave storage for crash recovery; read and write roots that confine an automation client's file access (`roots`). Bytes in, bytes out; the file system only in `fs`, `recovery` and `roots` (in the browser, files kept in memory: `memory`). Result files later. | started |
|
||||
| L6 | `fieldcraft-engine` | Commands (`CommandSpec`), `Session::execute` with rollback and the panic guard, scripts; every file a command names checked against the session's roots (`Session::file_access`); headless pictures of the session (`view`: model, geometry, deformed shape and contours on the CPU rasterizer). | started |
|
||||
| L7 | `fieldcraft-gpu` | The viewport's GPU pipelines (lit faces, wide lines, MSAA, depth) without a window toolkit, the adapter policy (`FIELDCRAFT_GPU`: hardware GPU, software GPU, CPU rasterizer) and offscreen pictures (`fieldcraft-cli snapshot`). The window draws through the same pipelines. | started |
|
||||
| L7 | `fieldcraft-lbm-gpu` | The lattice Boltzmann solver's GPU backend: the CPU scheme's step (TRT collision with Guo forcing, the per-link wall rules, Zou–He boundaries, momentum exchange, and the temperature lattice with its wall conditions, buoyancy and heat exchange) as wgpu compute shaders, driven through `lbm`'s `Backend` trait from the flow the CPU prepared; single precision, deterministic per device, checked against the CPU within its tests' tolerances; opt-in per solve (`solve.fluid` with `backend: "gpu"`). Opens its device with `gpu`'s adapter policy. | started |
|
||||
| L7 | `fieldcraft-lbm-gpu` | The lattice Boltzmann solver's GPU backend: the CPU scheme's step (TRT collision with Guo forcing, the per-link wall rules, Zou–He boundaries, momentum exchange, and the temperature lattice with its wall conditions, buoyancy and heat exchange) as wgpu compute shaders, driven through `lbm`'s `Backend` trait from the flow the CPU prepared; single precision, one population buffer streamed in place (the AA pattern, `docs/lbm-gpu-performance.md`), deterministic per device, checked against the CPU within its tests' tolerances; opt-in per solve (`solve.fluid` with `backend: "gpu"`). Opens its device with `gpu`'s adapter policy. | started |
|
||||
| L7 | `fieldcraft-ui-egui`, `fieldcraft-mcp` | Desktop UI in the Craft family's layout (title bar with quick actions, workspace ribbon from the catalogue, browser, viewport with navigation bar and orientation gizmo, context panel, command search and command line, journal timeline; native file pickers as host services, background opening and solving; results display with legend and probe; offscreen capture of the whole window) and control channel requests (docs/control-protocol.md); MCP server over stdio (docs/mcp.md): tools over engine commands, a headless session or the running app through its control channel, read and write roots. | ui, mcp started |
|
||||
| testkit | `fieldcraft-testkit` | Test tooling, a dev-dependency only: the meshing test corpus (`tests/corpus/*/recipe.json`, analytic shapes) and its runner (`cargo xtask corpus`). | started |
|
||||
| apps | `fieldcraft`, `fieldcraft-cli`, `fieldcraft-web` | Desktop, command line, browser (docs/web.md). | started |
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# GPU lattice Boltzmann: streaming layout and performance
|
||||
|
||||
The momentum populations stream in one of two layouts, chosen per flow with
|
||||
`fieldcraft_lbm_gpu::Options::streaming`:
|
||||
|
||||
- **In place** (the AA pattern: Bailey, Myre, Walsh, Lilja and Saar, "Accelerating lattice
|
||||
Boltzmann fluid flow simulations using graphics processors", ICPP 2009; Wittmann, Zeiser, Hager
|
||||
and Wellein, Comput. Math. Appl. 65 (2013) 924–935, §6). One population buffer. Even steps read
|
||||
population `q` of cell `x` at `(q, x)` and write the collided one at `(opposite(q), x)`; odd steps
|
||||
read it at `(opposite(q), x − c_q)` and write it at `(q, x + c_q)`. In both, a cell writes exactly
|
||||
the addresses it read, so cells never race, and streaming happens inside the collision: 152
|
||||
population bytes per cell update.
|
||||
- **Two buffers** (A–B). Collide in place, then pull into the other buffer: 304 bytes per update.
|
||||
|
||||
The wall, reflection and open-face rules are the same per-link rules in both. In place, a gather
|
||||
pass reads every link's post-collision source into a scratch value before a scatter pass writes,
|
||||
so no link overwrites another's source; the momentum exchange is taken in the gather of the step
|
||||
whose forces are read back (a submission's last). The temperature lattice is two-buffer in both.
|
||||
|
||||
`Streaming::Auto`, what a solve uses, streams in place: it was faster on every device measured
|
||||
(the ignored throughput test, million cell updates per second, means of three runs; the 4090 row
|
||||
is from the first version of this layout, before its addressing was made cheaper):
|
||||
|
||||
| device | case | in place | two buffers |
|
||||
|---|---|---|---|
|
||||
| RTX 4090, Vulkan | periodic 128³ | 3897 | 2578 |
|
||||
| RTX 4090, Vulkan | sphere 128³ | 3572 | 2441 |
|
||||
| GB10 (unified memory), Vulkan | periodic 128³ | 1224 | 656 |
|
||||
| GB10 (unified memory), Vulkan | sphere 128³ | 1146 | 630 |
|
||||
| M4 Max, Metal | periodic 128³ | 1364 | 937 |
|
||||
| M4 Max, Metal | sphere 128³ | 1256 | 872 |
|
||||
|
||||
On the 4090 and the GB10 the two-buffer step is bound by memory bandwidth (~0.8 TB/s and ~0.2 of
|
||||
the GB10's 0.27), so halving the bytes pays directly; on the M4 Max it gains once the in-place
|
||||
addressing is cheap (a cell's coordinates once, neighbours wrapped by compare and add). With
|
||||
128³ cells in place also needs one 152 MiB population buffer less. 64³ fits in cache, where the
|
||||
two layouts are closer and vary more between runs.
|
||||
|
||||
## Measuring
|
||||
|
||||
```
|
||||
cargo test --release -p fieldcraft-lbm-gpu --test throughput -- --ignored --nocapture
|
||||
```
|
||||
|
||||
runs each case in both layouts on the device `FIELDCRAFT_GPU` picks, marks the one `Auto` chooses
|
||||
and prints how long creating the pipelines took (on Metal this reads near zero: the driver
|
||||
compiles lazily or from its cache, so the cost lands in the first dispatches instead). With `FIELDCRAFT_LBM_GPU_PROFILE=1` it also prints
|
||||
each kernel's GPU time (`Options::profile`, timestamp queries; the throughput is then not
|
||||
representative, as every dispatch gets its own pass).
|
||||
Reference in new issue
Block a user