dataloader-rs
A high-performance DataLoader for Rust with a PyTorch-compatible Python API.
Typed, streaming batch pipeline with a familiar builder API. Supports sequential and parallel (inter-batch threads + intra-batch rayon) execution, pluggable samplers and collators, and Python bindings via PyO3.
See bench/bench.md for performance comparisons against
torch.utils.data.DataLoader.
Rust quick start
Add to Cargo.toml:
[]
= "0.1"
Define a dataset
use ;
Build a DataLoader and iterate
use ;
let mut loader = builder
.batch_size
.sampler // reproducible shuffle
.num_workers // inter-batch worker threads
.intra_workers // rayon threads per batch
.prefetch_depth // batches in-flight
.drop_last
.build;
for batch in &mut loader
Custom collator
use Collator;
;
Python quick start
Install:
Or build from source (requires a Rust toolchain):
Define a dataset
=
return
return
Build a DataLoader and iterate
=
# batch: list[bytes]
...
Custom collate function
return
=
Custom sampler
Any iterable of indices is accepted:
=
=
return
=
Architecture
Execution paths
DataLoader::iter() selects one of two paths:
inter_workers == 0 inter_workers > 0
┌─────────────────────┐ ┌──────────────────────────────┐
│ Direct path │ │ Parallel path │
│ │ │ │
│ Process each batch │ │ N worker threads pull from │
│ inline in next() │ │ unbounded work queue, push │
│ │ │ results to bounded channel │
│ Zero allocation, │ │ (capacity = prefetch_depth) │
│ no thread overhead │ │ │
└─────────────────────┘ └──────────────────────────────┘
Both paths can enable a rayon thread pool (intra_workers > 0) to fetch
items within a single batch in parallel.
Key design points
Fully generic — monomorphized hot path.
DataLoader<D, S, C> is generic over dataset, sampler, and collator. Every
code path is monomorphized — no virtual dispatch, no boxing, no dynamic
allocation in the iteration loop.
Bounded channel as backpressure.
prefetch_depth is the exact capacity of the crossbeam result channel.
Workers block when the channel is full, giving precise control over memory
without over-prefetching.
Reorder buffer preserves epoch order.
Workers complete batches out of order. A HashMap<batch_idx, Batch> reorder
buffer in the iterator reassembles results in epoch order regardless of
scheduling.
Safe raw-pointer sharing. Workers hold raw pointers to the dataset and collator, valid because:
DataLoaderIter<'a>— the borrow lifetime prevents use of the loader while the iterator is live;Dropjoins all threads before'aends.OwnedDataLoaderIter(Python FFI) — aPy<PyDataloader>strong reference keeps the loader alive until all threads are joined.
Python: GIL acquired once per batch.
PyDataset::get_batch acquires the Python thread state once for all items in
a batch, reducing GIL overhead from O(batch_size) to O(1) per batch in the
threaded path.
Module layout
src/
├── dataset.rs Dataset + IterableDataset traits
├── error.rs Error / Result type
├── sampler/
│ ├── sequential.rs SequentialSampler
│ ├── random.rs RandomSampler (inside-out Fisher-Yates, fastrand wyrand)
│ ├── distributed.rs DistributedSampler (padded strided sharding)
│ └── batch_sampler.rs BatchSampler (chunks indices into batches)
├── collator/
│ ├── vec_collator.rs VecCollator (collect items into Vec — default)
│ ├── default_collator/ DefaultCollator (tensor stacking, ndarray, primitives)
│ └── torch.rs TorchPinnedCollator (pin_memory for tch tensors)
└── loader/
├── builder.rs DataLoaderBuilder (type-safe builder)
├── core.rs DataLoader struct + IntoIterator
├── iter.rs DataLoaderIter, ParallelCore, OwnedDataLoaderIter (Python)
└── worker.rs process_batch, worker_loop
Building & testing
Prerequisites
- Rust stable ≥ 1.91
- Python ≥ 3.13 (free-threaded 3.13t / 3.14t recommended for parallel benchmarks)
- uv for Python environment management
Rust
# Library
# With Python bindings
# Tests
Python
# Install dev dependencies (maturin, pytest, torch, ...)
# Build and install the extension in development mode
# Run all Python tests
Benchmarks
Rust (Criterion)
HTML reports: target/criterion/.
Python regression (pytest-benchmark)
Python vs PyTorch (full sweep)
Features
| Feature | Default | Description |
|---|---|---|
python |
❌ | PyO3 bindings (PyDataloader, PyDataset) |
ndarray |
❌ | DefaultCollator for ndarray::Array types |
torch-rs |
❌ | TorchPinnedCollator for tch::Tensor + pin_memory |
License
MIT — see LICENSE.