diff --git a/docs/_static/figs/shuffle_epoch_loop.svg b/docs/_static/figs/shuffle_epoch_loop.svg
new file mode 100644
index 00000000..cb7a199e
--- /dev/null
+++ b/docs/_static/figs/shuffle_epoch_loop.svg
@@ -0,0 +1,80 @@
+
\ No newline at end of file
diff --git a/docs/_static/figs/shuffle_sample_files.svg b/docs/_static/figs/shuffle_sample_files.svg
new file mode 100644
index 00000000..b9a1123d
--- /dev/null
+++ b/docs/_static/figs/shuffle_sample_files.svg
@@ -0,0 +1,133 @@
+
\ No newline at end of file
diff --git a/docs/design.rst b/docs/design.rst
index a59009de..6a62ebf1 100644
--- a/docs/design.rst
+++ b/docs/design.rst
@@ -30,6 +30,9 @@ A producer and consumer scenario sharing a file
Deep Learning Training by Distributed Stochastic Gradient Descent (SGD)
=======================================================================
+.. figure:: _static/figs/shuffle_sample_files.svg
+ :alt: shuffling sample files
+
Deep learning training often requires randomizing the order of input samples at
each epoch. In distributed or parallel training, where each worker processes a
subset of the samples, the set of files assigned to each worker changes at every
@@ -41,7 +44,7 @@ workers when needed. Initially, the dataset is partitioned across workers. When
a file is reference for the first time, it is staged into the DYAD managed
directory on the local storage of the worker who owns the partition.
-Check out our use case with the `DLIO `_
+Check out our use case with the `DLIO `_
representing PyTorch dataloader :ref:`Devarajan et al. 2024 `.
diff --git a/tests/shuffle/README.md b/tests/shuffle/README.md
index a9dfdda8..422ea152 100644
--- a/tests/shuffle/README.md
+++ b/tests/shuffle/README.md
@@ -34,7 +34,7 @@ otherwise be fetched from another worker’s local storage or from shared storag
It can reside on either local or shared storage, controlled by `--is-local`.
When local, each worker has its own work-dir and owns an exclusive partition
of the file list — files are either generated or staged into it before the
-epoch loop begins. When shared, all workers reference the same work_dir —
+epoch loop begins. When shared, all workers reference the same `work_dir` —
files are either generated into it or assumed to already exist there.
## Options
@@ -110,10 +110,50 @@ file_N-1.dat
| Suffix | Multiplier | Example |
|---|---|---|
-| none | 1 | `--size 4096` → 4096 bytes |
-| `K` | 1024 | `--size 4K` → 4096 bytes |
-| `M` | 1024² | `--size 2M` → 2097152 bytes |
-| `G` | 1024³ | `--size 1G` → 1073741824 bytes |
+| none | 1 | `--size 4096` ->4096 bytes |
+| `K` | 1024 | `--size 4K` -> 4096 bytes |
+| `M` | 1024² | `--size 2M` -> 2097152 bytes |
+| `G` | 1024³ | `--size 1G` -> 1073741824 bytes |
+
+
+## Implementation
+
+
+
+**Epoch loop in the shuffle test**
+Epoch loop in the shuffle test. Before the loop begins, each worker prepares
+its partition of files through generation or staging into local storage. Each
+epoch starts with `worker.shuffle()`, which runs `std::shuffle` on `m_fidx` using a
+shared RNG seed — producing an identical file order across all workers with no
+MPI communication. Each worker then iterates over its assigned window of `m_fidx`
+and calls `open()` on each mapped filename via a plain POSIX call, unaware of
+where the file physically resides. If the file is in the worker's own local
+storage it is read directly; if it belongs to another worker, DYAD intercepts
+the `open()` call and fetches the file transparently via the KVS and RDMA
+transfer. In a real training scenario, each file would then be processed (e.g.
+decoded and fed to the model), but the shuffle test omits this step as it
+benchmarks I/O behavior only. The epoch ends with an `MPI_Barrier` that
+synchronizes all workers before the next shuffle begins.
+
+
+
+**Data structures used by the shuffle test.**
+`m_flist` stores the filenames in a fixed order shared by all workers.
+Each worker owns a contiguous partition of files in local storage — this
+ownership is static and does not change across epochs.
+`m_fidx` is an index array shuffled each epoch using std::shuffle with the
+same RNG seed on every worker, as described above.
+Each worker reads a contiguous window of `m_fidx` determined by `split()`
+— rank 0 gets indices [0,3), rank 1 [3,6), and rank 2 [6,8).
+While the window into `m_fidx` is fixed, the shuffled indices point to
+different files every epoch, so each worker processes a different subset
+of files each time.
+To read a file, a worker dereferences its assigned indices in `m_fidx` to
+obtain positions in `m_flist`, then opens the corresponding file. If the
+file resides in another worker's local storage (as shown: rank 1 reading
+"ibis.dat" owned by rank 2), DYAD intercepts the `open()` call and fetches
+it transparently via the KVS and RDMA transfer.
+
## Examples