1. num_workers: what it is
num_workers controls how many worker subprocesses the DataLoader uses to prepare data.num_workers = 0: everything runs in the main process (data loading + preprocessing + collation).
num_workers > 0: multiple worker processes load/decode/CPU-transform data and build batches in parallel, then send ready batches back to the main process.
Intuition: the main process trains (forward/backward) while workers “cook” the next batches in the background, reducing GPU idle time.
2. What a worker actually does
Conceptually, each worker repeats:
- Get a “task” from an index queue (usually a list of indices, e.g.,
[42, 17, 88, ...])
- For each index, run
dataset.__getitem__(idx)to fetch one sample
- Run
collate_fn(samples)to combine samples into a batch
- Put the completed batch into an output queue for the main process to consume
Key point (when
num_workers > 0 with auto-collation):__getitem__andcollate_fnrun inside worker processes
- the main process mainly: dispatch indices → receive batches → move to GPU → forward/backward
3. Who decides “how many indices per task”?
The main process decides, based on
batch_size, drop_last, and/or a custom batch_sampler.Default behavior (no custom batch_sampler)
- With
batch_size = B, DataLoader groups indices into lists of lengthB
- The last batch:
drop_last = False(default): may be a smaller batch if not enough samples remaindrop_last = True: the last incomplete batch is dropped
Custom batch_sampler
- If you provide a
batch_sampler, it fully controls how indices are grouped
- Batch sizes can be variable (whatever your sampler emits)
Conclusion: workers don’t choose batch sizes—they process whatever index list they receive.
4. prefetch_factor: worker prefetching
With multiprocessing (
num_workers > 0), DataLoader tries to keep batches “in flight” so the main process rarely waits.prefetch_factor≈ how many future batches per worker are kept queued/processing
- Default:
prefetch_factor = 2
Rule of thumb (steady state):
- In-flight batches ≈
num_workers × prefetch_factor
Example:
num_workers = 8,prefetch_factor = 2→ ~16 batches concurrently being prepared or waiting
Why it helps
- Better pipeline overlap: while the GPU trains on batch k, workers prepare batch k+1, k+2, ...
Why it can hurt
- Higher CPU RAM usage:
- Roughly:
batch_memory × num_workers × prefetch_factor(plus Python/object overhead, and optionally pinned memory)
5. CPU memory vs GPU memory (important)
The memory multiplied by
num_workers × prefetch_factor is primarily CPU RAM, because:- workers build batches on the host and place them into queues
- batches only consume GPU VRAM after the main process moves them to CUDA (e.g.,
.to(device))
6. persistent_workers: worker lifetime across epochs
Controls whether worker processes are recreated every epoch.
Without persistent_workers=True (default False)
- End of epoch → workers shut down
- Next epoch → workers start again
- Costs: process startup/teardown + re-initialization overhead (often noticeable when epochs are short)
With persistent_workers=True
- Workers are created once and reused across epochs
- Benefits:
- faster epoch transitions
- more stable performance
- avoids repeated worker initialization overhead
Practical recommendation:
- If
num_workers > 0and you train for multiple epochs with a mostly static dataset,persistent_workers=Trueis usually a win.
- 作者:SylviaXiao
- 链接:https://sylviaxiao.blog//article/314beda9-55cb-803a-8bff-c1f9b9bbdd02
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章







