OWL Measurement Methodology
How OWL measures the energy cost of compute tasks — and what it doesn’t measure.
Scope
OWL measures what happens inside one machine when it performs a real task. This is intentionally narrow. The energy cost of streaming is distributed across data centres, networks, and consumer devices — each with different measurement challenges and attribution problems. We start with the layer we can measure directly, at the wall, with no modelling assumptions.
This scoping decision means OWL results are not lifecycle assessments and should not be cited as total-cost-of-delivery figures. They answer a specific question: how much additional energy does this server draw to perform this task, above its idle baseline?
One deliberate extension exists: since 2026-07 the findings catalog also carries client-device decode panels measured on a separate rig under the same protocol; each states its own device scope (see Test Types).
Measurement Principle
OWL uses wall-power delta measurement: the difference between what the server draws at idle and what it draws under load, captured by an external smart plug.
OWL is the bench half of GoS’s dual-track methodology: its sister programme REM (Remote Energy Measurement) observes real devices in the field at fleet scale — where effects exist — while OWL quantifies why, and by how much, under controlled conditions. Both tracks share one instrument principle, documented by GoS’s LEM (Local Energy Measurement) programme: real hardware running real workloads, measured externally at the wall by a smart plug read over the local network — milliwatt-resolution readings at second-scale cadence, no software estimation.
Measurement Protocol
Every test in OWL — video, LLM, image generation, RAG — follows the same core protocol:
-
Focus mode. Suppress background system tasks (apt, cron, man-db, fwupd, etc.) that would introduce energy noise. Managed via
systemctl stopwith dedicated sudoers rules. -
Model unload (LLM/RAG only). Send
keep_alive=0to Ollama and wait 3 seconds for GPU memory release. Ensures a cold start when cold-inference mode is selected. -
Baseline capture. Poll the Tapo P110 at 1-second intervals on each of two staggered meters for a configurable period (currently
5polls — configurable in Settings). The mean of these readings becomes Wbase — the server’s idle power draw. -
Lock. Acquire
/tmp/gos-measure.lockto prevent concurrent measurements from overlapping. A FIFO queue manages waiting jobs. - Execute task. Run the actual workload (ffmpeg, Ollama inference, SD-Turbo diffusion) while continuing to poll the Tapo P110 at 1-second intervals on each of two staggered meters. Thermal sensors (CPU Tctl, GPU junction, GPU PPT) are read in parallel.
- Compute energy. Calculate delta power, total energy, and per-unit metrics (see formulas below).
- Persist. Write the full result to a JSON file — parameters, energy report, raw poll data, thermal readings, confidence flag. Every result is reproducible and exportable.
- Focus exit. Restart suppressed system timers in parallel (via ThreadPoolExecutor) to minimise downtime.
Between sequential runs (e.g., CPU vs GPU comparison), a configurable cooldown (currently 10 seconds — configurable in Settings) allows the system to return to thermal equilibrium.
Energy Calculation
where Δt = task duration in seconds
All formulas use wall-power from the P110 (system-level), not component-level readings. The GPU’s self-reported power (its vendor sensor — amdgpu PPT or nvidia-smi power draw) is captured for reference but is not used in the primary energy calculation — it covers only the GPU die/board, not the full system delta (CPU, RAM, drives, fans, PSU losses).
Marginal vs attributional energy — two lenses, one measurement
The headline ΔE above is marginal accounting: it answers “how much extra energy did this task cause, on a machine that was running anyway?” The idle floor is subtracted, so the task is never charged for occupying the machine. That is the honest lens for a shared, always-on box — and it is deliberately conservative for comparisons, because it cannot be inflated by a high idle floor.
There is a second, equally honest lens. If the machine exists to run these tasks — a dedicated encode fleet is the canonical case — then the full bill per task includes the idle power the machine burns while the task holds it open:
Both figures come from the same samples — the attributional one is derived, not separately measured. The choice between them is a scoping decision, stated openly, not a correction: marginal for “what did this task add?”, attributional for “what does a task cost on hardware dedicated to it?”. The distinction only matters when the compared tasks take different amounts of time. Real-time playback is immune (every codec occupies the device for exactly the video’s duration); faster-than-real-time work — VoD encoding above all — is where it bites: a slow software encode holds a whole machine open for minutes that a hardware encoder releases in seconds, so attribution adds far more idle energy to the slow row and widens the absolute gap between them (the “race to idle” effect). On this bench it roughly doubles CPU-encode figures while adding only a fraction to the seconds-long hardware encodes. Encode-parity rows now carry the attributional figure alongside the marginal one (wh_per_min_video_attributional), plus their per-row idle baseline. Earlier stored results can be recomputed under this lens: standard result envelopes persist their idle baseline and task duration; older parity artifacts, which stored only the delta, are recomputed against the documented idle floor of their hardware era and labelled as such.
Isolating the encoder — transcode vs encode
Every video figure above is the energy of a full transcode — ffmpeg decodes the source, converts colour space, scales, then encodes — not the encoder in isolation. For most comparisons that is the honest number (you cannot encode without first decoding), and when the input is held constant the decode cost is a near-constant offset that cancels out of the comparison. Where the encoder’s own share is wanted — currently in the REM file-prep flow (/prepare-rem) — OWL runs a second, decode-only pass under the identical protocol (the same source decoded and discarded to a null sink, no encode) and subtracts it:
All three figures — transcode, decode, and the derived encode — are reported side by side, so the attribution is shown rather than asserted.
transcode − decode agrees with the directly-isolated encode to within about ±5% across every content/codec cell, with a slight tendency to read a few percent low — the standalone encode-from-raw carries a little extra disk I/O, and the real transcode runs decode, scale and encode concurrently, so it costs marginally less than the parts measured apart (transcode < decode + encode). So the split is a sound estimate of pure-encoder energy, good to a few percent. (An earlier 30-second run pointed the other way; that was an artefact of the decode op being too brief for 1 Hz sampling — a reminder that the limit here is samples-per-task, not the method.) On the GPU path the probe is hardware-decode only, so scaling is counted under encode. The split signals its own uncertainty — consistent with the confidence framework below.Confidence Framework
Every OWL result carries a traffic-light confidence flag. Under the CR-028 Phase 2 model (designed with Tania Pouli), the flag answers one defensible question per run: can this run be distinguished from idle? It is a per-run confidence interval, not a fixed-watt rule of thumb.
We keep the raw per-poll power samples from both the baseline window and the task window, form a standard error on the measured power increase ΔW, then convert ΔW into a one-sided confidence that the task really draws above idle:
SEcalibrated = (variance_idle_pct/100 · Wbase) × √(1/nbase + 1/ntask)
SEper-run = √(σ²base/nbase + σ²task/ntask)
SEdrift = (variance_idle_drift_pct/100) · Wbase Confidence the task draws above idle confidencepositive = Φ(ΔW / SEfinal)
| Flag | Meaning | Criteria (defaults) |
|---|---|---|
| 🟢 | Repeatable — the task is almost certainly above idle, with enough samples to be reliable. | confidencepositive ≥ 95% and ≥ 9 task polls |
| 🟡 | Early insight — directional evidence; a longer run would strengthen it. | confidencepositive ≥ 80% and ≥ 4 task polls |
| 🔴 | Need more data — cannot yet be distinguished from idle. | below the yellow threshold |
SEper-run), takes the worst case against a calibrated idle floor (SEcalibrated), and adds a drift term for the time gap between the baseline and task windows — so it reflects real signal quality on the day, not an assumed noise floor. The minimum task-sample counts remain because 1 s power samples are autocorrelated: a very short task should not turn green on one or two lucky readings.
variance_idle_pct as the calibrated idle noise floor. The per-codec calibration CVs (variance_cpu_pct / variance_gpu_pct) are run-to-run repeatability measures, reserved for a future aggregate-confidence layer rather than mixed into the single-run formula. Planned refinements to the critical value and sample counts are listed under Open Questions.
variance_pct × Wbase), so historical runs keep their badge.
The confidence framework follows GoS’s broader principle: if it can’t be measured, it shouldn’t be asserted. A 🔴 result is not a failure — it’s an honest signal that the measurement instrument isn’t sensitive enough for that task. Publishing it transparently is more useful than hiding it.
Calibration integrity
The variance calibration runner (/variance/run) executes 20 pairs of H.264 CPU + H.265 GPU encodes with 50 seconds between them, and computes three coefficients of variation: idle (raw P110 baseline readings, captures system noise), CPU (run-to-run reproducibility of the CPU encode ΔW), GPU (same for GPU). Their mean becomes variance_pct.
The runner is hardened against silent encode failures: every ffmpeg invocation’s exit code is checked, only successful encodes contribute ΔW, and per-side failure counters are tracked. If ≥50% of either side fails, the runner refuses to update settings — the result JSON is still returned (with cpu_failed, gpu_failed, failure_stderr, abort_reason fields) for forensics, but variance_pct stays unchanged on disk. This protects against the failure mode where partial-encode ΔW values contaminate the calibration without the operator noticing.
Diagnostics & Pre-calibration
Two layers of measurement-discipline tooling sit alongside the calibration:
Thermal-recovery probe
Before trusting a calibration result, the system needs to know that variance_cooldown_s is long enough — the idle samples taken between encodes must come from a thermally recovered system, not from the tail of the previous workload. The bin/probe-thermal-recovery diagnostic characterises this empirically. For a sequence of distances d after each of a CPU and a GPU encode (defaults: 0, 2, 5, 8, 12, 18, 25, 35, 50, 70, 95, 120 seconds), the probe samples idle power for 8 polls and writes the mean / std / CV to a CSV under results/diagnostics/.
On the GoS1 hardware the recovery is fast (see chart above): post-CPU and post-GPU baselines converge to the settled idle floor by d = 5–8 s with within-window CV around 1–2.5%. So the configured cooldown of 50 seconds is comfortably more than necessary — useful as a margin, not as a correction.
The same curve is also on the Settings page (lab access), where it refreshes live from the probe endpoint. Each probe run overwrites nothing — it leaves a fresh timestamped CSV pair under results/diagnostics/ so historical curves can be diffed if hardware or thermal conditions change.
Why the probe matters
The probe was the seam that exposed the scale_vaapi leak (the GPU encode failed within 90 seconds of starting the diagnostic) and the silent-failure path in the calibration loop. Generalisable lesson: measurement code should fail loudly, not interpolate around brokenness. It now runs queue-aware from the Settings page (/precalibration/run, lab access) as well as the CLI.
Hardware Disclosure
All results are tied to specific hardware. Different CPUs, GPUs, RAM configurations, and PSU efficiencies will produce different numbers. OWL results should always be cited with their hardware context.
| GoS1 | GoS2 | |
|---|---|---|
| Role | Tower server (since 2025): hosts the public site, the decode rig and its clip origin | Apple Mac mini (since October 2026): second bench, co-located; built to run on its own or elsewhere |
| Machine & OS | Custom build — Ubuntu 24, kernel 6.17 | Mac mini Mac18,5 — macOS 27.0.1, headless (HDMI dummy plug) |
| CPU | AMD Ryzen 9 7900, 12 cores / 24 threads, 65W TDP | Apple M6, 12-core CPU (2 super + 4 performance + 6 efficiency cores) |
| GPU & media | NVIDIA GeForce RTX 5080, 16 GB — NVENC (H.264, HEVC, AV1) + CUDA | 12-core Apple GPU on the same chip — media engine for H.264 and HEVC (VideoToolbox); no AV1 encoder + Metal |
| Memory | 61 GB DDR5 | 24 GB unified (shared by CPU and GPU) |
| Storage | 500 GB NVMe (OS + working set) + 4 TB NVMe (media & results, /srv/data) | 512 GB internal SSD |
| Idle power | ~79W at the wall, display blanked (~101W with an active desktop). The mid-2026 RTX 5080 swap raised idle ~+20W over the prior AMD 7800 XT (~57–59W) — intrinsic to the larger card | ~1.3W at the wall (inner meter, settled) |
| Meters | Inner P110 fw 1.3.1 (earthed variant) · outer P110 fw 1.4.0 | Inner P110 fw 1.3.1 (earthless variant) · outer P110 fw 1.4.8 |
| Video | ffmpeg current master build (ships the NVENC encoders + scale_cuda) — libx264, libx265, libsvtav1 (CPU); h264_nvenc, hevc_nvenc, av1_nvenc (GPU) | ffmpeg 9.0.2 (Homebrew) — libx264, libx265, libsvtav1 (CPU); h264_videotoolbox, hevc_videotoolbox in VBR (media engine) |
| LLM | Ollama 0.20.2 — model ladder ~1B–20B, CPU + CUDA GPU (live panel on /llm); Qwen3 4B is the canonical RAG model | Ollama 0.35.1 (Metal) and MLX (mlx-lm 0.32.0) — Qwen3 1.7B / 4B / 8B, the same model files as GoS1 for Ollama |
| Image | PyTorch + diffusers — distilled diffusion models ~0.6B–3.5B, CPU + CUDA GPU (live panel on /image) | PyTorch 2.14.1 + diffusers 0.37.1 on Metal (MPS) — SD-Turbo, SDXL-Turbo, SANA-Sprint |
| Telemetry | CPU Tctl (lm-sensors) + GPU temperature & power (nvidia-smi) | Chip die temperature (IOHID sensors, every 30 s, never during a measurement); no separate GPU sensor |
| Focus mode | Background systemd timers paused for every measurement | Spotlight indexing permanently off; software-update schedule off |
| Measurement | Tapo P110, two per machine, daisy-chained wall → outer → inner → machine, polled at 1-second intervals on each of two staggered meters via the local API (tapo 0.8.12), staggered 0.5 s. Absolute watts come from the inner meter only. The outer meter also sees the inner plug’s self-draw, which cancels in the per-meter ΔW combine (confidence method ci2). Each machine is measured only by its own pair. Refresh: fw 1.3.1 updates its reading every 1 s above ~20W but every 2 s below ~12W; fw 1.4.x every 1.5 s (measured October 2026). Polls between refreshes repeat the last value. | |
| Software | The same OWL codebase on both. Each machine runs its own OWL (queue, meters, VMAF scoring, results) and they exchange jobs and results over a signed peer link; the same job can run on both at once. Every result names the machine and engine that produced it. Results from one machine are never expressed as multiples of a reference measured on the other. | |
| Location | Same basement room, wired Ethernet to the same router. The decode rig and its clip origin are wired to GoS1; GoS2 drives them through GoS1’s queue. | |
Test Types
Video transcoding
Transcode a source file (default: Netflix Meridian 4K, CC BY 4.0) to a target codec and 1080p. Measures the energy cost of the full encode pipeline — decode, colour-space conversion, scale, encode. Supports CPU vs GPU comparison: both paths are run sequentially with a cooldown between them, and results are presented side by side.
Six presets across three codecs: H.264 (libx264 / h264_nvenc, 4000 kbps), H.265 (libx265 / hevc_nvenc, 2000 kbps), AV1 (libsvtav1 / av1_nvenc, 1500 kbps). A seventh Compare all codecs preset runs all six in sequence and produces a cross-codec energy matrix. (Encoder names track the installed GPU — the live list is in the Hardware Disclosure table above.)
All presets use ABR (Average Bit Rate) rate control at a shared per-codec bitrate target, so CPU and GPU receive the identical encoding task — output file sizes match across devices as confirmation. All GPU presets use the full hardware pipeline: hardware decode (-hwaccel cuda) + scale_cuda + hardware encode, with frames GPU-resident throughout. This represents real live-encoding workflows (Harmonic, Ateme); an earlier partial pipeline (CPU decode + GPU encode) has been replaced because it was unrepresentative and bottlenecked on CPU decode overhead.
The ffmpeg command used for each run is logged in the result JSON, editable from the page (signed-in GoS members and lab access), and reproduced in the result card for full transparency.
Perceptual quality (VMAF). Comparison runs (CPU vs GPU, or all codecs) also report VMAF — Netflix’s perceptual quality metric (0–100, higher is better) — so the energy figures sit next to a quality figure rather than an unstated assumption that the encodes are equivalent. It is computed at the delivered 1080p, comparing each encoded output against the source downscaled to 1080p (the distorted side is cropped to strip hardware-encoder padding, never upscaled). VMAF runs after the measurement window closes, so its compute cost is excluded from the reported energy. It is a quality cross-check, not a primary GoS measurement.
VMAF model version — v1 since 2026-07-17. OWL scores with Netflix’s VMAF v1 (model vmaf_v1.0.16, released 2026-06-20 with libvmaf 3.2.0). v1 drops the VIF feature, adds a banding detector (CAMBI) and VMAF’s first chroma (colour) feature, and corrects v0’s over-prediction on high-motion content — failure modes that sit exactly where OWL’s tests live (starvation-bitrate encodes band; sports content is high-motion). Every stored score carries its model identity (vmaf_model); results without that field predate 2026-07-17 and were all scored by vmaf_v0.6.1 — they remain valid v0 scores. The two scales are not comparable (the same degraded-vs-clean 1080p pair scored 77.95 on v0 and 83.59 on v1 on this bench), so result cards label every score v0 or v1 and comparisons are only ever made within one model. Scoring runs through a dedicated newer ffmpeg build (needed for libvmaf ≥3.2.0); the pinned encode binary is untouched, so the model upgrade cannot confound energy measurements. Measured scoring cost on this bench is within a few percent of v0 either way — removing VIF roughly pays for CAMBI + chroma.
No-reference quality (CompressedVQA-HDR). Enhancement / super-resolution runs on uploaded or general content have no ground-truth reference (the AI adds detail the source never had), so full-reference metrics do not apply there. Instead, OWL reports a no-reference score from CompressedVQA-HDR (Sun et al., arXiv:2507.11900, Apache 2.0 — winner of the ICME 2025 HDR/SDR VQA grand challenge), a learned model that scores each file independently and handles both HDR10 and SDR content. Like VMAF it runs after the measurement window closes, so its compute cost is excluded from the reported energy. Being a learned opinion of perceptual quality rather than a measurement, it is presented as a relative indicator within a run — never as an absolute quality claim — and is subject to further refinement pending validation.
Full-reference fidelity on ladder fixtures — two axes, read together. For the ten degraded-ladder fixtures OWL generated from its own pristine 4K masters (Big Buck Bunny and Meridian), a ground truth does exist, and enhancement runs on them additionally report full-reference VMAF (v1, 4K model) against that master — computed after the measurement window closes, like all quality scoring. Fidelity and perceptual quality are different axes, and OWL reports both deliberately: the no-reference score rates how good the output looks on its own; the full-reference score measures similarity to the reference, not perceived quality in the broader sense. Enhanced output is rarely one thing or the other: in practice it mixes genuinely recovered structure, reconstruction from learned priors, and newly synthesised detail, and the synthesised share can look convincing without matching the master’s exact pixels. Enhancement therefore typically raises perceived quality while moving the signal further from the master, and an output scoring below the naive-upscale anchor on fidelity while beating it on the no-reference axis is an expected signature of generative enhancement — not a defect of the enhancer or the metric, and not evidence that the output looks worse; the divergence between the two axes is itself the measurement. Two anchors give the fidelity score context, both paying the same pipeline encode as the measured output: a plain lanczos upscale of the degraded source (naive-encode anchor — what dumb scaling preserves) and the pristine master itself through the same pipeline (pristine-encode anchor — its gap to 100 is encode cost alone, landing around 89–91 on these fixtures). The degraded source scored as a player would display it (no pipeline encode) is shown as a separate path, never compared directly. Full-reference scoring requires an SDR 4K output — the anchors are 4K-denominated, and VMAF across HDR/SDR transfer functions is not meaningful — so other output targets, uploads, and non-ladder sources stay no-reference only.
Contributed technology. Some measured workloads use technology contributed by GoS member organisations — the AI video-enhancement harness measures Pixop Live, contributed by Pixop. Contributed technologies run on GoS hardware under this methodology; results are energy data, not endorsements. GoS members can contribute streaming technologies for measurement on the same terms.
4K HDR enhancement — reduced-capacity note. The HDR → 4K super-resolution combo sits close to the GPU’s memory ceiling on this hardware (peak ~94 % of 16 GB). To run it reliably, OWL applies two Pixop-supplied pipeline settings that lower the in-flight memory pressure (a smaller input buffer and fewer concurrent super-resolution threads), which bring the peak down to ~85 %. A controlled A/B on identical content found this changed the measured energy and encode throughput by less than the run-to-run noise — so the figure is reported normally, with the settings recorded in the result JSON. This note applies only to the 4K HDR combo; every other enhancement preset runs at the pipeline’s default settings.
AI workloads — beta, exploratory
Video transcoding is OWL’s core benchmark. Three AI workloads run alongside it on the same protocol and confidence framework, but they are explicitly beta — useful for relative comparisons, with headline numbers still being hardened (see Open Questions). In brief:
- LLM inference — mWh/token across a model ladder (TinyLlama 1.1B, Qwen3 1.7B/4B/8B, Mistral-NeMo 12B, Phi-4 14B, up to GPT-OSS 20B), cold or warm, CPU or GPU, with an optional batch mode. Prompts are saved in the result JSON; output streams word-by-word as live-run proof.
- Image generation — Wh/image across the distilled diffusion panel (~0.6B–3.5B), CPU or GPU, with a Compare-Models mode that fixes prompt, seed and resolution so the model is the only variable.
- RAG — the energy delta of retrieval: baseline (no retrieval) vs RAG with 3 context chunks vs 8, retrieved from a document corpus via ChromaDB + sentence-transformer embeddings, compared side by side.
Framing (GoS Language Lab position paper, Jan 2026): AI in streaming is neither inherently sustainable nor unsustainable — type, size and deployment decide net impact. The type matters enormously: streaming leans on small specialised CNNs (per-title encoding, scene classification, super-resolution) that are orders of magnitude cheaper than the general-purpose LLMs and diffusion models these tabs measure as an upper bound. OWL measures the energy AI adds (inference only); it does not measure the infrastructure energy AI avoids through better compression, caching or routing — both halves are needed for net impact, and OWL has the first. Each AI result is also shown as a multiple of a real video encode (the pinned canonical H.265 GPU encode of Meridian-120s) so the number stays anchored to a streaming workload rather than floating free. Full framing: Language Lab AI position paper →.
Client-device decode panels — external rig, 2026-07
Encode energy is paid once per title; decode energy is paid per viewer — so in 2026-07 the protocol went to the client side. A portable rig — three devices at the start, ten by 2026-09 — measures decode power per codec, per decode path (hardware vs software) and per regime (paced playback vs flat-out), with the same baseline/ΔW/confidence method and local-mW metering. Headlines: a hardware decoder cuts decode power ~3.6–4× on the same board; codec choice is nearly free on decode silicon but moves software decode power by up to ~60%. Full results, replication counts and caveats are in the findings catalog; these panels state their own device scope and are not GoS1-server results. Connectivity disclosure: rig devices are on Ethernet, except the Amazon Fire TV Stick 4K (added 2026-08), which is Wi-Fi only — its device-total power therefore includes powering its own radio, a share the Ethernet boxes don't carry and that this rig cannot separate; the Wi-Fi 7 access point is a few metres away, so link quality is not a confound, the radio's energy is. Comparisons that include the stick carry that caveat.
Onboarding a device — written from eleven, not from theory
Adding a box to the decode rig is itself a measurement, and this is a record of what eleven onboardings taught us, not a general method we are proposing for others. The devices, with the operating system and silicon each actually reported: Raspberry Pi 5 (Raspberry Pi OS, Broadcom BCM2712, software decode only) · Raspberry Pi 400 (Raspberry Pi OS, Broadcom BCM2711) · Amazon Fire TV Stick 4K (Fire OS 8, MediaTek MT8696) · Google TV Streamer (Android 14, MediaTek MT8696 — the same part as the stick, a different OS) · Bbox 4K operator CPE (Android 11, Marvell Berlin / Arcadyan HMB9213NW) · Apple TV 4K 2017 (tvOS 26.6, Apple A10X Fusion) · Xiaomi TV Box Gen 2 (Android 11, Amlogic S905X4, since retired) · Xiaomi TV Box Gen 3 (Android 14, Amlogic s7d) · Roku Express 4K (Roku OS 14.0.4, Realtek RTD1315) · TV Box W5 (Android 12 AOSP, Allwinner H618) · and the LG C2 panel itself (webOS 22, LG α9 Gen5). Eight silicon vendors, six OS families.
Four things recur often enough to be worth stating. A device’s self-description is not evidence: some boxes leave their SoC fields empty, and one — the W5 — reports itself as a Google developer kit running a different Android version entirely, so silicon and decode path are taken from what the platform logs while decoding, never from the product label. Timing constants must be measured per device, not inherited: a settle interval tuned on the Android boxes was applied unchanged to the Apple TV and quietly corrupted about an hour of its baselines before anyone noticed, which is why onboarding now characterises each box’s own boot and idle-decay curve before it may contribute a row. Control-plane liveness lies: one platform reports “idle” during real playback and another never advances its playback position at all, so power and the decoder log are the signals we trust for “is this actually playing”. And not every platform can answer the provenance question: the Android boxes name the decoder that was allocated, the Roku offers no such signal of any kind, so its hardware-versus-software path is inferred from the energy signature and labelled as inference. Where a box cannot be made to report something, we record the gap rather than fill it.
Energy budget & encode parity
Operators rarely ask “how small can the file be.” They fix a quality target — most often VMAF 92 — and then try to hit that quality for the least energy. The transcode budget calculator answers the inverse: given an energy budget, how many minutes of video can you push at your target VMAF, on which hardware, with which codec?
That calculator runs on a measured calibration table, built under the same protocol as every other OWL test. For a fixed source we sweep an ABR bitrate ladder across each codec (H.264 / H.265 / AV1) on both the CPU encoder (libx264 / libx265 / libsvtav1) and the GPU encoder (NVENC), measuring wall energy and — as a terminal pass — VMAF. For a chosen target VMAF we read the bitrate that hits it off the measured curve, and the watt-hours per minute that bitrate costs.
Model note: the current calibration table and the VMAF 92 operator target were measured in vmaf_v0.6.1 terms (the model in force at calibration, 2026-06). Live scoring moved to VMAF v1 in 2026-07; because the scales are not comparable, the budget page stays in v0 terms — and says so — until the next re-calibration re-scores the table under v1.
Encode parity — is the GPU really “worse”?
Operators often say hardware encoders score lower than software, “especially for AV1.” To test that fairly we measure the GPU twice at each bitrate — once with OWL’s current NVENC arguments (baseline) and once with a quality-knob bundle (tuned: -preset p7 -multipass 2 -spatial-aq -temporal-aq -rc-lookahead, B-frame references) — so the VMAF difference, and its energy cost, are measured rather than asserted. What the first full run (90 encodes, all 🟢) showed:
- Energy: NVENC uses 2.5–4.4× less energy per minute of video than the CPU encoder — and the win is speed, not lower draw: instantaneous wattage is similar (~70 W either way); the GPU simply finishes far sooner.
- Parity: the “GPU is worse, especially AV1” effect is real but only on low-complexity content at low bitrate (AV1 on Big Buck Bunny trailed by up to ~9 VMAF at 1 Mbps). On high-complexity content (Meridian) the gap nearly vanishes — and NVENC AV1 actually beats libsvtav1 at its default preset at mid-to-high bitrate.
- Tuning, measured and rejected: the “tuned” bundle cost 1.6–2.8× the energy and lowered VMAF for H.264 and AV1 (adaptive quantisation trades a fidelity metric like VMAF for perceptual quality). So the live encode path keeps the baseline NVENC config; we do not pay energy to make the metric worse.
What an “ABR ladder” means here
A real delivery encodes the same source at several resolutions so a player can adapt to bandwidth. The calculator’s “full ABR ladder” unit is a 5-rung ladder: 1080p (the quality-anchor rung, whose bitrate is set by the VMAF target) plus fixed lower rungs at 720p / 540p / 480p / 360p (lower rungs scale per codec). Ladder energy is the sum of all five rungs; the “1080p only” unit is the top rung alone.
The calibration is keyed by a hardware fingerprint (GPU, CPU, ffmpeg version) and is re-runnable from the Lab when the encode hardware changes — e.g. when dedicated ASIC/FPGA transcode cards arrive — so a hardware swap produces a new dataset rather than silently reusing stale numbers. Full method note: docs/encode_parity_calibration_2026-06.md.
Known Limitations
bin/probe-p110-fw) — polling faster than the refresh yields duplicate readings, not more information. Power resolution is not the limit: the local API preserves the instrument’s ~1 mW reading (see Confidence above).From energy to CO2e — for reference only
OWL is a power meter, not a carbon calculator. The number OWL produces and stands behind is energy — watts at the wall and watt-hours per task, measured directly by the P110. Everything else on this page is about getting that energy number right. We lead with power because it is what we can measure at the wall with no modelling assumptions; carbon is one modelling layer removed.
Every result also carries a gCO2e figure, but only as a downstream convenience: we multiply the measured energy by a grid carbon-intensity factor (Wh × gCO2e/kWh) so the energy can be read against everyday activities. That makes it a reference estimate, never a GoS measurement. Carbon attribution — allocation, boundaries, double-counting, marginal vs average intensity — is a hard problem that GoS deliberately leaves to the bodies whose job it is. This follows the GoS principle directly: “if it can’t be measured, it shouldn’t be asserted” — and what OWL measures directly is energy. Read the energy figure as the result; the CO2e is a footnote.
🟢 Direct = the energy figure (P110 polling at the wall, validated method, GoS primary measurement — this is what we cite). 🟡 Indicative = the gCO2e figure (Wh × third-party grid intensity — context, not citable as GoS data). Vocabulary follows the Greening of Streaming Language Lab AI position paper (Jan 2026), which proposes this 🟢/🟡/🔴 traffic-light for the entire ICT-energy-measurement landscape and rates IEA top-down energy figures as 🟡 Amber. OWL applies the same framework to its own outputs — every result-card carbon block carries the 🟡 chip; the energy headline retains the green palette.
For what it’s worth, the intensity used is lifecycle-basis (IPCC AR6 factors): the live French grid mix via Eco2mix when reachable, ElectricityMaps as a backup, and Ember annual country means as the fallback (also used for the stable comparison cities). The value and which source produced it are recorded in every result JSON and CSV export (CSV header carries a leading comment marking the carbon columns indicative). A result’s carbon dropdown also shows the same energy on a few past French grids for context. Module status — live cache, source, age, fallback — is at /carbon.
Open Questions
These are questions OWL has surfaced but not yet answered. They are published here in the interest of transparency.