executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
performance-tuningTune FFCV cache, traversal, loader concurrency, compilation, and measurement choices; diagnose RAM, disk, CPU, and GPU bottlenecks without unsafe benchmark-scale execution.
Independent GitHub snapshot · Automated classification · Original documentation governs
Use this sub-skill when an FFCV Loader is correct but throughput, latency,
resource use, or distributed behavior needs diagnosis. It owns cache selection,
OrderOption selection, loader concurrency/prefetch knobs, compiler behavior,
small measurements, and resource-bottleneck recovery. It does not own
writer format design, decoder/transform semantics, or end-to-end model tuning.
.beton dataset.
Record dataset size, available RAM, storage type, CPU affinity, GPU count,
batch size, pipeline, and whether the loader is distributed.QUASI_RANDOM before checking distributed.num_workers, batches_ahead, or recompile at a time. Keep
the same seed, batch size, pipeline, and measurement window.python -m ffcv.benchmarks --help; the safe CLI procedure is in
benchmarking.md. Never make the full benchmark
matrix or a long training run the default validation path.| Situation | Starting point | Guardrail |
|---|---|---|
| Dataset fits comfortably in RAM, or several processes share it | os_cache=True with RANDOM or QUASI_RANDOM | The first epoch can be slower while the OS warms its cache. |
| Dataset is larger than RAM and randomness need not be perfectly uniform | os_cache=False plus OrderOption.QUASI_RANDOM | This combination is specifically for process-level page reuse; it is unavailable with distributed=True. |
| Dataset is larger than RAM and exact distributed sampling is required | os_cache=False with SEQUENTIAL or RANDOM, distributed=True | QUASI_RANDOM raises NotImplementedError in distributed mode. Test the I/O cost of RANDOM before adopting it. |
| Dataset order must be repeatable | SEQUENTIAL, or seeded RANDOM | In non-distributed mode random order uses seed + epoch; in distributed mode PyTorch DistributedSampler is used. |
| CPU is saturated and the GPU is idle | Start at the physical-core count or fewer for num_workers; prefer FFCV JIT transforms | num_workers controls compiler thread counts, not a pool of Python worker processes. |
| Prefetch causes memory pressure or no overlap benefit | Lower batches_ahead from its default of 3 | It sizes the output queue/ring buffers; lowering trades overlap for RAM/GPU headroom. |
| An augmentation implementation changes between epochs | recompile=True | It recompiles generated pipeline code each epoch; otherwise keep the default False. |
These are starting hypotheses, not throughput guarantees. Validate on the
actual storage and pipeline. os_cache=True uses a read-only numpy.memmap
backed by the operating system; os_cache=False creates a process-level page
schedule for each epoch and prefetches pages into reusable slots. The process
cache is not a second full-dataset cache.
num_workers=-1 resolves to the process CPU affinity. A positive value is
passed to Compiler.set_num_threads, which sets both Numba and PyTorch thread
counts. FFCV is thread-based; do not interpret it as PyTorch's process worker
count. More threads can hurt when JIT operations are already efficient.batches_ahead defaults to 3. EpochIterator uses it for the bounded
result queue, CUDA stream count (batches_ahead + 2), and rotating operation
buffers. Increase only when the consumer is waiting for data and memory is
available; decrease when host/device buffers or latency variance is the
problem.recompile=False compiles the graph when the loader is constructed and on
the first iteration. With True, Loader.__iter__ regenerates code each
epoch. Use it only when a stateful/dynamic augmentation really changes its
implementation or generated code.seed is part of the reproducibility contract. With distributed random order
and no seed, Loader warns and sets the seed to 0 to match PyTorch's
sampler. For other missing-seed cases it generates a seed, so record the
resulting loader configuration if comparing runs.Compiler is enabled by default. When enabled, Compiler.compile wraps
functions with Numba njit(fastmath=True, nogil=True, error_model='numpy');
parallel functions use prange only when they advertise is_parallel and
the configured thread count is greater than one. Disabling compilation is a
diagnostic/benchmark comparison, not a normal performance fix.os_cache=False, order=OrderOption.QUASI_RANDOM, distributed=False.
For distributed training, explicitly use SEQUENTIAL or RANDOM instead.The repository evidence supports CPU smoke checks, memory-reader/allocation checks, traversal-order tests, and a tiny CUDA allocation probe. Full benchmark matrices, ImageNet benchmark reproduction, and long training are excluded from routine verification. See the references for source anchors and hard cases.