feat(producer): lower parallel-DE router floor to 700 frames + power-state telemetry

HF_DE_PARALLEL_MIN_FRAMES default 2000 -> 700, re-calibrated by a controlled
crossover sweep (fixed content-per-frame, three synthetic profiles x
{350..3000f} x {single,par2,par3} x 3 reps, resolved worker counts and capture
modes verified per run): par3 beats single at EVERY size in every profile —
+17-21% at 700f rising to +28-34% at 3000f. That includes a
24-sub-composition profile built specifically to reproduce the 'workers
re-pay init' failure the original 2000 floor guarded against (92k tweens,
~2.5s pollSubCompositionTimelines per worker): workers initialize
concurrently, so duplicated init costs CPU, not wall-clock, and the comp
still parallelizes +19% at 700f. Below ~700f the win thins toward +10%
while paying three hardware-GPU browsers, so a floor remains. par2 loses to
par3 in every cell of every profile — the router's existing 3-worker pin is
confirmed, not changed. Harness:
plans/drawelement-fast-capture/de-crossover-bench.sh (docs repo).

Also adds on_battery / low_power_mode to render_complete and render_error.
The DE fleet is macOS laptops, and bench sweeps on an M4 Pro caught the SAME
render flipping between ~9.6 and ~17.2 ms/frame power-management regimes
with no existing telemetry signal to segment by — the router soak reading
this change needs that dimension to interpret perf on the machines users
actually render on. Sampled per event (volatile), pmset-based, darwin-only,
null-safe on failure.

Router stays default-off behind HF_DE_PARALLEL_ROUTER; this tunes what it
will do when the soak clears it to flip.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vance Ingalls
2026-07-27 22:51:28 -07:00
co-authored by Claude Opus 5
parent 97ec7db5cc
commit 3da31e399b
4 changed files with 143 additions and 8 deletions
+16
View File
@@ -3,6 +3,20 @@ import type { SubTimelineWaitOutcome } from "@hyperframes/engine";
import { FEEDBACK_RATING_SCALE } from "../utils/feedbackRating.js";
import { flush, trackEvent } from "./client.js";
import { readConfig } from "./config.js";
import { getPowerState } from "./system.js";
// Power state is volatile (a laptop docks/undocks mid-session), so it is
// sampled per render event rather than cached with SystemMeta. Attached to
// render_complete AND render_error: the DE fleet is macOS laptops whose
// power management shifts render perf ~1.8x with no other telemetry signal,
// and perf/soak analysis needs to segment by it (see getPowerState).
function powerStateFields(): { on_battery?: boolean; low_power_mode?: boolean } {
const power = getPowerState();
return {
on_battery: power.on_battery ?? undefined,
low_power_mode: power.low_power_mode ?? undefined,
};
}
// run_id is attached only when the orchestrator set HYPERFRAMES_RUN_ID — an
// absent property, never null/"" (PostHog treats those as real values).
@@ -277,6 +291,7 @@ export function trackRenderComplete(
de_blank_recaptures: props.deBlankRecaptures,
de_boundary_frames: props.deBoundaryFrames,
de_ncpr_fallbacks: props.deNcprFallbacks,
...powerStateFields(),
source: props.source ?? "cli",
composition_duration_ms: props.compositionDurationMs,
composition_width: props.compositionWidth,
@@ -356,6 +371,7 @@ export function trackRenderError(
elapsed_ms: props.elapsedMs,
peak_memory_mb: props.peakMemoryMb,
memory_free_mb: props.memoryFreeMb,
...powerStateFields(),
...renderObservabilityEventProperties(props),
},
props.distinctId,