PyDDStore reference¶
PyDDStore(comm_or_none=None, method=0, handshake_dir="", n_core=0, nic_map=None, rank=None, size=0, exchange_ns="")¶
Parameter |
Type |
Description |
|---|---|---|
|
|
MPI communicator covering all ranks. |
|
|
|
|
|
Required for |
|
|
Required for a |
|
|
Without MPI ( |
|
|
Without MPI: names this store’s exchange files; default |
|
|
Optional, |
Call shapes:
PyDDStore(comm) # method 0, MPI RMA
PyDDStore(comm, method=1) # method 1, libfabric RDMA
PyDDStore(comm, method=2, handshake_dir="/path") # method 2, core member (n_core == comm size)
PyDDStore(None, method=2, handshake_dir="/path", n_core=N) # method 2, extra member (no comm)
PyDDStore(None, method=1, handshake_dir="/path", rank=r, size=N) # method 1 without MPI
PyDDStore(None, method=2, handshake_dir="/path", rank=r, size=N) # method 2 core member without MPI
In a build without MPI (DDSTORE_MPI=0, pyddstore.HAVE_MPI is False) only the last three work; a communicator raises RuntimeError.
Independent stores per group of ranks: comm.Split() and pass the sub-communicator (DistDataset(ddstore_width=) does this).
init(name, nrows, disp, itemsize=1)¶
Pre-allocate a named variable without providing data yet. Use update() to fill it in afterwards. Collective.
Parameter |
Type |
Description |
|---|---|---|
|
|
Variable identifier |
|
|
Number of rows in this rank’s shard |
|
|
Number of elements per row |
|
|
Bytes per element (default |
add(name, arr)¶
Register a NumPy array as a named variable. Each rank contributes its local shard; the global index space is the concatenation of all shards in rank order. Collective — all ranks in comm must call with the same name.
Parameter |
Type |
Description |
|---|---|---|
|
|
Variable identifier |
|
|
C-contiguous 2-D (or 1-D) array/tensor. Supported dtypes: |
update(name, arr, offset)¶
Overwrite a region of the local shard for a variable registered with init(). Local operation — does not require epoch or barrier.
Parameter |
Type |
Description |
|---|---|---|
|
|
Variable identifier |
|
|
Data to write |
|
|
Row offset within the local shard |
get(name, arr, start=0)¶
Read arr.shape[0] consecutive rows starting at global index start into arr. The range must fall within a single rank’s shard. With method=0, call it between epoch_begin() and epoch_end() so that other ranks’ update() writes are visible.
Parameter |
Type |
Description |
|---|---|---|
|
|
Variable identifier |
|
|
Pre-allocated, C-contiguous output buffer. A CUDA/HIP tensor writes the RDMA transfer directly into GPU memory — see GPUDirect RDMA below |
|
|
Global row index |
get_batch(name, arr, indices)¶
Read rows indices (global ids; any order, repeats allowed) into arr; row i of arr gets row indices[i], so arr.shape[0] == len(indices) (else ValueError). Buffers as for get(). Indices are checked first; an out-of-range one raises IndexError.
method=1/2: all rows’ fi_reads are in flight together, with one lock acquisition, at most one memory registration (none into a registered buffer) and one device sync (GPU destination). Rows over DDSTORE_MAX_READ_BYTES (1 GiB) are split.
method=0: collective, after MDLoader (Citation): indices are all-gathered (MPI_Allgatherv) and rows delivered by MPI_Alltoallv, in rounds of at most DDSTORE_ALLTOALL_MAX_BYTES (2 MiB) per rank. Every rank calls it the same number of times, in the same order, from one thread; index counts may differ (including 0). A bad index raises on every rank. DistributedSampler meets this.
idx = np.array([2048, 7, 4096, 7])
out = np.zeros((len(idx), 64), dtype=np.float32)
store.get_batch("features", out, idx)
DistDataset/DistDatasetReader use it through __getitems__ by default; DDSTORE_BATCH_GET=0 reads per sample.
register_recv(name, arr) / unregister_recv(name, arr)¶
Register a reused destination (C-contiguous array or CUDA/HIP tensor) once, so reads into it or any slice skip memory registration, which can cost more than the transfer for large rows. Several buffers per variable; none evicted. The store keeps a reference until unregister_recv() or free(). No-op for method=0.
pool = np.empty((batch_size, ncols), dtype=np.float32)
store.register_recv("features", pool)
for idx in batches:
store.get_batch("features", pool[: len(idx)], idx) # no registration
get_profile(name)¶
With DDSTORE_PROFILE=1: get()/get_batch() timing for name (method=1/2), seconds unless noted. Per variable: calls, rows, lock_wait, mr (registration), mr_miss (count), read (posting fi_read), cq (completion wait). Per store: py_gets, py_get (whole calls), py_sync (GPU sync). Zero for method=0 or without profiling. See Performance.
join(name)¶
method=2 extra member: wait for the core group’s {name}.bin record file (up to DDSTORE_HANDSHAKE_TIMEOUT_S), then read the variable with get()/get_batch().
Parameter |
Type |
Description |
|---|---|---|
|
|
Variable identifier, matching the |
fork_reader()¶
A read-only PyDDStore for the same variables, with its own libfabric endpoints. Methods 1 and 2.
Forked process (
DataLoaderworker): call it in the child; host buffers only. On CXI needsDDSTORE_FORK_WORKERS=1before the parent’s store is created (raises underkdreg2).Same process: one reader per thread, so threads read in parallel.
DistDataset and DistDatasetReader use it automatically. See Forked DataLoader workers.
info(name)¶
Returns (total_rows, disp, itemsize) for a variable that has been add()-ed or join()-ed. Useful on the extra side to size output buffers without hardcoding shapes.
epoch_begin() / epoch_end()¶
Collective synchronization points for method=0: epoch_begin() makes each rank’s update() writes visible and waits for every rank; epoch_end() waits until every rank is done reading. Each get() locks its target itself (MPI_Win_lock), so these hold no RMA epoch. No-op for method=1/2.
free()¶
Release MPI windows or libfabric endpoints and registrations (including register_recv() buffers), and host buffers from add()/init(); GPU tensors passed to add() stay the caller’s. Idempotent; skips MPI resources after MPI_Finalize.
pyddstore.HAVE_MPI¶
False for a DDSTORE_MPI=0 build (methods 1 and 2 without a communicator).
pyddstore.FileComm(dir, rank, size, ns)¶
File-based collectives without MPI: allgather(obj) (list by rank) and barrier(). dir on a shared filesystem; every rank creates its FileComms and makes calls in the same order, with an ns unique in dir (also among stores’ exchange_ns). Attributes: rank, size, dir, ns. Rank 0 leaves barrier() once it has written the combined file, so don’t remove dir right after one.