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

8.5 KiB
Raw Permalink Blame History

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 строгий), поэтому профиль пишется скриптом.

Как обновлять профиль

# эмулятор 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 обычно заметно больше.