Add opt-in tensor spill to disk for colocated datasets - #757
Merged
Conversation
Large CPU tensors can live in files under GIGL_TENSOR_SPILL_DIR and be consumed as mmap views, so their pages are reclaimable page cache instead of unreclaimable anonymous or tmpfs memory. The API: prepare_spill_dir (run-start cleanup), spill_tensor_to_disk / allocate_disk_backed / allocate_preshared (placement), SpilledTensorHandle with share_memory_for_ipc / resolve_spilled_handles (crossing process boundaries without undoing a spill), release_page_cache (dropping the cgroup charge of written-once data), plus introspection helpers. Placement policy: sequential-write destinations default to disk, scattered destinations prefer memory with a checked fallback, and file block reservation via posix_fallocate is mandatory so a filesystem that fills fails as OSError at allocation rather than SIGBUS on a page write. share_memory() keeps its -> None signature; its one behaviour change is leaving already disk-backed tensors alone rather than copying them back into /dev/shm. Off by default: with GIGL_TENSOR_SPILL_DIR unset, nothing changes.
dsaini2-sc
requested review from
kmontemayor2-sc,
mkolodner-sc,
nshah-sc,
svij-sc,
xgao4-sc,
yliu2-sc and
zfan3-sc
as code owners
August 19, 2026 03:21
dsaini2-sc
enabled auto-merge
August 19, 2026 05:02
kmontemayor2-sc
left a comment
Collaborator
There was a problem hiding this comment.
Nice work Deepak! Thanks :)
Did we consider torch.from_file? See https://github.com/Snapchat/GiGL/pull/757/changes#r3814543741. I think that if that's feasible (and in my testing it seems like it should be, without adding additional mem overhead), then we should be able to simplify this a lot by using that API and not needing the registry or new dataclasses / handles / etc?
…asses Maintainer-requested rework: recognition now reads untyped_storage().filename off the tensor, so the address-range registry, its weakrefs, SpilledTensor and SpilledTensorHandle are deleted; spill functions return plain tensors. IPC is a ForkingPickler reducer for torch.Tensor, gated on spilling being enabled: file-backed tensors ship as (path, dtype, shape, stride, offset) and re-map on arrival; everything else delegates verbatim to torch's reducer (named and quantized included). A rebuild validates file existence and size first, since from_file would otherwise recreate a deleted spill file as zero-filled unreserved storage. Unchanged: posix_fallocate reservation (reserve-before-map now pinned by test), spill-dir lifecycle, placement policy, page-cache release order (madvise now via ctypes). numpy is out of the module; torch-only dtypes such as bfloat16 are now spillable, quantized dtypes are refused. Tests 33 -> 38; sequential-write probe shows the torch path ~3.5x faster than the numpy path with identical flush cost.
kmontemayor2-sc
approved these changes
Aug 19, 2026
kmontemayor2-sc
left a comment
Collaborator
There was a problem hiding this comment.
Thanks for the interations here!
yliu2-sc
approved these changes
Aug 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds an opt-in tensor-spill layer to
gigl/utils/share_memory.py, plus its tests. Off by default: withGIGL_TENSOR_SPILL_DIRunset, nothing changes.Why
In colocated mode each rank's features and topology live in POSIX shared memory.
/dev/shmis tmpfs, i.e. RAM, and without swap those pages cannot be evicted; worse,share_memory_()on an anonymous tensor copies it into/dev/shm, so the largest tensors transiently exist twice -- GLT'sGraph.__init__shares the CSR unconditionally, and at billion-node scale that transient 2x is what OOM-kills the graph build. Spilling large tensors to a local disk turns their bytes into reclaimable page cache, letting large graphs run colocated without a separate storage tier.What it adds
prepare_spill_dir()-- run-start cleanup of the spill directory. Cleanup never runs at exit, because the process that spills exits before the consumers unmap; a spill directory belongs to one workload and is reused across its sequential runs.spill_tensor_to_disk/allocate_disk_backed/allocate_preshared-- placement.Nonealways means "fall back to memory". Sequential-write destinations go to disk (~3.1x measured write cost, bytes never anonymous); scattered destinations prefer memory, because an uncached 8-byte file write is a 4 KiB read-modify-write and a CSR build revisits the same pages hundreds of times. Block reservation viaposix_fallocateis mandatory, so a filesystem that fills or cannot reserve fails asOSErrorat allocation, never as SIGBUS on a later page write.SpilledTensorHandle+share_memory_for_ipc/resolve_spilled_handles-- crossing process boundaries by descriptor. Pickling an mmap-backed tensor copies every byte back into/dev/shm, silently undoing the spill, so handles travel instead.release_page_cache/release_page_cache_by_path-- release the cgroup charge of written-once data (msync, thenMADV_DONTNEED, thenFADV_DONTNEED; skipping the middle step makes the call a silent no-op).is_tensor_spilling_enabled,is_disk_backed,disk_backed_handle,has_live_mapping.Placement decisions read
available_memory_bytes()from #756.Behaviour change
None when spilling is unset.
share_memory()keeps its-> Nonesignature; its one change is leaving an already disk-backed tensor alone rather than copying it into/dev/shm, reachable only with spilling on.Testing
32 tests pass;
tyandruffclean. The tests exercise the real filesystem: spill/re-map round trips, threshold and dtype refusals, handle pickling across processes, forcedposix_fallocaterefusal (both "no space" and "not supported") asserting memory fallback with no leftover file, preshared allocation asserted ondata_ptrstability acrossshare_memory_(), and page-cache release asserted on measured page residency (mincore) rather than return values.Open questions
share_memory.pybecauseshare_memory()itself must recognise disk-backed tensors; a separate module importing the registry check is workable if a ~1k-line utility file is unwanted.mp.spawnboundary, but a config field mapped to them may be the friendlier surface.