From 0f7c6c5179d619e03bab772bcef2b0bbba42adbb Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Thu, 29 Oct 2026 00:48:11 +0400 Subject: [PATCH] =?UTF-8?q?perf:=20first=5Frows=5Fms=20=D1=81=D0=B2=D0=BE?= =?UTF-8?q?=D0=B4=D0=B0=202=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20raf-=D0=BD?= =?UTF-8?q?=D0=B0=D0=B1=D0=BB=D1=8E=D0=B4=D0=B0=D1=82=D0=B5=D0=BB=D1=8C,?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B5=D0=B6=D0=BD=D0=B8=D0=B9=20waitRows=20?= =?UTF-8?q?=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD=D1=8F=D0=B5=D1=82=20RowsEr?= =?UTF-8?q?ror=20=D0=B8=20=D0=B1=D1=8B=D1=81=D1=82=D1=80=D1=8B=D0=B9=20?= =?UTF-8?q?=D0=BE=D1=82=D0=BA=D0=B0=D0=B7=20(I-6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- perf/README.md | 4 ++-- perf/e2e/scenarios/scroll.spec.ts | 23 +++++++++++++---------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/perf/README.md b/perf/README.md index 09cfe6d..17c4ba2 100644 --- a/perf/README.md +++ b/perf/README.md @@ -279,7 +279,7 @@ cd .. 2. Скопировать `results/manifest-s1.json` в оба worktree («до» из AURORA-1842, «после» из ветки PR 3). Код сценариев один (из ветки PR 3): в временном worktree «до» (вершина 1842) выполнить `git checkout feat/236-aurora-1843-svod2-server-paging-perf -- perf/` без коммита; иначе «до» снимется старым `scroll.spec.ts` без новых метрик. -3. «До»: `stand.sh reset/gen/verify/api/web-build/web/down` для «до» запускать из worktree «до»: БД остаётся на старой миграции +3. «До»: `stand.sh reset/api/web-build/web/down` и `perfkit.gen.run`/`perfkit.gen.verify` (шаг 1) для «до» запускать из worktree «до»: БД остаётся на старой миграции (иначе 0053 уронит 6-аргументные `v_svod2_go/rf`, и API «до» пойдёт в новые функции). Поднять `api`, `web-build`, `web`, снять замеры (команды ниже). 4. Остановить сервисы. 5. «После»: `stand.sh migrate` и все сервисы «после» запускать из worktree «после»: `alembic upgrade head`, затем `api`, `web-build`, `web`; перед замером выполнить сверку (ниже); снять замеры. @@ -304,7 +304,7 @@ node analysis/svod2_search_oracle.mjs --check --api http://localhost:8000 \ - `json_bytes_sheet` - прежний смысл: максимум одного ответа; `json_bytes_total` - сумма всех ответов; `json_bytes_first` - сумма до первых строк; - `pages_requested` - число запросов страниц (`offset=`); на «до» всегда 0 (экран грузит всё одним запросом) - как число страниц с «после» не сравнивать; - `placeholder_frame_share` - доля кадров прокрутки, где видна строка-заглушка; -- `filter_result_ms` - от ввода в поиск до стабилизации (и ответа сервера, если он был); пишется только в записи фильтра, там же нет `json_bytes_total/first` и `pages_requested` (они включили бы исходную загрузку). +- `filter_result_ms` - от ввода в поиск до стабилизации (и ответа сервера, если он был); пишется только в записи фильтра, там же нет `json_bytes_total/first`, `pages_requested` и `placeholder_frame_share` (они включили бы исходную загрузку). Сценарий `svod2-memory`: записи `scenario=svod2-memory`, `target=svod2/` (`go`, `rf`, `rf_scrolled` при `PERF_SVOD2_MEM_SCROLL=1`, `final`), метрики `heap_used_mb`, `nodes`, `documents`, `js_event_listeners`, `rows_visible`. diff --git a/perf/e2e/scenarios/scroll.spec.ts b/perf/e2e/scenarios/scroll.spec.ts index d34ba2e..0329532 100644 --- a/perf/e2e/scenarios/scroll.spec.ts +++ b/perf/e2e/scenarios/scroll.spec.ts @@ -198,24 +198,27 @@ async function measure(browser: import('@playwright/test').Browser, o: RunOpts) await page.getByLabel('ССП/РФ').click(); await page.getByRole('option', { name: 'Все ССП/РФ' }).click(); await page.keyboard.press('Escape'); + // first_rows_ms: raf-наблюдатель в странице (момент, когда строк данных >= MIN_ROWS); ожидание и отказ - прежний waitRows + await page.evaluate(([sel, rs, min]) => { + const st = { at: 0 }; + (window as any).__firstRows = st; + const tick = () => { + if (document.querySelectorAll(`${sel} ${rs}`).length >= (min as number)) { st.at = Date.now(); return; } + requestAnimationFrame(tick); + }; + requestAnimationFrame(tick); + }, [CONTAINER, rowSel, MIN_ROWS]); tLoad = Date.now(); await page.getByRole('button', { name: 'Загрузить' }).click(); } let rows: number; let firstRowsMs = 0; try { + rows = await waitRows(page, rowSel); if (t.kind === 'svod2') { - // опрос каждый кадр (raf), а не раз в 500 мс: иначе first_rows_ms квантуется шагом опроса - await page.waitForFunction( - ([sel, rs, min]) => document.querySelectorAll(`${sel} ${rs}`).length >= (min as number), - [CONTAINER, rowSel, MIN_ROWS] as const, - { polling: 'raf', timeout: LOAD_TIMEOUT }, - ); - firstRowsMs = Date.now() - tLoad; + const at = await page.evaluate(() => (window as any).__firstRows?.at as number | undefined); + firstRowsMs = (at || Date.now()) - tLoad; bytes.markFirstRows(); - rows = await waitRows(page, rowSel); - } else { - rows = await waitRows(page, rowSel); } } catch (e) { if (!(e instanceof RowsError)) throw e;