HPC Python
Python parallelism, PyTorch DDP, and CPU/GPU latency hiding patterns.
Extension Files
| File |
Content |
parallel-python.md |
Threading vs multiprocessing vs asyncio decision tree, GIL rules, CUDA+fork safety |
latency-hiding.md |
CUDA streams, double buffering, compute/comm overlap, CUDAGraphs, async checkpoint |
pytorch-ddp.md |
DDP internals, gradient buckets, common bugs, mixed precision, DistributedSampler |
preload-caching.md |
Three-level caching: L1 file (disk/memmap/shm), L2 function (lru_cache/dedup/index), L3 variable (buffer/GPU cache/warmup/KV cache) |
torch-compile.md |
torch.compile modes, graph break diagnosis/fixes, reading generated Triton as starting point for hand-tuning |
benchmarking.md |
Correct GPU timing (CUDA events, torch.utils.benchmark.Timer), warmup, common pitfalls, what to measure |
dataloader.md |
DataLoader params (num_workers/pin_memory/prefetch_factor), dataset patterns, data formats, collation, worker issues |
Quick Decision Tree
What is the bottleneck?
├─ I/O bound → threading (ThreadPoolExecutor) or asyncio
├─ CPU bound → multiprocessing (mp.Pool, fork BEFORE CUDA!)
├─ GPU bound → batch inputs, don't parallelize
├─ Mixed CPU→GPU → pipeline + CUDA streams (see latency-hiding.md)
└─ DDP communication → tune bucket_cap_mb, use model.no_sync()
Critical Rules
- mp.Pool BEFORE CUDA: Create multiprocessing pool before any
torch.cuda call (fork+CUDA = deadlock)
- Never
.item() in loops: Accumulate on GPU, transfer final result only
pin_memory=True: Required for non_blocking=True transfers to actually be async
- DDP
no_sync(): Use during gradient accumulation steps to avoid redundant AllReduce
find_unused_parameters=True: Only when needed — it's expensive
Review Checklist (Python)
□ No .cpu()/.item()/.numpy() in hot loops?
□ DataLoader: num_workers > 0, pin_memory=True?
□ mp.Pool created before CUDA init?
□ Threading used only for I/O, not CPU-bound work?
□ DDP gradient accumulation uses no_sync()?
□ CUDA streams used for transfer/compute overlap?
□ Pre-allocated buffers reused (not created per iteration)?
1---2name: hpc-python3description: Python HPC patterns — threading/multiprocessing/async, CUDA streams, latency hiding, PyTorch DDP. Use when writing performance-sensitive Python code, distributed training, or parallel data processing.4---5
6# HPC Python
7
8Python parallelism, PyTorch DDP, and CPU/GPU latency hiding patterns.
9
10## Extension Files
11
12| File | Content |
13|------|---------|
14| `parallel-python.md` | Threading vs multiprocessing vs asyncio decision tree, GIL rules, CUDA+fork safety |
15| `latency-hiding.md` | CUDA streams, double buffering, compute/comm overlap, CUDAGraphs, async checkpoint |
16| `pytorch-ddp.md` | DDP internals, gradient buckets, common bugs, mixed precision, DistributedSampler |
17| `preload-caching.md` | Three-level caching: L1 file (disk/memmap/shm), L2 function (lru_cache/dedup/index), L3 variable (buffer/GPU cache/warmup/KV cache) |
18| `torch-compile.md` | torch.compile modes, graph break diagnosis/fixes, reading generated Triton as starting point for hand-tuning |
19| `benchmarking.md` | Correct GPU timing (CUDA events, torch.utils.benchmark.Timer), warmup, common pitfalls, what to measure |
20| `dataloader.md` | DataLoader params (num_workers/pin_memory/prefetch_factor), dataset patterns, data formats, collation, worker issues |
21
22## Quick Decision Tree
23
24```
25What is the bottleneck?
26├─ I/O bound → threading (ThreadPoolExecutor) or asyncio
27├─ CPU bound → multiprocessing (mp.Pool, fork BEFORE CUDA!)
28├─ GPU bound → batch inputs, don't parallelize
29├─ Mixed CPU→GPU → pipeline + CUDA streams (see latency-hiding.md)
30└─ DDP communication → tune bucket_cap_mb, use model.no_sync()
31```
32
33## Critical Rules
34
351. **mp.Pool BEFORE CUDA**: Create multiprocessing pool before any `torch.cuda` call (fork+CUDA = deadlock)
362. **Never `.item()` in loops**: Accumulate on GPU, transfer final result only
373. **`pin_memory=True`**: Required for `non_blocking=True` transfers to actually be async
384. **DDP `no_sync()`**: Use during gradient accumulation steps to avoid redundant AllReduce
395. **`find_unused_parameters=True`**: Only when needed — it's expensive
40
41## Review Checklist (Python)
42
43```
44□ No .cpu()/.item()/.numpy() in hot loops?
45□ DataLoader: num_workers > 0, pin_memory=True?
46□ mp.Pool created before CUDA init?
47□ Threading used only for I/O, not CPU-bound work?
48□ DDP gradient accumulation uses no_sync()?
49□ CUDA streams used for transfer/compute overlap?
50□ Pre-allocated buffers reused (not created per iteration)?
51```