GPU Programming · Reference
GPU lifecycle cheat sheet
Bounded queues, atomic slot reservation, dense compaction, and ping-pong buffers.
01 The invariants
| Invariant | Meaning |
|---|---|
active_count ≤ capacity |
The logical range always fits the fixed allocation. |
Live range is [0, active_count) |
Every record in the logical range is live after compaction; there are no holes. |
Spare range is [active_count, capacity) |
No pass may treat spare records as active merely because the bytes exist. |
| Overflow is observable | Rejected records increment telemetry; no write wraps or escapes the allocation. |
| Index is not identity | Unstable compaction may move a body to a different slot every lifecycle pass. |
next logical count min(survivors + requested births, capacity)
02 The five-dispatch pass
| Order | Phase | Dispatch size | Barrier after? |
|---|---|---|---|
| 1 | Reset next_count and overflow |
One workgroup | Yes |
| 2 | Mark removal indices dead | ceil(remove_count / local_size) |
Yes |
| 3 | Compact live source → destination | ceil(capacity / local_size) |
Yes |
| 4 | Append addition records | ceil(add_count / local_size) |
Yes |
| 5 | Clamp and publish active_count |
One workgroup | Before a dependent consumer |
Skip an empty queue phase rather than dispatching zero workgroups. Barriers are for dependencies, not decoration: if the next dispatch consumes the previous dispatch’s buffer writes, call compute_list_add_barrier() between them.
03 The slot-reservation pattern
uint slot = atomicAdd(counts.next_count, 1u);
if (slot < capacity) {
destination.data[slot] = record;
} else {
atomicAdd(counts.overflow_count, 1u);
}
atomicAddreturns the old value: the reserved slot.- Reservations are unique, but their order is unspecified.
- The bounds test occurs before every destination write.
- Finish with
active_count = min(next_count, capacity).
04 Persistent resources
| Resource | Lifetime / role |
|---|---|
| Body A, Body B | Fixed-capacity ping-pong pair; source and destination swap after each pass. |
| Addition queue | Fixed-capacity initialization records uploaded or produced before append. |
| Removal queue | Indices or handles to mark dead; delayed CPU indices require validation. |
| Counter buffer | active_count, next_count, overflow telemetry, padding: 16 bytes. |
| Two uniform sets | A→B and B→A bindings; select one per lifecycle pass. |
# Select the current direction, record the pass, then flip.
var lifecycle_set := set_a_to_b if front_is_a else set_b_to_a
front_is_a = not front_is_a
05 Failure map
| Symptom | Likely cause |
|---|---|
| Duplicate or missing survivors | In-place compaction or a non-atomic slot counter. |
| Occasional stale flags/counts | Missing compute barrier between dependent dispatches. |
| Active count exceeds capacity | Published next_count without clamping. |
| Writes corrupt another buffer | Bounds test happened after the destination write. |
| Different order every run | Expected for atomic, unstable compaction. |
| Removal hits the wrong body later | A compacted slot index was mistaken for stable identity. |
| All records vanish | Reset and compact were not separated by a barrier, or the source/destination set is reversed. |