UMBP storage and transfer simulation
Simulation keeps allocation, placement, commit/abort, lookup, LRU, leases, events, tier policies and control RPCs. Payload storage/transfers are virtual; sim is off by default.
Enable
Keep existing capacities, policies and framework buffers; add the sim settings:
from mori.umbp import UMBPClient, UMBPConfig
config = UMBPConfig.from_environment()
config.sim.enabled = True
config.sim.profile_path = "/path/to/profile.json"
config.sim.trace_path = "/tmp/umbp-worker-0.jsonl"
client = UMBPClient(config)
client.put_from_ptr(key, ptr, nbytes)
client.get_into_ptr(key, ptr, nbytes)
With UMBPConfig.from_environment(), the equivalent settings are UMBP_SIM_ENABLE=1,
UMBP_SIM_PROFILE=/path/to/profile.json and UMBP_SIM_TRACE=/tmp/umbp-worker-0.jsonl.
Existing APIs and return types remain available. Use a separate trace file per client/run.
DRAM, HBM and file-backed SSD tiers are supported. Embedded mode defaults to DRAM;
select SSD through the existing backend policy or distributed medium configuration;
ssd.enabled alone does not select SSD. Virtual GPU callers require location/device
metadata via C++ RegisterMemory; unregistered real pointers use existing classification.
Virtual HBM needs no GPU, but normal build dependencies, including HIP, still apply.
SSD uses the existing SsdBackend, PeerSsdManager, segment index and multi-drive
placement. Its files and staging payloads are virtual: storage_dir identifies
shards but no directories/files are created. Capacity includes record headers,
4 KiB padding and dead records; deleting a key frees disk capacity when its whole
segment becomes reclaimable. Commit/abort, LRU, staging limits, read leases and
single-flight reads keep their normal behavior. Simulation starts with an empty
tier and does not restore persisted data. SPDK backends are outside this file-tier
implementation.
Standalone / CI
Run one server per node; its capacity, page size, LRU and policies apply to all ranks:
export UMBP_SIM_ENABLE=1 UMBP_SIM_PROFILE=/path/to/profile.json
export UMBP_SIM_TRACE=/tmp/umbp-server.jsonl
umbp_standalone_server unix:/tmp/umbp.sock
Ranks keep their standalone_process config and socket, enable config.sim.enabled,
and use distinct trace files. Forwarding ranks need no profile. For auto_start=True,
set the profile in the client; the server receives it and uses trace suffix .server.
Both sides need the updated build and matching sim mode. Range checks remain active;
closing a rank releases registrations while other ranks retain the shared KV tier.
Rank timings include IPC and server work; do not add server trace times to rank times.
Profile and model
sim_profile.json contains illustrative, uncalibrated
estimates for cpu_cpu, cpu_gpu, gpu_cpu, gpu_gpu, remote_read, remote_write,
file_cpu, cpu_file and file_gpu.
Copy it and adjust the coefficients for your platform.
Each path names its source/destination memory type, except the remote paths, which use
the initiator’s read/write direction. Configure every path your workload uses; a missing
path rejects the transfer. Unused paths may be omitted.
This first version uses manually supplied parameters. Automatic calibration belongs in
a follow-up PR covering the actual engines (LocalCopyEngine, HbmCopyEngine and
MoriIoEngine), with reusable profiles for CPU, GPU and RDMA transfers. SSD coefficients
also need measurements of the actual configured storage path.
For staged SSD load/offload, optional file_cpu_staged / cpu_file_staged describe storage-path cost;
normal CPU/GPU/network transfers add their own costs. Fit those SSD coefficients
to SSDTier device I/O plus its skipped payload work (record building, CRC, bounce
copies and the configured durability mode), excluding control and external
GPU/RDMA copies that the simulator already counts. Local direct file transfers use
file_cpu / cpu_file; profiles without staged variants fall back to those curves.
file_gpu models the opt-in GDS read path
(UMBP_ENABLE_GDS=1); GPU offload remains GPU→DRAM→SSD, matching the real engines.
Checksum-bearing whole reads use file_cpu_staged. Checksum-free records and
partial reads prefer file_cpu_staged_no_crc, which models the same SSDTier I/O
backend without CRC work. Older profiles fall back to file_cpu, then the staged
curve for these reads; whole reads fall back to file_cpu.
With CRC disabled, calibrate the staged profile with the same setting.
The example SSD values are estimates, not calibration results.
Field |
Meaning |
|---|---|
|
Same names share a service queue within one backend client/server. |
|
Nonnegative fixed cost per submitted batch/path. |
|
Nonnegative cost per coalesced copy; default 0. |
|
Optional nonnegative first-FD cost for |
|
Optional nonnegative SSD read cost per copy × |
|
Optional nonnegative SSD read cost per batch × the same size term; default 0. |
|
Optional SSD read flag to queue latency as well as bytes, for mutually exclusive I/O batches; default false. |
|
Positive effective bandwidth. |
cpu_cpu coefficients may be arrays indexed by copy threads minus one, clamped to the last
entry; other paths use scalars. Existing thread selection/thresholds still apply.
Channel capacity is the maximum measured bandwidth; independent callers can use
more cores than one call’s copy/write thread limit. Each call retains its thread curve.
size_term = log2(max(1, bytes / copies / 4096))
overhead = latency_ns[p] + copies * copy_latency_ns[p]
+ size_term * (copies * log_size_latency_ns + batch_log_size_latency_ns)
private_ns = overhead + bytes * 1e9 / bandwidth[p]
service_ns = overhead + bytes * 1e9 / channel_capacity
channel_available = max(submitted, channel_available) + service_ns
finish = max(submitted + private_ns, channel_available)
Shared paths sum service costs; zero bytes add no delay and missing paths abort normally.
For independent SSD reads, service_ns includes only the byte cost so request
latency can overlap, unless serialize_latency is true. Writes and mixed read/write
submits retain the full service cost.
Pure file_gpu submits with serialize_latency=false share channel capacity among
active synchronous copies. Copy/log latency stays private; fixed batch/log latency
is paid once, using the same five coefficients. Ordinary reservations on that
channel form a barrier; mixed submits retain the formulas above. File registration
timing is unchanged.
Native CRC reads with a matching serialized no-CRC curve on the same channel queue the smaller of the two costs as I/O, then finish the remaining CRC cost outside that queue. Missing or incompatible curves retain full serialization.
cpu_file_staged uses the complete payload curve: record preparation, I/O,
durability and record-buffer cleanup are included in its measured total cost.
The basic model reserves that combined cost on the SSD channel; it does not model
preparation or cleanup overlap between callers separately.
Cross-peer sharing
For share, keep normal Master/peer configuration, distinct node IDs and reachable
addresses/ports. Use a dedicated Master, enable sim on every peer, and configure
remote_read/remote_write from the initiator’s perspective. Separate channel names
allow independent directions. Fit target-link Submit+Wait, not API totals or ping
RTT when replacing estimates with measurements. The example GPU/network coefficients
remain uncalibrated; use the separate calibration workflow for platform measurements.
Local copies finish in Submit; registered remote copies finish in Wait. Unregistered
CPU callers retain RDMA bounce geometry and serial staging inside Submit. Ranged
scratch and re-cache payloads are virtual; a re-cached replica can serve other peers.
Staging uses optional scalar cpu_staging / cpu_staging_read coefficients calibrated
from MoriIo’s serial copyin/copyout geometry. Reads fall back to cpu_staging, then
the single-thread cpu_cpu estimate. Validate staged timings separately from local copies.
Sim/real engines reject each other’s handshake. Distributed ranged I/O still needs scratch.
Remote GPU callers retain real registration restrictions; standalone GPU IPC is local-copy-only.
Timing and limits
JSONL fields are api, key_count, requested bytes (range bytes for ranged calls),
monotonic start_ns/elapsed_ns, result_count and exception. Results count successes/
matches; void calls and exceptions use -1. Records cover scalar/batch/depth/ranged
put/get, existence, register/deregister, clear, flush and close. Completion order may
differ. Files append and flush on flush/close/destruction; an empty path uses INFO
logging, and disabling INFO skips formatting.
elapsed_ns includes real control code, RPC/locks, queueing and modeled transfers,
ending before trace serialization; whole-API timing also includes trace output.
Workers/timers use wall time; background work counts when it delays an API/channel.
Queues belong to one backend client/server, shared by its ranks. Remote-NIC aggregation, switch congestion and dynamic fair sharing are not modeled.
Mixed transfer paths on different channels may overlap in the model; real inline engines can serialize them. Validate mixed-tier batches separately.
Real control RPC failures remain possible; modeled payload-transfer failures are not synthesized. Same-host peers measure that host’s RPC cost.
Scheduling/control overhead limits short-operation accuracy. Profiles depend on platform, concurrency and workload.
Virtual addresses and page/key metadata consume resources. Framework buffers, external copies and model execution remain real; use trace/dummy execution, not generated-token correctness checks.
GPU/network accuracy and full SGLang performance validation require separate work.
Build test_umbp_sim and run ctest -R '^umbp_sim$'; these functional tests need no GPUs.