Shuffle extraction is often the OOM point. It acts as a barrier that waits on all data before continuing, so at that point a rank holds its entire local share of the shuffle. In RapidsMPF the normal answer is to spill device memory to host, but on very large datasets host memory is limiting too, and there is no tier below it.
We propose an option for the existing Shuffler to spill shuffled data to disk, and to unspill it back to host or device only when the user extracts it.
Proposal
Add a DiskBuffer, a file-backed handle to a single spilled chunk. Disk becomes a tier the shuffler can move a chunk into, and it stays entirely outside the MemoryType taxonomy.
Concretely, rapidsmpf::shuffler::detail::Chunk gains a disk-resident state, ReceivedChunks::spill gets a disk fallback instead of a throw, and Shuffler::extract rehydrates. Nothing in BufferResource, Buffer, MemoryType, or the general spill machinery changes.
Host limit
The Shuffler takes a host limit, a byte count or percentage mirroring the existing spill_device_limit. Device pressure spills device to host exactly as today, and crossing the host limit is what triggers host to disk. Disk is therefore entered on a threshold the user sets, rather than only once host allocation has already failed.
Why not MemoryType::DISK
A general MemoryType::DISK is something we may well want later, but it is a much bigger change than the enum makes it look, and most of the cost lands downstream.
Within RapidsMPF, Buffer cannot represent disk. Its storage is a std::variant of a device and a host buffer (buffer.hpp:370), and data(), write_access(), and exclusive_data_access() all return a raw pointer that a file-backed buffer does not have. Every cross-tier move funnels through buffer_copy() (buffer.cpp:134), one cuda_memcpy_async between two pointers against a single reservation, so disk cannot join that funnel without turning it into a tier dispatch that affects every BufferResource user. On top of that, once DISK is in MEMORY_TYPES it becomes reachable by accident from the sites that pass the whole array as a preference list.
Downstream is the larger cost. cudf_streaming has its own exhaustive switch over rapidsmpf::MemoryType with a default: RAPIDSMPF_FAIL, reached on the spill path, and it sizes an array by rapidsmpf::MEMORY_TYPES.size().
Shuffle extraction is often the OOM point. It acts as a barrier that waits on all data before continuing, so at that point a rank holds its entire local share of the shuffle. In RapidsMPF the normal answer is to spill device memory to host, but on very large datasets host memory is limiting too, and there is no tier below it.
We propose an option for the existing
Shufflerto spill shuffled data to disk, and to unspill it back to host or device only when the user extracts it.Proposal
Add a
DiskBuffer, a file-backed handle to a single spilled chunk. Disk becomes a tier the shuffler can move a chunk into, and it stays entirely outside theMemoryTypetaxonomy.Concretely,
rapidsmpf::shuffler::detail::Chunkgains a disk-resident state,ReceivedChunks::spillgets a disk fallback instead of a throw, andShuffler::extractrehydrates. Nothing inBufferResource,Buffer,MemoryType, or the general spill machinery changes.Host limit
The
Shufflertakes a host limit, a byte count or percentage mirroring the existingspill_device_limit. Device pressure spills device to host exactly as today, and crossing the host limit is what triggers host to disk. Disk is therefore entered on a threshold the user sets, rather than only once host allocation has already failed.Why not
MemoryType::DISKA general
MemoryType::DISKis something we may well want later, but it is a much bigger change than the enum makes it look, and most of the cost lands downstream.Within RapidsMPF,
Buffercannot represent disk. Its storage is astd::variantof a device and a host buffer (buffer.hpp:370), anddata(),write_access(), andexclusive_data_access()all return a raw pointer that a file-backed buffer does not have. Every cross-tier move funnels throughbuffer_copy()(buffer.cpp:134), onecuda_memcpy_asyncbetween two pointers against a single reservation, so disk cannot join that funnel without turning it into a tier dispatch that affects everyBufferResourceuser. On top of that, onceDISKis inMEMORY_TYPESit becomes reachable by accident from the sites that pass the whole array as a preference list.Downstream is the larger cost.
cudf_streaminghas its own exhaustive switch overrapidsmpf::MemoryTypewith adefault: RAPIDSMPF_FAIL, reached on the spill path, and it sizes an array byrapidsmpf::MEMORY_TYPES.size().