Install an agent, watch the GPU-utilisation graph scroll by, feel informed. That is the default posture toward ML observability, and it is where most cloud-cost decisions quietly go wrong. A single utilisation percentage tells you almost nothing about whether you are wasting GPU-hours — and it certainly won’t tell you whether you’re wasting them the same way on AWS as on Azure. The useful question is not “what is my GPU doing right now?” It is “what decision is this telemetry supposed to inform?” For most teams staring at a rising cloud bill, that decision is concrete: should this workload run on a different provider, a different instance family, or a right-sized reserved commitment instead of on-demand? Observability tools earn their keep when their output feeds that decision. They fail — expensively — when they merely render pretty timelines nobody acts on. What “ML observability” actually means when GPU cost is the target The term gets stretched across three overlapping jobs: keeping a service alive, catching model-quality drift, and understanding resource efficiency. All three matter, but they are not interchangeable, and the tooling that does one well often does another badly. When the target is cloud cost, observability means one specific thing: producing a per-workload trace that separates the time a GPU spends doing useful floating-point work from the time it spends idle, blocked, or waiting on data. That distinction is the whole game. A generic infrastructure dashboard reports allocated capacity and averaged utilisation. If a training step is compute-bound for 40% of wall-clock time and stalled on data transfer for the other 60%, an averaged view shows something like “65% GPU utilisation” and calls it healthy. The 60% you paid for and got nothing from disappears into the average. That hidden idle time is exactly what inflates cost per useful FLOP, and it is invisible to anyone watching a single line on a graph. We see this pattern regularly in cost reviews: the billed GPU-hours are real, the utilisation graph looks respectable, and yet the effective rate — dollars per unit of actual computation — is two or three times what the hardware could deliver. The tool wasn’t lying. It was answering a different question than the one that mattered. Which ML observability tools surface GPU utilisation per workload phase? The tools worth naming here fall into layers, and you almost always need more than one. Nothing gives you the full picture from a single agent. At the device layer, NVIDIA’s DCGM (Data Center GPU Manager) exposes SM occupancy, memory-controller activity, PCIe throughput, and power draw as time-series metrics. Piped into Prometheus and Grafana, DCGM gets you past the single-percentage trap — you can at least see SM activity separately from memory-bus activity. But DCGM tells you what the hardware did, not which part of your code caused it. For that causal link you drop to the tracing layer. PyTorch Profiler and the underlying NVIDIA Nsight Systems timeline attribute GPU time to specific kernels and — critically — show the gaps between kernels where the device sat idle waiting on a cudaMemcpy, a data-loader stall, or a synchronisation barrier. Those gaps are the data-transfer and IO cost the averaged view swallows. Nsight Systems in particular makes the compute-bound versus IO-bound split legible as a timeline you can point at. At the experiment-and-serving layer, tools like Weights & Biases or an OpenTelemetry-instrumented inference server correlate utilisation with the workload’s own units of progress — steps per second, tokens per second, requests served. This is the layer that converts raw device metrics into something a cost model can use. Tool layer Example Answers Blind spot Device metrics NVIDIA DCGM + Prometheus/Grafana SM occupancy, memory activity, PCIe throughput, power No code attribution; no phase breakdown Kernel tracing PyTorch Profiler, Nsight Systems Per-kernel GPU time; idle gaps between kernels Heavy to run continuously; sampling-window bias Workload progress Weights & Biases, OpenTelemetry Utilisation correlated with steps/tokens/requests Depends on honest instrumentation of “useful work” The reason this is a table and not a ranking: there is no single winner. A team profiling before a provider commitment typically runs a tracing pass with Nsight Systems for the phase breakdown, then leaves DCGM running for sustained observation. The tools compose. Anyone selling you one dashboard as “full-stack observability” is selling you the averaged view under a nicer logo. How does observability data distinguish compute-bound time from idle transfer time? The mechanism is simpler than the tooling makes it look. Every GPU operation is either doing arithmetic (compute-bound), moving bytes to or from HBM (memory-bound), or waiting on something outside the device entirely — host-to-device copies over PCIe, a slow data loader, a network fetch (IO-bound). Nsight Systems and the PyTorch Profiler timeline draw these as distinct bands, so a phase where the SMs are near-saturated looks visibly different from a phase where the copy engines are busy and the SMs are parked. The interpretive skill is reading the gaps. A kernel that runs for 8 ms followed by a 12 ms window where nothing executes on the SMs is a 60% idle stretch — and if it repeats every step, you are paying for a GPU that is arithmetically busy 40% of the time. That idle window usually resolves to one of a small set of causes: a data loader that can’t keep the batch pipeline full, an un-overlapped host-to-device transfer, or a synchronisation point that serialises work that could run concurrently. This is the same terrain covered in the three pillars of observability applied to GPU utilisation, which breaks down how metrics, traces, and logs each contribute a different slice of that picture. In profiling passes we run, the phase split is often the single most surprising number for a team — not because the GPU is slow, but because the workload was never keeping it fed (observed across cost-review engagements; not a published benchmark). Turning observability output into a cost-per-useful-FLOP comparison Here is where telemetry stops being a dashboard and becomes a decision input. The cost that actually matters is not billed GPU-hours; it is billed GPU-hours divided by GPU-hours spent doing useful FLOPs. Two providers can quote nearly identical hourly rates for comparable silicon and still deliver very different effective rates once you account for the idle fraction your workload incurs on each. The comparison is a short arithmetic worked example: Worked example (illustrative assumptions). Say your profiled workload runs 1,000 wall-clock GPU-hours in a month. Nsight Systems shows the SMs are genuinely busy 45% of the time; the rest is data-transfer and loader stalls. That means 450 GPU-hours of useful work against 550 wasted. On-demand at, for example, $3.00/hr: $3,000 billed → $6.67 per useful GPU-hour. If right-sizing to a smaller instance family plus better data-loader overlap raises the useful fraction to 70%, the same useful work now needs roughly 640 billed hours: at $3.00/hr that is ~$1,920 → $3.00 per useful GPU-hour. If a reserved or spot commitment then drops the effective rate to ~$1.20/hr, the useful-hour cost falls further, toward ~$1.20. The numbers above are illustrative, not a benchmark — plug in your own provider quotes and your own measured useful fraction. The point is structural: the largest single lever is usually the useful-work fraction, not the sticker rate. And that fraction is only visible through phase-separated observability. This is precisely the comparison developed at length in AWS vs Azure for GPU workloads and cost per useful FLOP, which walks through how the same workload’s idle profile can differ between providers because of storage-throughput and network-topology differences, not just instance pricing. What telemetry to capture before committing to a reserved or spot instance Reserved and spot commitments lock you into an instance family for months. Committing on the strength of an averaged utilisation graph is how teams end up paying for a GPU tier they never saturate. Before you sign, the observability output needs to answer a fixed checklist. Pre-commitment telemetry checklist: Phase-separated utilisation — the compute-bound / memory-bound / IO-bound split from a tracing pass, not a single average. If you only have the average, you are not ready to commit. Steady-state versus warm-up — capture enough steps that startup and checkpoint spikes don’t distort the profile. A commitment is priced against steady state. Data-path throughput — storage read bandwidth and host-to-device transfer rates, because these determine whether a faster GPU would even help or just idle more. Peak-versus-sustained memory — the actual working-set high-water mark, so you don’t reserve a 80 GB card for a 24 GB workload. Per-provider profile — the same workload profiled on each candidate provider’s instance family, since the idle fraction is provider-specific. If observability can’t produce those five, the honest move is to stay on-demand until it can. Over-committing on bad telemetry is a more expensive mistake than a few weeks of on-demand rates. For the workloads sitting behind a managed API rather than a bare instance, the same discipline applies one layer up — see how GPU costs hide in machine-learning-as-a-service pricing for where the meter runs when you don’t control the instance directly. How a GPU Performance Audit uses this data A GPU Performance Audit consumes exactly this observability output — it is the input, not a separate exercise. The audit takes the phase-separated traces, isolates the largest idle contributors, and translates them into two artifacts: a right-sizing recommendation (which instance family actually fits the workload’s sustained profile) and a provider comparison expressed in cost per useful FLOP rather than sticker rate. The tools described in this article are what generate the profiled-utilisation data that makes an honest AWS-versus-Azure comparison possible in the first place. Without the traces, an audit is guesswork; with them, it is arithmetic. What observability tooling cannot tell you Being clear about the boundary matters, because over-trusting the tools produces its own class of bad decisions. Observability measures what your workload did, not what it could do on hardware you haven’t profiled. It won’t tell you whether a code change — better kernel fusion, overlapping transfers, a different batching strategy — would move the useful fraction, only that today’s fraction is low. It won’t price a provider you didn’t run the workload on. And it can’t distinguish “the GPU is idle because the workload is genuinely IO-bound” from “the GPU is idle because someone left a synchronous logging call in the hot path” — that judgement is human. There is also a measurement-cost caveat: full kernel tracing is heavy and can perturb the very timings you’re measuring, so continuous production tracing is rarely the right posture. The realistic pattern is periodic tracing passes for phase analysis plus lightweight DCGM metrics for sustained observation. FAQ What matters most about ml observability tools in practice? In practice, ML observability for cost means running a layered stack: device-level metrics (NVIDIA DCGM) for sustained SM and memory activity, kernel tracing (Nsight Systems, PyTorch Profiler) for per-phase attribution, and workload-progress instrumentation to tie utilisation to steps or tokens. The output that matters is a per-workload trace separating useful compute from idle time — not a single utilisation percentage. Which ML observability tools actually surface GPU utilisation per workload phase rather than a single averaged percentage? Kernel-tracing tools do the phase separation: NVIDIA Nsight Systems and the PyTorch Profiler draw compute-bound, memory-bound, and idle stretches as distinct timeline bands. Device-metric tools like DCGM piped into Prometheus/Grafana separate SM activity from memory activity but don’t attribute it to your code. You typically compose both; no single dashboard does the whole job. How does observability data distinguish compute-bound time from idle time spent on data transfer or IO? Every GPU operation is either doing arithmetic, moving bytes to/from HBM, or waiting on something off-device (PCIe copies, data loaders, network). Tracing tools render these as separate bands, so a saturated-SM phase looks visibly different from a phase where copy engines run and SMs are parked. The interpretive skill is reading the idle gaps between kernels and tracing them to a loader stall, an un-overlapped transfer, or a synchronisation barrier. How do I turn observability output into a cost-per-useful-FLOP comparison across AWS and Azure? Divide billed GPU-hours by GPU-hours doing useful FLOPs — the useful fraction comes from the phase-separated trace. Multiply billed hours by the provider’s rate, then normalise by useful work, and the effective rate emerges. The same workload can show a different idle fraction on each provider because of storage-throughput and network-topology differences, so profile it on each candidate family rather than comparing sticker rates. What telemetry do I need to capture before committing to a reserved or spot instance on either provider? Five things: phase-separated utilisation (not an average), steady-state versus warm-up separation, data-path throughput (storage and host-to-device), peak-versus-sustained memory high-water mark, and the same workload profiled per candidate provider. If your observability stack can’t produce all five, stay on-demand until it can — over-committing on averaged telemetry is more expensive than a few weeks of on-demand rates. How does a GPU Performance Audit use observability data to identify wasted GPU-hours? The audit consumes the phase-separated traces directly: it isolates the largest idle contributors, then produces a right-sizing recommendation and a provider comparison expressed in cost per useful FLOP. The observability tools generate the profiled-utilisation data; the audit turns it into a right-sizing and provider decision. What are the limits of observability tooling — what can it not tell me about effective cloud cost? It measures what your workload did, not what it could do on hardware or a provider you haven’t profiled. It won’t predict whether a code change would raise the useful fraction, only that today’s is low, and it can’t judge whether idle time is genuinely IO-bound or the result of a stray synchronous call. Full tracing is also heavy enough to perturb timings, so continuous production tracing is rarely the right posture. The real test of any observability setup is simple: does its output change a provider or instance decision this quarter? If the traces sit in a dashboard nobody consults before signing a reserved commitment, the tooling is decorative. The failure class to watch for is the averaged-utilisation illusion — a healthy-looking graph masking a 60% idle fraction — and the AWS-versus-Azure cost-per-useful-FLOP comparison is where that data finally does some work.