server : expose per-device memory usage on /metrics and GET /memory#26130
Open
tobocop2 wants to merge 1 commit into
Open
server : expose per-device memory usage on /metrics and GET /memory#26130tobocop2 wants to merge 1 commit into
tobocop2 wants to merge 1 commit into
Conversation
|
Hi @tobocop2, thanks for your contribution! Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:
Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below. |
4 tasks
Author
Updated |
tobocop2
force-pushed
the
feat/server-memory-metrics
branch
from
July 26, 2026 18:06
8872ce9 to
390816c
Compare
tobocop2
force-pushed
the
feat/server-memory-metrics
branch
from
July 26, 2026 18:31
390816c to
6cb47f2
Compare
tobocop2
force-pushed
the
feat/server-memory-metrics
branch
from
July 26, 2026 19:07
6cb47f2 to
d1fc256
Compare
- split the per-device aggregation out of common_memory_breakdown_print() into common_memory_breakdown_get(), and fold --fit's duplicate of it into the same function - report it as device-labelled gauges on /metrics, plus a model_n_layer gauge - add GET /memory returning the same rows plus n_layer as JSON, behind a new --memory flag - report the multimodal projector's per-device memory (mmproj) on both surfaces via a new mtmd_get_ctx_memory_usage() - account the context's output buffer in the memory breakdown, previously omitted - fix the metrics writer rounding integers past 6 significant digits through double coercion - escape backslash, quote and newline in metric label values per the exposition format
tobocop2
force-pushed
the
feat/server-memory-metrics
branch
2 times, most recently
from
July 26, 2026 20:42
7057524 to
e38bb2f
Compare
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.
Overview
Closes #26129
llama-server now reports its memory usage: how many bytes it allocated on each GPU (and in host RAM), split into model weights, context/KV cache, and compute buffers - plus each device's total and free memory.
Two surfaces over the same data:
/metricsfor Prometheus/Grafana, andGET /memoryfor JSON consumers.--metricsenables the Prometheus surface; a new--memoryflag enables the JSON one (the--slotspattern: one flag per endpoint).The server has always known these numbers - it prints them at startup and shutdown - but never exposed them anywhere a program can read. I run several llama-server instances sharing a set of GPUs and need to know what each one actually allocated on each card. Today the only way is scraping log lines, which breaks whenever the log format changes.
1.
common: made the per-device summary usable on its own.The numbers were computed inside the function that prints the shutdown table, so the only way to get them was as text.
They now come from
common_memory_breakdown_get(): one row per device, plus one for host memory. The print function just renders those rows - same table as before.--fit's memory projection had its own copy of this aggregation and now uses the same function.One accounting fix along the way: the context's output buffer was missing from the breakdown (and so from the printed table). It's counted now.
No new use of the staging header
src/llama-ext.h.2.
server: the breakdown on/metrics(Prometheus) andGET /memory(JSON), plus the model's layer count and the multimodal projector's memory.{ "n_layer": 12, "data": [ { "name": "MTL0", "model": 83349984, "context": 0, "compute": 22559744, "total": 22906503168, "free": 22800187392 }, { "name": "Host", "model": 13191648, "context": 500640, "compute": 3684352 } ] }The
devicelabel /nameis the backend's own device name - the sameCUDA0/MTL0names that--list-devicesprints and--deviceaccepts./metricsstays behind--metrics./memorygets its own--memoryflag, advertised on/propsand refusing with 501 when disabled, like the other optional endpoints.The JSON response is an object so fields can be added later without breaking clients -
n_layernext todatais the first.Both endpoints reuse the existing internal metrics task - no new task type.
/slotsuses that task too and skips the device query.With a multimodal projector loaded, rows carry its per-device memory too - via a new
mtmd_get_ctx_memory_usage()that reads the live clip contexts instead of re-estimating from the file. Real output,tinygemma3on Metal:{ "n_layer": 8, "data": [ {"name": "MTL0", "model": 40707072, "context": 510656512, "compute": 165136416, "mmproj": 2181120, "total": 22906503168, "free": 22187376640}, {"name": "Host", "model": 35660288, "context": 4195328, "compute": 152064032, "mmproj": 12288} ] }3. Bug fix the new metrics ran into:
/metricsrounded large values.The exporter formatted every value as a
double, which silently rounds anything past 6 significant digits. Byte counts made it obvious:This was already quietly affecting
prompt_tokens_totalpast a million tokens. Integers are now emitted exactly; float metrics (the throughput averages) keep their old formatting.# HELP/# TYPEare also emitted once per metric name now that one name can carry several{device=...}series.Additional information
Using it
Grafana / Prometheus, straight off
/metrics- graph VRAM per card, or alert on headroom:Python, off
GET /memory:C++, from anything that links
common:Verification
llama-server,llama-cli,llama-perplexity,llama-bench,llama-fit-paramsllama-server/metricsvs the shutdown breakdown table, same runGET /memoryJSONtools/server/tests/unit/test_metrics.py(9 tests, incl. a vision model exercisingmmproj)/metrics+/memory+ embeddings, and again withtinygemma3(mmproj path)/metrics+/memory, embeddings, andtinygemma3(mmproj path)/metricswakes a sleeping server (--sleep-idle-seconds)Metal cross-check (
nomic-embed-text-v1.5.Q4_K_M):/metricsRunPod A40 (CUDA 12.4) cross-check, same model:
/metricsGET /memoryon the pod (captured one revision earlier - with the output-buffer accounting,Host.contextadditionally reports the ~0.5 MiB output buffer, as in the Metal example above):{ "n_layer": 12, "data": [ { "name": "CUDA0", "model": 70170624, "context": 0, "compute": 22559744, "total": 47708110848, "free": 47301066752 }, { "name": "Host", "model": 13191648, "context": 0, "compute": 3684352 } ] }A CPU-only run on the same pod (GPU masked) returns the host-plus-buffer-type shape (
HostandCPU_REPACKrows, nototal/free).Prebuilt Binaries
Prebuilt binaries of this PR for all platforms (built with the repo's own release workflow on my fork): https://github.com/tobocop2/llama.cpp/releases/tag/build-memory-metrics-release-b10139-b11c3a9
Notes
n_layerrides the memory surfaces because layers are the unit of memory planning (-ngl).--devicetakes. Label values are still escaped per the exposition format, since RPC device names embed the user's endpoint string.Related demand: #23926 (users asking for these numbers after the load report moved out of the default log level).
Requirements