Andrey Epifancev 627ea82321 perf: замеры открытия модалок
Перечень модалок (modals.json), measureOpen с main_thread_ms, профиль
перерисовок React (reactProfile), сценарий modals.spec.ts, подкоманда
stand.sh web-profiling (:8081), генератор modals-inventory.md.

Refs #202

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 00:53:55 +04:00

8.9 KiB
Raw Blame History

Профилирование: perf/

Стенд

Требования: Docker, python3.12, Node 24, npm.

perf/stand.sh all       # up, migrate, bootstrap, web-build, api, web, smoke
perf/stand.sh down      # остановить бэк, фронт и Postgres

Подкоманды: up | migrate | bootstrap | api | web-build | web | smoke | reset | down | all. reset — чистая БД: останавливает бэк, сносит volume Postgres, поднимает заново (up, migrate, bootstrap, api); фронт не трогает.

  • Postgres 16 (pg_stat_statements) на localhost:55432, perf/perf/perf.
  • Бэк (DEBUG=true) на http://localhost:8000, логи и PID в perf/.run/.
  • Фронт (prod-сборка, rsbuild preview) на http://localhost:8080; /api (включая WebSocket) проксируется на бэк.
  • Профилирующая сборка (PERF_PROFILING=1, react-dom/profiling) лежит в perf/.build/profiling, обычная в perf/.build/prod. Для запуска preview с профилирующей сборкой: PERF_PROFILING=1 perf/stand.sh web (после остановки обычного web).
  • Админ: admin / admin.
  • Переменные: PERF_API, PERF_WEB, PERF_DB_DSN.
  • Зависимости Python ставятся в perf/.venv (api/requirements-dev.txt + perf/requirements.txt).
  • npm берёт пакеты с https://registry.npmjs.org (npm_config_registry), web/.npmrc не нужен.

Данные

perf/stand.sh reset                                   # пустая БД (генератор на непустой БД откажется работать)
cd perf && .venv/bin/python -m perfkit.gen.run --scale 1      # ~20-40 мин, пишет results/manifest-s1.json
cd perf && .venv/bin/python -m perfkit.gen.verify --scale 1   # должен напечатать VERIFY OK

Генератор работает через HTTP-API под admin (WebSocket — только для программ и проектов FORM_4, у них нет HTTP-ручки). Пользователи: perf_rf, perf_rf_coauthor (одна РФ, роль 3) и perf_dfip (роль 2, все оргединицы), пароль perf; в манифесте они под ключами rf, rf_coauthor, dfip. Числа плана и их обоснование — в perf/perfkit/gen/plan.py.

Фронт: замеры

Каркас: perf/e2e (Playwright + CDP). Версии: @playwright/test 1.64.1, Chromium 156.0.8078.4 (headless shell, revision 1248).

