Repository navigation
Conversation
On Linux, /metrics now reports VmRSS, RssAnon, RssFile, RssShmem and VmHWM as five process_resident_* gauges, in live and parked scrapes. The reader uses a 4 KiB stack buffer and doesn't allocate. A count the kernel doesn't report is left out, never sent as zero. The macOS footprint gauges don't change.
|
Thanks, @nuk3s. This is well scoped, and the TP=2 GB10 investigation in the description makes the case for it clearly.
Against This will land shortly after the 1.1.7 release. Thank you! |
|
Landed on main as 49e8b84, with a changelog line. Thank you! |
|
Thank you for merging it so quickly! |
|
This is in TensorFold 1.1.8, released today. Thank you, @nuk3s! |
What this changes
On Linux,
/metricsreports no process memory at all, so you can't see the server's own host memory grow. On a GB10 that matters, because the CPU and GPU share one memory pool. Host memory a rank adds counts against the same limit as the weights and the KV pool.This PR reads
/proc/self/statusat scrape time and adds five gauges to/metricsand/v1/metrics:tensorfold:process_resident_bytesVmRSStensorfold:process_resident_anon_bytesRssAnon: heap, stacks and private mapstensorfold:process_resident_file_bytesRssFile: the binary, libraries and mapped filestensorfold:process_resident_shmem_bytesRssShmem: System V shared memory, tmpfs maps and shared anonymous mapstensorfold:process_resident_peak_bytesVmHWM: the kernel's lifetime peakWhat's in it:
zig/src/server/process_resident.zig(new): reads the file into a 4 KiB stack buffer, no allocation.zig/src/server/metrics.zig:residentGaugeswrites the gauges afterprocessGauges, in the live and the parked scrape.docs/memory-diagnostics.md: lists the new gauges.The macOS footprint gauges don't change.
Why
I run DeepSeek-V4.1-Flash at TP=2 on two GB10s. Rank 0 and rank 1 are the two engine processes, one per GB10. Twice, a memory watchdog outside the engine killed rank 0, 29 and 40 minutes after ready, and nothing told me which part of its memory grew. With this reader in my fork build it was right there:
RssShmemrose 512 MiB andRssAnon420 MiB in the 12 minutes after ready. TheRssShmemjump lines up with NCCL's pinned buffers for its RDMA connections.Receipt
No inference path changes, so exactness and speed are n/a. The server tests go from 796 to 803, the
metrics.zigmodule alone from 11/11 to 18/18, and the goldens don't move. On a real scrape each gauge was within 0.2 MiB of/proc/<pid>/status, and on a GB10 the/metricsvalues matched an outside sampler.Full test details and what I didn't cover
More on the change:
process_resident.zigsits next toprocess_memory.zig, which keeps the macOS footprint reader.process_footprint_bytesandprocess_footprint_peak_bytesstay macOS only. The resident set has no device or driver memory, so the help text and the doc say it isn't a physical footprint (CUDA: retain pinned peaks and scoped process memory receipts #865)./health,/memoryand the macOS output don't change.More on why:
device_memory_bytesandpinned_memory_bytes(Report the CUDA backend's pinned host memory in /metrics #541).tools/memory_receipts.pyreadsVmRSSandVmHWM, but only for a child process it starts, and without the split. The growth only shows up in/proc.mainis still experimental (DeepSeek V4.1: the lane backend — one stream per slot on the lane core #827), so this ran on bertholomus/TensorFolddeepseek-v41-zigat629c524a.MemAvailableon rank 0's machine went from 4.8 GiB at ready to 2.5 GiB at the kill.psshowed rank 0's RSS at 4.0 and 4.3 GiB at the two kills, but nothing showed the RSS at ready.RssShmemjump happened inside one 30 s sample, at the first KV transfer between the ranks, when NCCL connected new queue pairs (its RDMA connections). GPUDirect RDMA was off, so NCCL's buffers were pinned host memory.Environment:
mainat8c36359d, and this branch at462e50de. x86_64 Linux (Fedora 43, kernel 7.2), Zig 0.17.0, no GPU. Alsoaarch64-linux-muslunderqemu-aarch64-static.Exactness: n/a. No inference path changes. The reader only runs on a scrape.
Decode speed and prompt speed: n/a, same reason.
Tests run. Each command's result on
mainand on this branch:8c36359d)462e50de)zig build test-server-cpu --summary allzig build test-golden --summary allmetrics.zigmodule alone (zig testwithengine_apiandlanes)aarch64-linux-muslunder qemuaarch64-macos,x86_64-macosandx86_64-windows-gnu,--test-no-execpython3 zig/tests/server/test_memory_http.pyon the CPU fake serverpython3 tools/zig/lean_check.py, thenlean_new.pyzig fmt --checkandgit diff --checkare clean.process_resident.zig: status lines parse to bytes; a kernel with 64 KiB pages still reports kB; a missing line, another unit, a bad number or au64overflow is unknown, never zero; a repeated line keeps its first value; this process's own status reads and parses. Inmetrics.zig: the gauges carry only what the kernel reports and no footprint; on Linux a live and a parked render carry them and noprocess_footprint_bytes; off Linux neither render has them.VmRSSsent asprocess_footprint_bytesfails 2 of 18 module tests, dropping theprocess_resident_bytesline fails 2 of 18, and a parked scrape without the gauges fails 1 of 18. A parser that takes any unit fails 1 of 5, and so does one that keeps the last repeated line./metricsand/v1/metricshave the five gauges and noprocess_footprint_bytes. Each value was within 0.2 MiB of the server's/proc/<pid>/status, read right after the scrape./proc/<pid>/statusevery 30 s, and the/metricsrow is a scrape in between. Values in MiB:/proc/<pid>/status/metrics/proc/<pid>/statusNot run, and why:
main: I didn't serve a model on a GPU with this branch. The GB10 numbers come from the fork build./metrics, so these gauges don't cover its memory.device_memory_bytes, so these gauges can't show growth there.Checklist
"draft": false, a resumed prompt equals a fresh one, and concurrent equals solo. No inference path changes.SUMMARYline is in the receipt. n/a: no GPU kernel changes.