fix(#000057): measure power STATES, not a duty-cycle blend; guarantee cache miss
fox 2026-05-21: 'gen 200W' was a bug — joules/window blends the ~400W generation bursts with the sub-100W gaps (retrieval/verify/network) into a power state the card never sits at. A card occupies DISTINCT states (idle / middle-idle = resident-between-requests / generation), differing per card×model×server. watt_probe.classify_power_bands(): largest-gap split of the window samples into a low band (serving floor) and high band (generation draw) + duty cycle. Data-derived, never hardcoded — tested at two scales. The worker emits the decomposition + raw samples; RemoteProbe/LocalProbe expose band_stats() uniformly. energy_cogs: marginal now taken against the measured SERVING FLOOR (the standing cost of being ready), not deep idle; the blend is kept but labelled window_mean_w. Reports idle/serving-floor/gen-draw/duty. Cache-miss certainty (fox's question): the arborist arm runs burn_existing=True (force-deletes any live providence row before inference) and asserts cache_hits==0 with a loud warning + real_inference flag — so we time real generation, never a SQLite lookup. Solo has no cache path. 11 tests (energy math + band split). Validated live on the isolated 4090: solo gen 308W/70%-duty vs substrate 396W/8.6%-duty — substrate marginal/tok is LOWER, gross/tok higher (it holds the card longer for retrieval).
This commit is contained in:
parent
1aff09f021
commit
5b1cbeed80
3 changed files with 211 additions and 68 deletions
|
|
@ -1,59 +1,102 @@
|
|||
"""Energy-COGS math for bench/watt_bench.py:energy_cogs (#000057).
|
||||
"""Energy-COGS math + power-state classification (#000057).
|
||||
|
||||
Pure function, no GPU / no nvidia-smi. fox 2026-05-21: power states are
|
||||
MEASURED per card/model/server; the only operator input is price_per_kwh.
|
||||
These tests pin the joules→kWh→$ arithmetic and the warm-idle marginal
|
||||
decomposition. See docs/calculator-test-patterns.md.
|
||||
Pure functions, no GPU / no nvidia-smi. fox 2026-05-21: a card occupies
|
||||
DISTINCT power states (idle / middle-idle / generation) that differ per
|
||||
card×model×server — measure them, never hardcode, never collapse them
|
||||
into one blended "gen W". These tests pin the joules→kWh→$ arithmetic,
|
||||
the serving-floor marginal, and the data-derived band split.
|
||||
See docs/calculator-test-patterns.md.
|
||||
"""
|
||||
from bench.watt_bench import energy_cogs
|
||||
from bench.watt_probe import classify_power_bands
|
||||
|
||||
|
||||
# ---- energy_cogs -------------------------------------------------------
|
||||
|
||||
def test_gross_cogs_one_kwh():
|
||||
# 3.6e6 J == exactly 1 kWh.
|
||||
c = energy_cogs(gpu_joules=3.6e6, window_s=100.0, tot_tok=1000,
|
||||
idle_mean_w=100.0, price_per_kwh=0.33)
|
||||
idle_mean_w=30.0, price_per_kwh=0.33)
|
||||
assert c["available"] is True
|
||||
assert c["gross_usd"] == 0.33 # 1 kWh * $0.33
|
||||
assert c["gross_usd_per_1k_tok"] == 0.33 # 0.33 / 1000 * 1000
|
||||
assert c["mean_gen_w_measured"] == 36000.0 # 3.6e6 J / 100 s
|
||||
assert c["gross_usd"] == 0.33 # 1 kWh * $0.33
|
||||
assert c["gross_usd_per_1k_tok"] == 0.33
|
||||
assert c["window_mean_w"] == 36000.0 # blend, honestly labelled
|
||||
|
||||
|
||||
def test_marginal_subtracts_measured_warm_idle():
|
||||
# warm-idle 100 W over 100 s = 10_000 J of the 3.6e6 J is "keep hot".
|
||||
def test_marginal_uses_serving_floor_when_present():
|
||||
# Serving floor (middle-idle 60W) is the right marginal floor, not idle.
|
||||
c = energy_cogs(gpu_joules=3.6e6, window_s=100.0, tot_tok=1000,
|
||||
idle_mean_w=100.0, price_per_kwh=0.33)
|
||||
assert c["warm_idle_joules"] == 10000.0
|
||||
assert c["marginal_joules"] == 3.6e6 - 10000.0
|
||||
# marginal < gross because warm-idle energy is removed.
|
||||
idle_mean_w=30.0, price_per_kwh=0.33,
|
||||
serving_floor_w=60.0, gen_draw_w=400.0, gen_duty=0.5)
|
||||
assert c["marginal_floor"] == "serving"
|
||||
assert c["marginal_floor_w"] == 60.0
|
||||
assert c["marginal_joules"] == 3.6e6 - 60.0 * 100.0
|
||||
assert c["gen_draw_w_measured"] == 400.0
|
||||
assert c["gen_duty_cycle"] == 0.5
|
||||
|
||||
|
||||
def test_marginal_falls_back_to_idle_without_serving_floor():
|
||||
c = energy_cogs(gpu_joules=3.6e6, window_s=100.0, tot_tok=1000,
|
||||
idle_mean_w=30.0, price_per_kwh=0.33)
|
||||
assert c["marginal_floor"] == "idle"
|
||||
assert c["marginal_joules"] == 3.6e6 - 30.0 * 100.0
|
||||
assert c["marginal_usd_per_1k_tok"] < c["gross_usd_per_1k_tok"]
|
||||
|
||||
|
||||
def test_marginal_clamps_to_zero_when_idle_exceeds_load():
|
||||
# A noisy idle window can read hotter than a short quiet load window.
|
||||
def test_marginal_clamps_to_zero_when_floor_exceeds_load():
|
||||
c = energy_cogs(gpu_joules=5000.0, window_s=100.0, tot_tok=10,
|
||||
idle_mean_w=100.0, price_per_kwh=0.33)
|
||||
idle_mean_w=30.0, price_per_kwh=0.33,
|
||||
serving_floor_w=100.0)
|
||||
assert c["marginal_joules"] == 0.0
|
||||
assert c["marginal_usd"] == 0.0
|
||||
|
||||
|
||||
def test_no_idle_baseline_gives_gross_only():
|
||||
def test_no_floor_gives_gross_only():
|
||||
c = energy_cogs(gpu_joules=3.6e6, window_s=100.0, tot_tok=1000,
|
||||
idle_mean_w=None, price_per_kwh=0.33)
|
||||
assert c["available"] is True
|
||||
assert c["warm_idle_w_measured"] is None
|
||||
assert "marginal_usd_per_1k_tok" not in c # can't decompose without it
|
||||
assert "marginal_usd_per_1k_tok" not in c # no floor -> can't decompose
|
||||
|
||||
|
||||
def test_unavailable_when_inputs_missing():
|
||||
assert energy_cogs(None, 100.0, 1000, 100.0, 0.33)["available"] is False
|
||||
assert energy_cogs(3.6e6, None, 1000, 100.0, 0.33)["available"] is False
|
||||
assert energy_cogs(3.6e6, 100.0, 0, 100.0, 0.33)["available"] is False
|
||||
assert energy_cogs(None, 100.0, 1000, 30.0, 0.33)["available"] is False
|
||||
assert energy_cogs(3.6e6, None, 1000, 30.0, 0.33)["available"] is False
|
||||
assert energy_cogs(3.6e6, 100.0, 0, 30.0, 0.33)["available"] is False
|
||||
|
||||
|
||||
def test_price_is_the_only_lever():
|
||||
# Same measured energy, double the rate -> double the dollar COGS.
|
||||
a = energy_cogs(3.6e6, 100.0, 1000, 100.0, 0.33)
|
||||
b = energy_cogs(3.6e6, 100.0, 1000, 100.0, 0.66)
|
||||
# Tolerate the function's 6-decimal display rounding on the ratio.
|
||||
a = energy_cogs(3.6e6, 100.0, 1000, 30.0, 0.33, serving_floor_w=60.0)
|
||||
b = energy_cogs(3.6e6, 100.0, 1000, 30.0, 0.66, serving_floor_w=60.0)
|
||||
assert round(b["gross_usd"] / a["gross_usd"], 3) == 2.0
|
||||
assert round(b["marginal_usd"] / a["marginal_usd"], 3) == 2.0
|
||||
|
||||
|
||||
# ---- classify_power_bands ---------------------------------------------
|
||||
|
||||
def test_band_split_is_bimodal_on_idle_vs_gen():
|
||||
# 60W middle-idle gaps interleaved with 400W generation bursts.
|
||||
samples = [60, 62, 58, 405, 398, 410, 61, 400, 59, 402, 63, 395]
|
||||
b = classify_power_bands(samples)
|
||||
assert b["available"] and b["bimodal"]
|
||||
assert 55 <= b["low_band_w"] <= 70 # serving floor
|
||||
assert 390 <= b["high_band_w"] <= 415 # generation draw
|
||||
assert b["peak_w"] == 410
|
||||
assert 0.0 < b["duty_cycle"] < 1.0
|
||||
|
||||
|
||||
def test_band_unimodal_when_idle_only():
|
||||
# A quiet idle window: no distinct generation state.
|
||||
b = classify_power_bands([30, 31, 29, 30, 32, 28, 31])
|
||||
assert b["available"] is True
|
||||
assert b["bimodal"] is False
|
||||
|
||||
|
||||
def test_band_unavailable_too_few_samples():
|
||||
assert classify_power_bands([400, 60])["available"] is False
|
||||
|
||||
|
||||
def test_band_thresholds_not_hardcoded_scale_with_data():
|
||||
# Same shape at a different absolute scale (a different card) still
|
||||
# splits — proves the split is data-derived, not a fixed watt cut.
|
||||
b = classify_power_bands([12, 13, 11, 95, 98, 92, 12, 96])
|
||||
assert b["bimodal"] is True
|
||||
assert b["low_band_w"] < 20 and b["high_band_w"] > 90
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue