Files
lancedb/docs/web/training/distributed-training.mdx
T
Jack YeandClaude Sonnet 5 de95ed3f6d docs: overhaul curation, feature engineering, security and deployment guides (#4748)
A broad documentation pass across the areas LanceDB Enterprise and
Embedded
diverge on most:

**Data Curation** -- GPU Function execution, Slurm scheduling (and what
Slurm is, for readers new to it), blob late materialization as its own
page, joins, aggregation/profiling, sampling, near-duplicate detection,
chunking, distributed shuffle, and IVF-partition clustering.

**Feature Engineering** -- computed columns and materialized views,
restructured to lead with the Function-based path and show the SQL form
side by side with Python throughout; a single Auto Refresh page instead
of
duplicating scheduling details on every refresh page; adaptive
execution;
checkpointing; GPU and Slurm placement for refresh workers; a Training
Cache page for the PyTorch data loader's automatic cache benefit.

**Security & Governance** -- an Audit Log reference (schema, access,
grants), Secrets promoted to an ordinary catalog object with their own
SQL
DDL and a vending/rotation guide, and privilege-table fixes verified
against the authorization source.

**Deployment & Maintenance** -- Horizontal Scaling and OpenTelemetry
moved
into a real Deployment area; Automated Cleanup folded into Intelligent
Auto-Maintenance (every other maintenance decision already lived there);
RTE Compaction and Advanced Conflict Resolution documented in depth.

**Landing page** -- the Enterprise feature comparison table reorganized
(several redundant or stale rows removed, several new areas added), and
the
"why Enterprise" bullets replaced with ones tied to a concrete page
(Performance, Scale, Fault tolerance, Multicloud deployment, Simplify
operations) instead of vague promises.

Plus a full pass addressing lancedb-gatekeeper's review: a real
test-safety
bug (a quickstart test could silently overwrite and delete a
deployment's
own `frames` table), a fabricated SQL statement with no backing
implementation, an actual dedup-guarantee bug in `take_row_ids`, and
dozens
of documentation-accuracy fixes verified against source rather than
taken
on faith -- each either fixed, or left with an explicit note on why it
needs a feature or deployment owner's sign-off instead of being guessed
at.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_018PXMCbRH6i5w47TsjeEUMT

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-11-03 16:17:24 -08:00

87 lines
4.4 KiB
Plaintext

---
title: "Distributed Training"
sidebarTitle: "Distributed Training"
description: "Shard StreamingDataset across ranks and workers, with elastic determinism across topologies."
icon: server
availability:
embedded: available
enterprise: available
---
To understand the wide range of scenarios handled by [`StreamingDataset`](/training/data-loading) across a
distributed job, consider a model trained with stochastic gradient descent (SGD) and distributed data parallelism
(DDP). In this example, we need to load batches onto multiple GPUs across multiple servers. After each batch is
processed, the GPUs exchange weights and the next batch is loaded. We fix the following terminology:
- **World size** - The number of GPUs being loaded simultaneously. For example, if we have $2$ servers with $4$
GPUs each, the world size is $8$.
- **Rank** - The identifier of an individual GPU. It is an integer in the range `[0, world_size)`.
Each rank is allocated a disjoint portion of the source data.
- **Batch size** - The number of rows processed by one GPU in a single training step. For example, $128$.
- **Global batch size** - The total number of rows processed across all GPUs in a training step (sometimes called a "minibatch" in machine learning literature). For
example, if we have $8$ GPUs with batch size $128$, the global batch size is $1024$.
Other concepts, such as read batch size and `num_workers`, are introduced in [PyTorch Data Loader](/training/data-loading).
## Parallelism & Elasticity {#parallelism-and-elasticity}
When loading data, `StreamingDataset` partitions the source dataset among disjoint parallel threads called **splits**. Each rank is allocated a contiguous group of splits, and each DataLoader worker associated to a rank gets a contiguous subgroup of its rank's splits.
If using `shuffle=False`, data is allocated to splits naively in ordered contiguous blocks of equal size. With `shuffle=True`
(the default), the rows of the source dataset are shuffled and distributed among splits such that each split receives the same
number of rows; with `shuffle_clump_size` set, they are shuffled in clumps of that many contiguous rows.
The number of splits defaults to `world_size` if `num_splits` is omitted during initialization. This is not recommended, as a later run using a different world size will then construct a different layout and structure of global batches.
To get **elastic determinism**, choose a fixed `num_splits` that is compatible with every topology you plan to use.
For example, a run with `num_splits=8` and $4$ GPUs assigns $2$ splits to each rank. The ranks pull from their splits in
round-robin order, producing the same global batches as a run with $8$ GPUs and the same `num_splits`, `shuffle_seed`,
and `epoch`.
The following constraints apply:
- `num_splits` must be divisible by `world_size`.
- With DataLoader worker processes, `num_splits` must be divisible by `world_size * num_workers`.
- For the same samples to form each global training step across topologies, `global_batch_size` must be a multiple of
`num_splits`.
- The filtered row count must be at least `num_splits`. If it is not evenly divisible by `num_splits`, up to
`num_splits - 1` surplus rows are dropped so that every split has the same length.
For example, to switch between $8$ and $6$ GPUs, use a common multiple such as $24$ for `num_splits`. Highly composite values can support many layouts; for example, `num_splits = 48` is compatible with $10$ distinct possible GPU counts. Remember to include `num_workers` when checking divisibility.
<CodeGroup>
```py Python icon=Python
import torch.distributed as dist
from lancedb.streaming import StreamingDataset
dist.init_process_group("nccl")
rank = dist.get_rank()
world_size = dist.get_world_size()
# num_splits=48 is divisible by many integers,
# so this dataset works unchanged as you add or remove GPUs.
ds = StreamingDataset(
table,
num_splits=48,
shuffle_seed=42,
epoch=current_epoch,
rank=rank,
world_size=world_size,
)
for sample in ds:
train_step(sample)
```
</CodeGroup>
## Next steps {#next}
<CardGroup cols={2}>
<Card title="Checkpointing" icon="floppy-disk" href="/training/checkpointing">
Save progress so a failed run can pick back up.
</Card>
<Card title="Resuming" icon="rotate-left" href="/training/resuming">
Load a saved checkpoint back, on the same or a different topology.
</Card>
</CardGroup>