Drive safetensors load by the target sharding with bounded concurrent ranged reads#3455
Open
mridul-sahu wants to merge 5 commits into
Open
Drive safetensors load by the target sharding with bounded concurrent ranged reads#3455mridul-sahu wants to merge 5 commits into
mridul-sahu wants to merge 5 commits into
Conversation
Restructure the safetensors loader around the target sharding: per host, resolve the byte runs each local-device shard needs from the file blob, coalesce neighbouring runs per file, and serve each coalesced block as a ranged read. An over-read ratio caps block_size/needed_bytes so inner-dim (row-parallel TP) shardings don't amplify cross-host egress to N*file_size; leading-dim FSDP stays one contiguous read per host. Bound peak in-flight memory by the shared restore budget (MemoryOptions.read_concurrent_bytes) rather than a per-block cap: one limiter across every file's scheduler streams oversized blocks in budget-sized chunks, so host memory beyond the destination shard buffers is bounded regardless of block, shard, or file size. Expose the loader tuning knobs (max_over_read_ratio) on SafetensorsOptions and the load benchmark, add a TP ratio-sweep config, and extend the single- and multi-process layout tests to cover the sharded read paths.
…r file Replace the per-file scheduler and its cursor-callback contract with a pure read plan: byte runs carry their destination buffer views and every chunk's buffer copies are precomputed, so chunks are order-independent. Blocks larger than a fixed chunk size (128 MiB) are now fetched as parallel ranged reads under the same shared in-flight byte budget, instead of streaming sequentially at single-stream throughput -- the single-host whole-file case especially. Read all file headers (and length checks / stats) concurrently instead of one round trip at a time, and assemble each file's tensors as soon as its reads complete, overlapping host-to-device transfer with the remaining reads and releasing host buffers earlier.
The generator referenced the pre-rename singular module path and the fixture URIs predate the st_fixtures/ convention; the sweep failed at generator import. Also point the pair reference at mistral7b_tp.yaml.
The 128 MiB chunk size that splits coalesced blocks into concurrent ranged reads was a module constant; the right value is storage-backend dependent (request-rate quotas and per-request overhead vs parallel stream throughput). SafetensorsOptions.read_chunk_bytes overrides it, clamped to the in-flight budget as before, and the load benchmark gains a sweepable safetensors_read_chunk_mb knob to measure the tradeoff.
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.
Description
Rewrites the v1
SafetensorsLayoutload path to be driven by the target sharding: each process resolves which shards of each tensor its own devices need (Sharding.devices_indices_map), maps those index domains to byte ranges in the file, and reads exactly those ranges. Resharding happens purely through which bytes each process reads — there is no cross-process communication and no XLA compilation. This mirrors how the v0 array deserializer drives reads from the target sharding, with hand-computed byte ranges standing in for TensorStore's chunk index.Read planning. Byte runs from every tensor a process needs in one file are coalesced under a bounded over-read policy:
SafetensorsOptions.max_over_read_ratio(default 2.0) capsblock_size / needed_bytes, so inner-dim (e.g. row-parallel TP) shardings can't amplify per-host reads to N × file size, while a 4 KiB gap floor prevents strided reads from shattering into microscopic requests. Coalesced blocks are split at a fixed 128 MiB chunk size and all chunks — across all blocks and files — are issued as concurrent ranged reads, so large blocks fetch at parallel-stream throughput instead of one long single-stream read. Every chunk's destination-buffer copies are precomputed, so chunks may complete in any order.Memory bound. One shared
LimitInFlightBytes(reusing_src/serialization/limits.py, keyed offMemoryOptions.read_concurrent_bytes, 2 GiB fallback) throttles all in-flight chunks together: peak host memory beyond the destination shard buffers is bounded by the budget regardless of block, shard, or file size. Tensors are assembled intojax.Arrays per file as each file's reads complete, overlapping host-to-device transfer with the remaining reads and releasing host buffers progressively. File headers (and the fail-fast truncation check) are fetched concurrently across files.Generality. Anything
devices_indices_mapcan express now loads — inner-dim partitions, multi-axis partitions, N-D tensors — replacing the previous transient-array + collective-sum path, which required leading-dimension divisibility by the local device count and staged whole tensors on their owner hosts (the OOM risk tracked in its TODO).Behavior changes.
SafetensorsOptions.ignore_load_shardingis removed (its only users were its own tests; the converter it anticipated does not exist yet).inf/NaNpayloads (masks, sentinels) are legitimate.Observability. Per-host reads are reported via
jax.monitoring(/jax/orbax/read/safetensors/{bytes_read,num_reads,storage_reads}), and a one-shot warning fires when the over-read cap is what fragmented a load, pointing atmax_over_read_ratio.Tested with the single-process suite, a 4-process × 2-devices-per-process multiprocess suite covering the sharded read paths (including partial-replication, subset loads, and a peak-memory regression test), and the safetensors load benchmarks.
Type of change
Checklist
CHANGELOG.mdunder the[Unreleased]section (or the relevantcheckpoint//export/changelog).