Skip to content

feat(dashboard): level-profile chart (speed + ratio) and fix legend click target #183

Description

@polaz

Problem

Текущий dashboard (.github/bench-dashboard/index.html) показывает только один график — delta_ratio Rust/FFI по времени. Из него не видно мгновенный «перекос» по уровням: где Rust медленнее FFI и насколько, где Rust хуже по ratio и насколько. Плюс два UX-бага в существующем графике мешают читать данные.

Scope

Часть 1 — добавить второй график «Level Profile» (compress + decompress + ratio)

Новый график рядом с существующим:

  • X-axis: уровни компрессии (level_1_fast, level_2_dfast, ..., level_22_btultra2) в порядке возрастания.
  • Y-axis: двойная — слева speed (MiB/s), справа output ratio (compressed/input, меньше = лучше).
  • Серии: 6 линий, filled area + smoothing (tension: 0.35):
    • rust_compress_speed (левая ось)
    • ffi_compress_speed (левая ось)
    • rust_decompress_speed (левая ось, dashed-dot)
    • ffi_decompress_speed (левая ось, dashed-dot)
    • rust_ratio (правая ось, dashed)
    • ffi_ratio (правая ось, dashed)
  • Toggles — 6 чекбоксов над графиком, по одному на серию. Toggle через dataset.hidden/setDatasetVisibility + chart.update("none") — без destroy/recreate.
  • Filters: переиспользуем верхние фильтры delta-chart (Target / Scenario / Level), плюс собственный Snapshot. По умолчанию все three top filters стоят в __all__ = кумулятивные данные (среднее по тому измерению). Пин конкретного значения сужает чарт до этой когорты.
  • Snapshot: дропдаун над профильным графиком — список всех generated_at (или commit_sha) для текущей когорты. latest берёт самый свежий снапшот per cohort (а не глобально), чтобы кумулятивный режим не уезжал в сторону самой свежей target/scenario.
  • Decompress усредняется по обоим bench source variants (rust_stream + c_stream) на сторону — оба measure valid decompression numbers, arithmetic mean детерминистичен независимо от порядка итерации.
  • Источник данных: существующий benchmark-relative.json.

Acceptance criteria:

  • График рендерится для всех сценариев / уровней из benchmark-relative.json.
  • Все 6 toggle работают независимо, состояние сохраняется при смене фильтров.
  • Top filters одновременно управляют delta-chart и profile-chart; __all__ даёт unbiased per-cohort усреднение.
  • Snapshot=latest использует per-cohort latest, не глобальный.
  • Существующий delta-time chart продолжает работать без регрессий.

Часть 2 — починить клики на легенде первого графика

Скриншот (Latest series: 63. Outside parity band: 61. Showing top 8 by deviation.):

  • Цветные коробочки (Chart.js legend items) визуально не выровнены с подписью — клик регистрируется выше текста, который пользователь читает. Текущий клик-target живёт у coloured square, а подпись уезжает на следующую строку при wrap.
  • Не все scenario-чипы видны при большом количестве серий — Chart.js скрывает «лишние» молча, без счётчика и без скролла.

Acceptance criteria:

  • Click-target совпадает с визуальной строкой [color] [label] целиком, не только с квадратом. Hover-highlight на всей строке.
  • Все серии видны (через scrollable container легенды).
  • Поведение click → hide/show series сохраняется.
  • A11y: keyboard activation (Enter/Space), aria-pressed, focus-visible outline; фокус и scrollTop сохраняются между rerender-ами.

Часть 3 — починить семантику "Outside parity band"

Текущий aggregator флагал "Outside parity band" для отклонений в ОБЕ стороны от эталона. Положительные сдвиги (мы лучше донора) попадали в тот же канал, что и реальные регрессы.

Что нужно сделать:

Sign-gate по метрике:

  • Ratio axis: флагать только если rust_ratio / ffi_ratio > 1.0 (мы произвели больший output) — реальный регресс по сжатию.
  • Speed axis: флагать только если rust_speed < ffi_speed (мы медленнее) — реальный регресс по throughput.
  • Memory axis (peak_alloc_bytes): флагать только если rust > ffi (мы аллоцировали больше).
  • Положительные сдвиги в нашу пользу — отдельный "notable wins not counted" channel.

Acceptance:

  • Ни одна строка "Outside parity band" не эмитится для отклонения в нашу пользу.
  • Aggregate count считает только реальные регрессы.
  • Существующая детекция регрессов для отрицательных delta продолжает работать.

Implementation hints

  • Chart.js 4.x — кастомная HTML-легенда вместо встроенной; <button type="button"> per row для a11y.
  • Level Profile — type: 'line' + fill: 'origin' + tension: 0.35 + dual Y-axis (scales.y / scales.y1).
  • Toggle-чекбоксы — нативные <input type="checkbox">, обработчик переключает setDatasetVisibility и зовёт chart.update("none").
  • Sign-gate — JS-side в updateStatus(), не на стороне эмиттера.

Files

  • .github/bench-dashboard/index.html (single-file dashboard) — все изменения тут.

Related

Estimate

1d 6h (mid-level dev):

  • 5h — Level Profile chart + dual axis + 6 series + shared top filters + Snapshot + toggle UI + per-cohort latest
  • 4h — кастомная HTML-легенда (a11y: keyboard + aria-pressed + focus/scroll preservation; фиксит обе UX-проблемы первого графика)
  • 2h — sign-gate "Outside parity band"
  • 3h — стили / адаптив / manual smoke-test обоих графиков на текущем payload

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2-mediumMedium priority — important improvementenhancementNew feature or requestperformancePerformance optimization

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions