Some checks failed
Android CI / build (push) Has been cancelled
- 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>
101 lines
8.5 KiB
Markdown
101 lines
8.5 KiB
Markdown
# 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 обычно заметно больше.
|