From 4836580c50f08dbc2fe4e73e98570eec483aa2e7 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sun, 25 Oct 2026 23:20:48 +0400 Subject: [PATCH] =?UTF-8?q?perf:=20=D0=B7=D0=B0=D0=BC=D0=B5=D1=80=D1=8B=20?= =?UTF-8?q?=D0=B1=D0=B5=D0=B7=20=D1=82=D1=80=D0=BE=D1=82=D1=82=D0=BB=D0=B8?= =?UTF-8?q?=D0=BD=D0=B3=D0=B0=20CPU=20(=D1=80=D0=B5=D1=88=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=87=D0=B5=D0=BB=D0=BE=D0=B2=D0=B5=D0=BA=D0=B0?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #202 Co-Authored-By: Claude Opus 5.5 --- openspec/changes/perf-profiling/design.md | 6 ++++++ perf/README.md | 4 ++-- perf/e2e/scenarios/scroll.spec.ts | 15 ++++++++------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/openspec/changes/perf-profiling/design.md b/openspec/changes/perf-profiling/design.md index a3c3962..4f584f1 100644 --- a/openspec/changes/perf-profiling/design.md +++ b/openspec/changes/perf-profiling/design.md @@ -150,3 +150,9 @@ | 2026-10-25 | Только вертикальный скролл (ось y), роль `rf` | Бриф: «скролл при соавторе»; горизонтальная ось не зависит от потока входящих правок | Нет замера оси x при соавторе | | 2026-10-25 | Вспомогательный `waitRows` продублирован в `scroll-coauthor.spec.ts`, а не вынесен в lib/ | Не трогать `scroll.spec.ts` Task 5; функция короткая | Дубли при правке | | 2026-10-25 | Фильтр свода по умолчанию мерится на `max(PERF_CPU_RATES)` (при умолчании `1`), имя теста `filter cpu` | Троттлинг отключён по умолчанию, но `PERF_CPU_RATES=1,4,6` должен вести себя как прежде | Нет | + +## Decisions made with the human + +| Дата | Решение | Источник | +|---|---|---| +| 2026-10-25 | Замеры без троттлинга CPU: все сценарии по умолчанию на CPU 1×; код `throttle()` и env `PERF_CPU_RATES=1,4,6` оставлены для ручного включения; число повторов (3) не сокращено | Решение человека, передано координатором | diff --git a/perf/README.md b/perf/README.md index dfc6861..4ebd1bc 100644 --- a/perf/README.md +++ b/perf/README.md @@ -61,9 +61,9 @@ PERF_RUN_ID=t PERF_TARGETS='FORM_2/AHR/*' PERF_CPU_RATES=1 PERF_REPEATS=1 npx pl cd .. && .venv/bin/python -m perfkit.aggregate results/s1 ``` -- Матрица: роль `rf` x листы с `owner=rf` и отчёты проектов x оси y,x x CPU 1,4,6 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`). +- Матрица: роль `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//`, `project//`, `svod/`; к записи добавляется суффикс оси `@y` / `@x` (агрегатор группирует по target). -- Переменные: `PERF_TARGETS` (glob через запятую; сопоставляется с target без суффикса оси и с полным), `PERF_CPU_RATES` (`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 мин). +- Переменные: `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//artifacts/` (30-70 МБ каждый, в git не попадают). - Сайт свода не читает `#hash` из URL: вкладка (`FORM_1`, `GO`...) выбирается кликом по `role=tab`. diff --git a/perf/e2e/scenarios/scroll.spec.ts b/perf/e2e/scenarios/scroll.spec.ts index 73e50ce..4c3ff56 100644 --- a/perf/e2e/scenarios/scroll.spec.ts +++ b/perf/e2e/scenarios/scroll.spec.ts @@ -7,7 +7,7 @@ * сопоставляется с target без суффикса оси и с полным (`.../@y`) * PERF_RUN_ID каталог прогона perf/results/ * PERF_REPEATS число повторов вместо матричного (3 для rf/свод, 1 для admin-контроля) - * PERF_CPU_RATES '1,4,6' - список CPU throttling rate + * PERF_CPU_RATES '1' по умолчанию (без троттлинга, решение человека); '1,4,6' включает троттлинг вручную * PERF_ROLES фильтр ролей через запятую (rf,admin,dfip) * PERF_AXES 'y,x' - оси для rf (по умолчанию y,x) * PERF_LOAD_TIMEOUT_MS ожидание данных после навигации (по умолчанию 900000) @@ -29,7 +29,8 @@ const LOAD_TIMEOUT = Number(process.env.PERF_LOAD_TIMEOUT_MS ?? 900_000); const MAX_STEPS = Number(process.env.PERF_MAX_STEPS ?? 5000); const envList = (n: string, d: string[]) => (process.env[n] ? process.env[n]!.split(',').map((s) => s.trim()).filter(Boolean) : d); -const CPU_RATES = envList('PERF_CPU_RATES', ['1', '4', '6']).map(Number); +const CPU_RATES = envList('PERF_CPU_RATES', ['1']).map(Number); +const FILTER_CPU = Math.max(...CPU_RATES); // замер смены фильтра: на самом тяжёлом из заданных CPU (по умолчанию 1) const ROLES = envList('PERF_ROLES', ['rf', 'admin', 'dfip']); const AXES = envList('PERF_AXES', ['y', 'x']) as Axis[]; const repeats = (dflt: number) => (process.env.PERF_REPEATS ? Number(process.env.PERF_REPEATS) : dflt); @@ -234,7 +235,7 @@ async function measure(browser: import('@playwright/test').Browser, o: RunOpts) } } -/** Таймаут теста: 45 мин по умолчанию, свод 2 (загрузка до 15 мин + скролл на CPU 6 ~40 мин) - 3 ч. */ +/** Таймаут теста: 45 мин по умолчанию, свод 2 (загрузка до 15 мин + скролл на CPU 6 ~40 мин, если включён вручную) - 3 ч. */ const testTimeout = (t: Target) => t.kind === 'svod2' ? Number(process.env.PERF_TEST_TIMEOUT_SVOD2_MS ?? 3 * 3600_000) @@ -256,11 +257,11 @@ test.describe('scroll', () => { }); } } - // смена фильтра - только своды, один раз на CPU 6 - if (t.kind !== 'sheet' && t.kind !== 'project' && CPU_RATES.includes(6)) { - test(`${t.name} ${ra.role} filter cpu6`, async ({ browser }) => { + // смена фильтра - только своды, один раз на FILTER_CPU + if (t.kind !== 'sheet' && t.kind !== 'project') { + test(`${t.name} ${ra.role} filter cpu${FILTER_CPU}`, async ({ browser }) => { test.setTimeout(testTimeout(t)); - await measure(browser, { t, role: ra.role, axis, cpu: 6, repeat: 0, filter: true }); + await measure(browser, { t, role: ra.role, axis, cpu: FILTER_CPU, repeat: 0, filter: true }); }); } }