ZapretKVN-android/docs/PERFORMANCE.md
loop-uh a0511c375a
Some checks failed
Android CI / build (push) Has been cancelled
Baseline Profile: правила запуска и скрипты записи и замеров
- app/src/main/baseline-prof.txt: 29 641 правило (4 344 — код приложения),
  записано на эмуляторе по обходу Главной, Серверов, Маршрутов и Настроек;
  AGP сам кладёт его в assets/dexopt/baseline.prof и переносит через R8.
- scripts/generate-baseline-profile.sh: сборка build type profileGen (release
  без R8, подпись одноразовым ключом), обход UI через uiautomator/input,
  сброс профиля SIGUSR1, profman -> отсортированный HRF.
- scripts/measure-startup-frames.sh: холодный старт и gfxinfo до/после,
  docs/PERFORMANCE.md с методом и честными цифрами (эмулятор, не телефон).
- verify-project.sh проверяет файл профиля, verify-release-candidate.sh
  требует baseline.prof(m) в релизном APK; release build type не менялся.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 23:16:30 +03:00

101 lines
8.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Zapret KVN — Baseline Profile и замеры скорости
| Поле | Значение |
|---|---|
| Файл правил | `app/src/main/baseline-prof.txt` (формат HRF, отсортирован, 29 641 правило, из них 4 344 — код приложения) |
| Генерация | `scripts/generate-baseline-profile.sh` |
| Замеры | `scripts/measure-startup-frames.sh` |
| Дата записи профиля и замеров | 14 октября 2026 года, эмулятор API 37 x86_64 (google_apis) |
AGP сам компилирует `baseline-prof.txt` в `assets/dexopt/baseline.prof(m)` релизного APK и при R8
переносит исходные имена на обфусцированные. Профили библиотек (Compose и др.) в APK были и раньше:
файл приложения добавляется к ним. Плагины `baselineprofile`/`macrobenchmark` не подключены
(`gradle/verification-metadata.xml` строгий), поэтому профиль пишется скриптом.
## Как обновлять профиль
```bash
# эмулятор google_apis (нужен `adb root`), только adb из SDK, не `adb` из PATH
$ANDROID_HOME/emulator/emulator -avd zapret_api37 -port 5560 -no-window -no-audio -no-snapshot-save &
scripts/generate-baseline-profile.sh --serial emulator-5560
```
Что делает скрипт:
1. Собирает `:app:assembleProfileGen` — тот же release, но без R8 (`isMinifyEnabled = false`), чтобы
записанный профиль содержал исходные имена классов. Сборка подписана одноразовым ключом
(`keytool` во временной папке, исчезает после работы); боевой JKS не используется, build type
`release` и выпускные скрипты не менялись.
2. Убирает из APK встроенный `assets/dexopt/baseline.prof*` и подписывает заново, иначе прошлые правила
«самоподтвердятся» в новом файле.
3. Ставит APK, делает `cmd package compile --reset`, импортирует 60 выдуманных ссылок VLESS через
`VIEW file://` (длинный список серверов; VPN не запускается и сеть серверов не нужна).
4. Три круга: холодный старт и обход Главной, Серверов (прокрутка, сортировка, поиск, смена
профиля), Маршрутов (выбор приложений, режим, правило, исходный JSON профиля) и страниц
Настроек. Узлы находятся по тексту через `uiautomator dump`, прокрутка и вкладки — `input`.
После каждого круга `killall -s SIGUSR1` заставляет ART сохранить профиль.
5. `profman --dump-classes-and-methods` даёт текст с флагами H/S/P; остаются пакеты
`io/github/zapretkvn`, `io/nekohasekai`, `androidx`, `kotlinx`, `kotlin`, `com/journeyapps`,
результат `LC_ALL=C sort -u` пишется в `baseline-prof.txt`.
`scripts/verify-project.sh` проверяет, что файл существует, отсортирован, состоит только из правил HRF
и содержит не менее 500 правил приложения, а `profileGen` не упомянут в выпускных скриптах.
`scripts/verify-release-candidate.sh` требует, чтобы в релизном APK был `assets/dexopt/baseline.prof`
и `.profm`. Профиль нужно перезаписывать после крупных правок экранов: правила для переименованных
классов просто не сработают, но и не вредят.
## Метод замеров
`scripts/measure-startup-frames.sh --before A.apk --after B.apk` сравнивает два release-APK (x86_64,
R8, одноразовая подпись одним ключом), собранных из одного коммита:
- `before` — без правил приложения (`baseline-prof.txt` убран; остались профили библиотек, то есть
то, что было в выпусках до этого);
- `after` — с `baseline-prof.txt`;
- `noprofile` (`--verify-baseline`, тот же APK, что `after`) — `cmd package compile -m verify -f`,
то есть без AOT вообще.
Для `before` и `after` профиль применяется так же, как в Macrobenchmark: широковещательная команда
`androidx.profileinstaller.action.INSTALL_PROFILE` (результат `1` — успех), затем
`cmd package compile -m speed-profile -f`. Перед каждым состоянием приложение ставится заново,
импортируются те же 60 серверов. Раундов два, порядок состояний чередуется.
- Холодный старт: `am start -W -S`, поле `TotalTime`; перед каждым запуском `force-stop` и
`echo 3 > /proc/sys/vm/drop_caches`; один прогревочный запуск не считается; 10 запусков на раунд.
- Кадры: `dumpsys gfxinfo <пакет> reset`, затем скрипт из `input swipe`/`input tap` — три раза по
6 прокруток списка Серверов вниз и вверх и три раза обход вкладок с прокруткой Настроек;
после этого `dumpsys gfxinfo`. Три прогона на раунд.
## Результаты
Эмулятор API 37, x86_64 с KVM, headless; хост в это время был загружен чужими задачами
(load average 13–23 на 32 ядрах). Это не телефон: ядра быстрые, JIT догоняет за секунды, GPU
программный. Цифры показывают направление и порядок величин, но не обещание для реальных устройств.
| Метрика | before | after | noprofile (verify) |
|---|---|---|---|
| Холодный старт, медиана 20 запусков, мс | 694 | 652 | 686 |
| Холодный старт, min–max, мс | 587–1747 | 572–745 | 628–857 |
| Медианы по раундам (1 / 2), мс | 798 / 632 | 660 / 622 | 671 / 703 |
| Кадров всего | 6 988 | 7 108 | 7 112 |
| Janky-кадры, % | 8,0 | 6,0 | 5,7 |
| p50, мс | 14 | 10 | 9 |
| p90, мс | 23 | 19 | 20 |
| p95, мс | 28 | 26 | 26 |
| p99, мс | 45 | 36 | 44 |
| Записей в профиле на устройстве | 9 945 | 15 374 | — |
| Размер `base.odex`, байт | 5 045 360 | 6 570 616 | 49 920 |
Перцентили — медианы по шести прогонам (`gfxinfo` считает время кадра в целых миллисекундах).
Что из этого следует:
- Профиль применился: записей в профиле на 55 % больше, скомпилированного кода на 30 % больше.
- Старт `after` на 6 % быстрее `before` по общей медиане, но во втором раунде разница 632 против 622 мс,
то есть в пределах шума; у `before` в первом раунде были выбросы 1,2–1,7 с, которые и тянут медиану.
- Кадры `after` немного лучше `before` (janky 6,0 % против 8,0 %, p99 36 против 45 мс), но
`before` в первом раунде мерился при самой большой нагрузке на хост (10,1 % против 5,9 % во втором).
- `noprofile` на этом эмуляторе не хуже `before`: на быстром хосте JIT и без профиля прогревает
приложение почти мгновенно. Значимого выигрыша от Baseline Profile эмулятор не показывает;
настоящую проверку нужно делать на физическом устройстве (arm64, слабые ядра, холодный старт после
установки), где разница между интерпретатором и AOT обычно заметно больше.