Skip to content

feat(execution): sample local VRAM peak via nvidia-smi during guarded execution - #265

Merged
toasterbook88 merged 3 commits into
mainfrom
feat/local-vram-sampling
Jul 27, 2026
Merged

feat(execution): sample local VRAM peak via nvidia-smi during guarded execution#265
toasterbook88 merged 3 commits into
mainfrom
feat/local-vram-sampling

Conversation

@toasterbook88

Copy link
Copy Markdown
Owner

Summary

Fills the 1A gap from the v0.14.7 optimization spec audit: `PeakVRAMMB` was previously only populated from remote execution responses — local execution on GPU hosts recorded zero VRAM. This adds real local VRAM telemetry.

Implementation

New `internal/execution/vram.go`:

  • `samplePeakVRAM(ctx, interval)` — starts a background goroutine that polls `nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits` at a fixed interval, sums across all GPUs, tracks the max total observed, and returns a `stop() int64` closure.
  • `queryTotalVRAMUsed(ctx)` — single-shot helper, returns `(totalMiB, ok)`; `(0, false)` if nvidia-smi is missing or output unparseable.

Wired into `runLocal` (`internal/execution/guarded.go`):

  • Sampler starts immediately before `runWithReservationHeartbeat`, stops immediately after.
  • `resp.PeakVRAMMB` is set on both success and failure paths.
  • Polling interval: 500ms (cheap vs. the seconds-to-minutes timescale of real workloads).

Degradation

On hosts without nvidia-smi (Apple Silicon, Intel-only, or PATH without the binary), `queryTotalVRAMUsed` returns `(0, false)` and the sampler's peak stays 0. No error surfaced — VRAM is best-effort telemetry, not load-bearing.

Verification

Check Result
`TestSamplePeakVRAMReturnsZeroWhenNvidiaSmiMissing` (PATH=`/nonexistent`) PASS — 0 peak, no error
`TestSamplePeakVRAMStopsCleanly` PASS
`TestQueryTotalVRAMUsedHandlesMissingBinary` PASS — (0, false)
`TestSamplePeakVRAMLiveOnGPUHost` on cranium (2x NVIDIA) PASS — captured 17,896 MiB peak over 500ms
Full `internal/execution` suite PASS
Full `internal/mcp` suite PASS (no regression)
`go build ./...` clean

Related

Follows #264 (MCP tools, 4B). The remaining audit gap (1B pairwise link matrix) will follow as a separate PR.

… execution

Fills the 1A gap from the v0.14.7 optimization spec audit: PeakVRAMMB
was previously only populated from *remote* execution responses; local
execution recorded zero VRAM even on GPU hosts.

Adds a background sampler (internal/execution/vram.go) that polls
nvidia-smi --query-gpu=memory.used at a 500ms interval while the local
workload runs, sums across all GPUs, and returns the max total observed.
runLocal starts the sampler right before runWithReservationHeartbeat and
stops it immediately after, setting resp.PeakVRAMMB on both success and
failure paths.

Degrades cleanly to 0 on hosts without nvidia-smi (Apple Silicon,
Intel-only, or PATH without the binary) — the sampler's query returns
(0, false) and the goroutine stops without error. VRAM is best-effort
telemetry, not load-bearing.

Verified:
- Unit tests pass on the no-GPU path (PATH=/nonexistent).
- Live smoke test on cranium (2x NVIDIA GPUs) captured 17896 MiB peak
  over a 500ms window.
- Full execution + mcp suites pass; go build ./... clean.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@toasterbook88
toasterbook88 merged commit ba7dab1 into main Jul 27, 2026
7 checks passed
@toasterbook88
toasterbook88 deleted the feat/local-vram-sampling branch July 27, 2026 19:10
toasterbook88 added a commit that referenced this pull request Jul 27, 2026
…266)

## Summary

Fills the **1B gap** from the v0.14.7 optimization spec audit: the spec
described a \`PairwiseLinkMatrix\` / \`LinkMetric\` data structure that
did not exist anywhere in the codebase. This adds it for real — a first
cut that measures what's actually measurable from a single vantage.

## Types (\`internal/models/types.go\`)

\`\`\`go
type PairwiseLinkMatrix struct {
    Links []LinkMetric
}
type LinkMetric struct {
    SourceNode     string
    TargetNode     string
    OverlayType    string        // lan | tailscale | thunderbolt
    RTTLatencyP95  time.Duration
    ThroughputMBps float64       // 0 = unmeasured in this cut
}
\`\`\`
\`ClusterSnapshot\` gains an optional \`Topology *PairwiseLinkMatrix\`
field (zero-value-safe; existing consumers unaffected).

## Builder (\`internal/facts/pairwise.go\`)

\`BuildPairwiseLinkMatrix(ctx, localName, nodes)\`:
- Probes directional RTT from the local node to each remote node's
\`ResolvedDialTarget\` (fallback \`SSHTarget\`) via \`ping -c 4 -W 1\`.
- Parses the **max** RTT from the ping summary line as a P95 stand-in (4
samples).
- Infers overlay from address shape: \`100.*\` → \`tailscale\`,
\`169.254.*\` → \`thunderbolt\`, else \`lan\`.
- **Single-vantage model**: only local→remote is probed; cross-remote
edges are not measured. Throughput left at 0 (unmeasured) in this first
cut — RTT is the primary signal.
- **Best-effort**: unreachable nodes or missing \`ping\` are omitted,
not fatal. No-local-node returns an empty matrix.

\`TopologySummary(m)\` helper for logging.

## Verification

| Check | Result |
|---|---|
| \`TestParseRTTP95\` (linux summary / no-summary / malformed) | PASS |
| \`TestInferOverlay\` (tailscale / thunderbolt / lan / 10.x / hostname)
| PASS |
| \`TestBuildPairwiseLinkMatrixNoLocalNode\` | PASS — empty matrix, no
panic |
| \`TestBuildPairwiseLinkMatrixSkipsLocalAndEmptyTargets\` | PASS — self
+ empty skipped |
| \`TestTopologySummaryEmpty\` / \`NonEmpty\` | PASS |
| \`TestBuildPairwiseLinkMatrixLiveOnCranium\` (loopback) | PASS —
captured \`cranium→loopback-target lan 27µs\` |
| Full \`internal/facts\` + \`internal/models\` suites | PASS |
| \`go build ./...\` | clean |

## Scope notes

- This is a **first cut**: throughput is unmeasured, and only
local→remote edges are probed. Cross-remote measurement would require
distributed probing (each node probing the others) — out of scope for a
single-PR addition, and the spec didn't require it.
- The builder is **not yet wired into the snapshot pipeline** — it's
available for callers but \`ClusterSnapshot.Topology\` stays nil by
default. Wiring it into \`axis status\` / daemon refresh is a deliberate
follow-up so this PR stays focused on the type + builder.

## Related

Follows #264 (MCP tools, 4B) and #265 (local VRAM sampling, 1A).
Completes the three real gaps flagged in the spec audit.

---------

Co-authored-by: AXIS Contributor <axis-dev@example.invalid>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant