P5: гейты холостого хода в pop_loose_tick — минус 33 840 тактов на кадре
Замер 11/15 показал, что loose-механика берёт 28 872 такта в комнате, где не анимируется ни одна плита и не летит ни один кусок. Раскладка зондами m9..m12: два цикла по тайлам 9 852, обход 14 слотов mob 12 090, поиск куска над головой Кида 5 868 (там ещё и банковый трамплин). Два гейта: loose_any (статик pop_map.c) — «идёт ли анимация плит». Ставят пять мест записи ненулевой фазы: make_loose_fall, ветка потолка в check_press, do_knock для обоих рядов и восстановление фазы из room_modif при входе в комнату. Снимает его сам цикл, по факту прохода, в котором не осталось ни одной живой фазы. pop_mob_busy (резидент pop_state.c) — «занят ли слот падающего куска» (active или дочистка clean). Ставит mob_alloc, снимает обход по факту пустой таблицы. В резиденте, а не в pop_room.c, потому что читает его pop_map из банка 3, а писучие статики банкового модуля наружу не видны. Гейт отвечает не на «есть ли в комнате плиты», а на «идёт ли анимация»: у лежащей плиты-потолка фаза нулевая, и крутить нечего (вопрос пользователя). Асимметрия намеренная — ложная единица стоит одного холостого прохода, ложный ноль стоит застывшей навсегда плиты, поэтому взвод стоит рядом с КАЖДОЙ записью, а снятие только по факту пустого прохода. Стало: 132 / 996 / 546, вся функция 28 872 -> 2 760. На кадре работа 801 768 -> 767 928. Ожидание по реестру было -28 000. Покрытие: новый phys_loose_gate_survives_room_change на пятое место взвода (фаза восстановлена входом в комнату) — единственное, которое не прогонял ни один тест, и дающее самый тихий отказ. Мутационная проверка: со снятым взводом тест падает (фаза 3 вместо 4). Заодно отладочный старт сразу в целевую комнату: make ROOM=15 POS=2 (дефолт), roomtest стартует в 11/15 с Кидом в (0,2). kid_init ставит x = x_bump[col] + TILE_SIZEX, а это левая граница СЛЕДУЮЩЕЙ колонки — с неё физика относила Кида в тайл чомпера, и он погибал на старте (найдено пользователем). Сдвиг внутрь на 2: колонку определяет весовая точка кадра, поэтому число снято замером, а не выведено геометрией. План работ между сессиями — docs/perf_registry.md §4: очередь позиций со статусами, текущий бюджет сцены, рецепт её воспроизведения и метод замера. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -103,6 +103,19 @@ heal 32×64 ≈ 33 700, значит на один `draw_tile` уходит ≈
|
||||
— та же болезнь (полный тайл там, где хватает полосы), но на другом
|
||||
материале.
|
||||
|
||||
## 5. Журнал правок по этой сцене
|
||||
|
||||
| дата | правка | работа | синяя | зелёная | циан |
|
||||
|---|---|---:|---:|---:|---:|
|
||||
| 2026-08-19 | базовый замер (`09f32ce`) | 801 768 | 293 238 | 320 916 | 187 758 |
|
||||
| 2026-08-19 | **P5**: гейты холостого хода в `pop_loose_tick` | **767 928** | 285 864 | 294 384 | 187 764 |
|
||||
| | | −33 840 | −7 374 | −26 532 | +6 |
|
||||
|
||||
Циан не изменился (+6 тактов — шум), и это ожидаемо: `loose_tick` целиком
|
||||
лежит в зелёной. Синяя просела на 7 374 без прямой причины в правке —
|
||||
скорее всего перераскладка кода банка 3 компилятором; проверять отдельно
|
||||
не стали, знак верный.
|
||||
|
||||
**Полная очередь оптимизаций с оценками — [`perf_registry.md`](perf_registry.md).**
|
||||
Там же разложена цена одного блита фона по этапам (замер 2026-08-19, 1603
|
||||
блита) и модель зелёной фазы этой сцены.
|
||||
|
||||
@@ -120,13 +120,41 @@ heal 141 048 + отрисовка 148 566 = 290 000 тактов (36 % рабо
|
||||
Все четыре — низкий риск, механическая работа. Вместе снимают ~3 700 из
|
||||
6 126 фиксированных.
|
||||
|
||||
### P5. `pop_loose_tick` при пустой комнате — 28 872 [замер: −28 000]
|
||||
### P5. `pop_loose_tick` при пустой комнате — 28 872 → 2 760 ✅ СДЕЛАНО 2026-08-19
|
||||
|
||||
В комнате 15 нет ни одной loose-плиты, а фаза всё равно платит 9 % зелёной.
|
||||
Записано в `perf_backlog.md` §7 как 27 438 — цифра подтвердилась в другой
|
||||
сцене, значит это постоянная плата, а не особенность 13/23.
|
||||
**Получено −26 112 внутри функции, −33 840 на кадре** (замер до/после в
|
||||
11/15). Оценка была −28 000.
|
||||
|
||||
Нужен ранний выход по «в комнате нет плит и нет летящих кусков».
|
||||
Раскладка холостого хода (замер зондами m9..m12) и что с ней стало:
|
||||
|
||||
| участок | было | стало |
|
||||
|---|---:|---:|
|
||||
| два цикла по тайлам (30 + 10 позиций) | 9 852 | **132** |
|
||||
| `pop_loose_mob_tick` (обход 14 слотов) | 12 090 | **996** |
|
||||
| `check_loose_fall_on_kid` (трамплин + обход) | 5 868 | **546** |
|
||||
| вход + хвост | 1 062 | 1 086 |
|
||||
| **итого** | **28 872** | **2 760** |
|
||||
|
||||
Сделано двумя гейтами:
|
||||
|
||||
- `loose_any` (статик `pop_map.c`) — «идёт ли анимация плит». Ставится в
|
||||
пяти местах записи ненулевой фазы, снимается САМИМ циклом по факту
|
||||
прохода, где не осталось ни одной живой фазы;
|
||||
- `pop_mob_busy` (резидент `pop_state.c`) — «занят ли хоть один слот
|
||||
падающего куска» (`active` или дочистка `clean`). Ставит `mob_alloc`,
|
||||
снимает обход по факту пустой таблицы. В резиденте, а не в `pop_room.c`,
|
||||
потому что читает его `pop_map` из банка 3.
|
||||
|
||||
**Важно про границу:** гейт отвечает не на «есть ли в комнате плиты», а на
|
||||
«идёт ли анимация». У лежащей плиты-потолка фаза 0, и крутить нечего —
|
||||
вопрос пользователя 2026-08-19. Асимметрия намеренная: ложная единица
|
||||
стоит одного холостого прохода, ложный ноль — застывшей навсегда плиты,
|
||||
поэтому взвод стоит рядом с КАЖДОЙ записью, а снятие только по факту.
|
||||
|
||||
Покрытие: `phys_loose_floor_breaks` (взвод от шага и сотрясения) и новый
|
||||
`phys_loose_gate_survives_room_change` — на пятое место взвода
|
||||
(фаза восстановлена входом в комнату), которое не покрывал никто.
|
||||
Мутационная проверка: со снятым взводом тест падает.
|
||||
|
||||
### P6. Цикл `pop_process_trobs` — 59 100 без блитов пламени [гипотеза: −20 000 … −40 000]
|
||||
|
||||
@@ -193,7 +221,61 @@ BUG-LATTICE-DOORTOP, BUG-SEAM-WEDGE-1) — только отдельным за
|
||||
|
||||
---
|
||||
|
||||
## 4. Сводка: что сколько даёт в 11/15
|
||||
## 4. ПЛАН РАБОТ — состояние между сессиями
|
||||
|
||||
Рабочий чеклист. Правило: одна позиция = один заход = один коммит с замером
|
||||
до/после на сцене 11/15. Замер обязателен даже когда «очевидно» — из шести
|
||||
сделанных позиций две дали не то, что ожидалось.
|
||||
|
||||
| # | что | статус | факт |
|
||||
|---|---|---|---|
|
||||
| P5 | `loose_tick`: гейты холостого хода | ✅ **сделано** 2026-08-19 | −33 840 на кадре (ждали −28 000) |
|
||||
| P2 | замер логики двух Char | ⬜ следующее | нужна пересборка с зондами |
|
||||
| P6 | замер цикла `process_trobs` | ⬜ вместе с P2 | та же пересборка |
|
||||
| P1 | чомпер под факелом: только слой anim | ⬜ | ждём −160 000 |
|
||||
| P4 | накладные блита (4 правки) | ⬜ | ждём −28 000 |
|
||||
| P11 | мелочи с известной ценой | ⬜ | ждём −26 000 |
|
||||
| P10 | футпринт персонажа из физики | ⬜ | ждём −23 000 |
|
||||
| P7 | раскол `draw_tile` (G5) | ⬜ | для 13/23, не для 11/15 |
|
||||
| P8/P9 | HEAL-WIDTH + G8 | ⬜ обязательная | для сцен с плитами |
|
||||
| P12 | снять оснастку | ⬜ ПОСЛЕДНЕЙ | без неё не мерить |
|
||||
|
||||
**Текущее состояние бюджета 11/15:**
|
||||
|
||||
| | работа | синяя | зелёная | циан | период |
|
||||
|---|---:|---:|---:|---:|---:|
|
||||
| до оптимизации | 801 768 | 293 238 | 320 916 | 187 758 | 4 растра |
|
||||
| после P5 | **767 928** | 285 864 | 294 384 | 187 764 | 4 растра |
|
||||
|
||||
Цель — уложить РАБОТУ в 430 000 (тогда период станет 3 растра вместо 4;
|
||||
хвост кадра — три `gfx_wait_vsync`). Осталось снять **338 000**.
|
||||
|
||||
### Как воспроизвести сцену (важно для следующей сессии)
|
||||
|
||||
Сборка стартует прямо в ней: `make` (дефолты `LEVEL=11 ROOM=15 POS=2`) →
|
||||
`make hdd` → **полный рестарт MAME** (`chdman -f` даёт новый inode, memory
|
||||
`mame_hdd_rebuild_restart`) → в DSS набрать `d:` и `roomtest`. Кид встаёт в
|
||||
(0,2) лицом к чомперу, справа факел и страж — та самая сцена замеров.
|
||||
Штатный старт уровня возвращается через `make ROOM=`.
|
||||
|
||||
Проверка, что программа ЖИВА, обязательна перед любым чтением памяти:
|
||||
`cur_room` (0x97AA) должен лежать в 1..24 — на этом уже был сорван один
|
||||
замер (прочитаны два случайных байта остановленной машины).
|
||||
|
||||
### Метод замера
|
||||
|
||||
Зонды — `out (_io_border)` в `roomtest.c` (база модуля 0x42AD) плюс
|
||||
резидентные пустышки `pop_dbg_m*` из `pop_state.c`. Адреса брать ЗАНОВО из
|
||||
`.sprinter-cc-roomtest/roomtest.map` после каждой пересборки. Скрипты
|
||||
сессии: `perfrun.py <out> <сек> tag=addr ...` и `parseseq.py <файл> ПОСЛЕД`.
|
||||
|
||||
Цену отдельной РЕЗИДЕНТНОЙ функции можно снять вообще без пересборки:
|
||||
`bpset <вход>,1,{temp0=totalcycles; g}` плюс `bpset <точка>,1,{printf "…
|
||||
%d",totalcycles-temp0; g}`. Так разложен блит в §1.
|
||||
|
||||
---
|
||||
|
||||
## 5. Сводка: что сколько даёт в 11/15
|
||||
|
||||
| # | приём | эффект | тип оценки | риск |
|
||||
|---|---|---:|---|---|
|
||||
@@ -201,7 +283,7 @@ BUG-LATTICE-DOORTOP, BUG-SEAM-WEDGE-1) — только отдельным за
|
||||
| P3 | Кид перестанет будиться | −70 000 | модель | следствие P1 |
|
||||
| P2 | логика двух Char | −40 000 … −70 000 | гипотеза | ? |
|
||||
| P4 | накладные блита (4 правки) | −28 000 | модель | низкий |
|
||||
| P5 | `loose_tick` без плит | −28 000 | замер | низкий |
|
||||
| P5 | `loose_tick` без плит | ✅ −33 840 | ФАКТ | сделано |
|
||||
| P6 | цикл `process_trobs` | −20 000 … −40 000 | гипотеза | ? |
|
||||
| P10 | футпринт из физики | −23 000 | замер | средний |
|
||||
| P11 | мелочи (4 штуки) | −26 000 | замер | низкий |
|
||||
|
||||
Reference in New Issue
Block a user