cd perf/e2e && npm install && npx playwright install chromium   # npm_config_registry=https://registry.npmjs.org
npx playwright test tests/harness.spec.ts                      # самопроверка каркаса
  • PERF_RUN_ID — имя каталога прогона perf/results/<run_id>/ (по умолчанию YYYYMMDD-HHMM).
  • PERF_SCALE — масштаб манифеста perf/results/manifest-s<scale>.json (по умолчанию 1).
  • PERF_API, PERF_WEB — адреса стенда (http://localhost:8000, http://localhost:8080).
  • Агрегация: cd perf && .venv/bin/python -m perfkit.aggregate results/<run_id>.
  • Библиотеки perf/e2e/lib/: auth.loginAs, cdp.* (throttle, trace, gc, heap), probe (long tasks > 50 мс, кадры rAF; тяжёлый кадр — интервал > 50 мс), results.append, manifest.loadManifest.

Сценарии скролла

perf/e2e/scenarios/scroll.spec.ts, библиотека lib/scroll.ts (scrollThrough: колесо мыши шагами по 200 px вниз/вправо до конца и обратно, complete = scrollTop + clientHeight >= scrollHeight - 2). Самопроверка: npx playwright test tests/scroll-lib.spec.ts.

cd perf/e2e
PERF_RUN_ID=s1 PERF_SCALE=1 npx playwright test scenarios/scroll.spec.ts           # вся матрица масштаба (долго!)
PERF_RUN_ID=t PERF_TARGETS='FORM_2/AHR/*' PERF_CPU_RATES=1 PERF_REPEATS=1 npx playwright test scenarios/scroll.spec.ts
cd .. && .venv/bin/python -m perfkit.aggregate results/s1
  • Матрица: роль rf x листы с owner=rf и отчёты проектов x оси y,x x CPU 1 (без троттлинга, решение человека) x 3 повтора; admin x все листы x ось y x CPU x 1 повтор (только масштаб 1); admin и dfip x своды (svod1_MAIN/FORM_1..4, svod2_GO/RF, projects_summaries) x ось y x CPU x 3 повтора + одна смена фильтра на CPU 6 (target .../filter).
  • target: FORM_x/<sheet>/<direction|null>, project/<report_type>/<project_id>, svod/<name>; к записи добавляется суффикс оси @y / @x (агрегатор группирует по target).
  • Переменные: PERF_TARGETS (glob через запятую; сопоставляется с target без суффикса оси и с полным), PERF_CPU_RATES (по умолчанию 1; 1,4,6 включает троттлинг вручную), PERF_REPEATS (число повторов вместо матричного), PERF_ROLES (rf,admin,dfip), PERF_AXES (y,x), PERF_LOAD_TIMEOUT_MS (ожидание данных, по умолчанию 15 мин; ответ свода 2 до 47 МБ), PERF_MAX_STEPS, PERF_TEST_TIMEOUT_MS (таймаут одного замера, по умолчанию 45 мин), PERF_TEST_TIMEOUT_SVOD2_MS (для свода 2, по умолчанию 3 ч: загрузка до 15 мин + скролл на CPU 6 порядка 40 мин).
  • Каждый замер - новый browser context; CPU-троттлинг включается после загрузки данных (до скролла). Перед замером ждём >= 20 строк tbody tr; если не дождались - запись incomplete: true с skip_reason.
  • Метрики: probe (long tasks, кадры), axis (0 = y, 1 = x), rows_visible_before/after, json_bytes_sheet (размер распакованного ответа загрузки данных из сетевого лога), steps. Записи с skip_reason (no_overflow, rows<20, filter_no_effect, scroll_incomplete...) всегда incomplete:true; в конце прогона они выводятся списком. Метрика traced=1 - замер с включённым трейсом (повтор 0), Task 10 может исключать его из статистики. Trace первого повтора - в perf/results/<run_id>/artifacts/ (30-70 МБ каждый, в git не попадают).
  • Сайт свода не читает #hash из URL: вкладка (FORM_1, GO...) выбирается кликом по role=tab.

Модалки

Перечень - perf/e2e/modals.json (id, page, source file:line, open{selector, action, pre}, priority, роль; skip_reason для недостижимых из UI). lib/modal.ts (measureOpen: click_to_visible_ms, long_task_n, long_task_ms, main_thread_ms = прирост CDP Performance.TaskDuration от клика до отрисовки), lib/reactProfile.ts (минимальный __REACT_DEVTOOLS_GLOBAL_HOOK__: renders и self_ms по компонентам, нужна профилирующая сборка). Самопроверка: npx playwright test tests/modal-lib.spec.ts.

cd perf/e2e
PERF_RUN_ID=m PERF_PRIORITY=1 PERF_REPEATS=2 npx playwright test scenarios/modals.spec.ts          # priority 1 (по умолчанию 5 повторов; priority 2 - 3)
PERF_RUN_ID=m PERF_PRIORITY=2 npx playwright test scenarios/modals.spec.ts
../stand.sh web-profiling                                                                          # профилирующая сборка на :8081
PERF_WEB=http://localhost:8081 PERF_PROFILING=1 PERF_RUN_ID=m PERF_MODALS='sheet_*,projects_create' npx playwright test scenarios/modals.spec.ts   # scenario modal_renders
cd .. && .venv/bin/python -m perfkit.modals_inventory results/m                                   # report-data/m/modals-inventory.md
  • Переменные: PERF_MODALS (glob по id; <id>@big - вариант на самом большом листе), PERF_PRIORITY (1, 2, 1,2), PERF_ROLES, PERF_REPEATS, PERF_CPU_RATES (по умолчанию 1).
  • Каждый замер - новый browser context. Записи: scenario: modal (метрики measureOpen) и modal_renders (metrics.renders_top - топ-20 по self_ms, commits, total_renders).
  • Оценка полной матрицы на CPU 1: priority 1 (17 целей x 5) около 6 мин, priority 2 (15 x 3) около 2 мин, profiling около 2 мин; всего 10-12 мин. С PERF_CPU_RATES=1,4,6 priority 1 вырастет втрое.