В этом документе приведены рекомендации для партнеров по ускорению загрузки определенных устройств Android. Время загрузки – важный показатель производительности системы, поскольку пользователям приходится ждать, пока устройство загрузится, прежде чем они смогут им воспользоваться. Для устройств, которые часто загружаются с нуля, например автомобилей, быстрая загрузка критически важна. Никому не понравится ждать десятки секунд, чтобы просто ввести пункт назначения в навигаторе.
В Android 8.0 время загрузки сокращено за счет улучшений в разных компонентах. В таблице ниже приведены данные об улучшении производительности (на основе измерений на устройствах Google Pixel и Pixel XL).
| Компонент | Улучшение |
|---|---|
| Загрузчик |
|
| Ядро устройства |
|
| Настройка ввода-вывода |
|
| init.*.rc |
|
| Анимированная заставка при запуске |
|
| Правила SELinux | Сохранено 0,2 с с помощью genfscon |
Оптимизация загрузчика
Чтобы оптимизировать загрузчик для ускорения загрузки:
- Для ведения журналов:
- Отключите запись журнала в UART, так как при большом количестве записей это может занять много времени. На устройствах Google Pixel это замедляет загрузчик на 1, 5 секунды.
- Регистрируйте только ошибки и сохраняйте другую информацию в памяти с помощью отдельного механизма извлечения.
- Для распаковки ядра на современном оборудовании рекомендуется использовать LZ4 вместо GZIP (пример патча). Учтите, что разные варианты сжатия ядра могут иметь разное время загрузки и распаковки, и некоторые варианты могут работать лучше, чем другие, для вашего оборудования.
- Проверьте, не слишком ли долго устройство ждет перед переходом в специальный режим или устранением дребезга, и сократите это время.
- Передавать время загрузки, потраченное в загрузчике операционной системы, в ядро в виде cmdline.
- Проверьте тактовую частоту ЦП и рассмотрите возможность распараллеливания (требуется поддержка многоядерности) для загрузки ядра и инициализации I/O.
Оптимизация эффективности ввода-вывода
Повышение эффективности I/O имеет решающее значение для ускорения загрузки. Все, что не нужно для загрузки, следует отложить на более поздний этап. На устройстве Google Pixel при загрузке считывается около 1,2 ГБ данных.
Настройте файловую систему
Упреждающее чтение ядра Linux запускается, когда файл считывается с начала или когда блоки считываются последовательно, что требует настройки параметров планировщика I/O специально для загрузки (которая имеет другие характеристики рабочей нагрузки, чем обычные приложения).
Устройства, поддерживающие параллельные обновления (A/B), получают значительное преимущество от настройки файловой системы при первой загрузке (например, 20 секунд на Google Pixel). Например, для Google Pixel мы настроили следующие параметры:
on late-fs
# boot time fs tune
# boot time fs tune
write /sys/block/sda/queue/iostats 0
write /sys/block/sda/queue/scheduler cfq
write /sys/block/sda/queue/iosched/slice_idle 0
write /sys/block/sda/queue/read_ahead_kb 2048
write /sys/block/sda/queue/nr_requests 256
write /sys/block/dm-0/queue/read_ahead_kb 2048
write /sys/block/dm-1/queue/read_ahead_kb 2048
on property:sys.boot_completed=1
# end boot time fs tune
write /sys/block/sda/queue/read_ahead_kb 512
...Другое
- Включите размер предварительной выборки хеша dm-verity, используя конфигурацию ядра DM_VERITY_HASH_PREFETCH_MIN_SIZE (размер по умолчанию – 128).
- Чтобы повысить стабильность файловой системы и отказаться от принудительной проверки, которая выполняется при каждой загрузке, используйте новый инструмент создания ext4, задав значение TARGET_USES_MKE2FS в файле BoardConfig.mk.
Анализ ввода-вывода
Чтобы понять, какие операции ввода-вывода выполняются во время загрузки, используйте данные ftrace ядра (они также используются systrace):
trace_event=block,ext4 in BOARD_KERNEL_CMDLINE
Чтобы разбить доступ к файлам для каждого файла, внесите следующие изменения в ядро (только в ядро разработки; не используйте в производственных ядрах):
diff --git a/fs/open.c b/fs/open.c index 1651f35..a808093 100644 --- a/fs/open.c +++ b/fs/open.c @@ -981,6 +981,25 @@ } EXPORT_SYMBOL(file_open_root); +static void _trace_do_sys_open(struct file *filp, const struct open_how *how, long fd) +{ + char *buf; + char *fname; + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return; + fname = d_path(&filp->f_path, buf, PAGE_SIZE); + + if (IS_ERR(fname)) + goto out; + + trace_printk("%s: open(\"%s\", %d, %d) fd = %ld, inode = %ld\n", + current->comm, fname, how->flags, how->mode, fd, filp->f_inode->i_ino); +out: + kfree(buf); +} + long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) { struct open_flags op; @@ -1003,6 +1022,7 @@ } else { fsnotify_open(f); fd_install(fd, f); + _trace_do_sys_open(f, flags, mode, fd);
Используйте приведенные ниже скрипты, чтобы анализировать производительность при загрузке.
system/extras/boottime_tools/bootanalyze/bootanalyze.pyИзмеряет время загрузки с разбивкой по важным этапам процесса загрузки.system/extras/boottime_tools/io_analysis/check_file_read.py boot_traceПредоставляет информацию о доступе к каждому файлу.system/extras/boottime_tools/io_analysis/check_io_trace_all.py boot_trace– разбивка на уровне системы.
Оптимизация init.*.rc
Init – это мост от ядра до создания фреймворка. Обычно устройства проводят несколько секунд на разных этапах инициализации.
Параллельное выполнение задач
Хотя текущий процесс инициализации Android в основном однопоточный, некоторые задачи можно выполнять параллельно.
- Выполняйте медленные команды в сервисе сценариев командной строки и присоединяйте их позже, ожидая определенного свойства. В Android 8.0 для этого случая предусмотрена новая команда
wait_for_property. - Как определить медленные операции в init Система регистрирует команду init
exec/wait_for_prop или любое действие, занимающее много времени (в Android 8.0 – любую команду, выполнение которой занимает более 50 мс). Пример:
init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms
Проанализировав этот журнал, вы можете найти способы улучшить работу сайта.
- Запускайте сервисы и включайте периферийные устройства на ранних этапах критического пути. Например, некоторые SOC требуют запуска сервисов, связанных с безопасностью, до запуска SurfaceFlinger. Если ServiceManager возвращает сообщение "wait for service" (ожидание сервиса), проверьте системный журнал. Обычно это означает, что сначала нужно запустить зависимый сервис.
- Удалите все неиспользуемые сервисы и команды в init.*.rc. Все, что не используется на раннем этапе инициализации, должно быть отложено до завершения загрузки.
Примечание. Сервис Property является частью процесса init, поэтому вызов setproperty во время загрузки может привести к длительной задержке, если init занят встроенными командами.
Как использовать настройку планировщика
Используйте настройку планировщика для ранней загрузки. Пример для Google Pixel:
on init
# boottime stune
write /dev/stune/schedtune.prefer_idle 1
write /dev/stune/schedtune.boost 100
on property:sys.boot_completed=1
# reset stune
write /dev/stune/schedtune.prefer_idle 0
write /dev/stune/schedtune.boost 0
# or just disable EAS during boot
on init
write /sys/kernel/debug/sched_features NO_ENERGY_AWARE
on property:sys.boot_completed=1
write /sys/kernel/debug/sched_features ENERGY_AWAREНекоторым сервисам может потребоваться повышенный приоритет во время загрузки. Пример:
init.zygote64.rc:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
...Ранний запуск Zygote
На устройствах с шифрованием на основе файлов процесс zygote можно запустить раньше, используя триггер zygote-start (по умолчанию zygote запускается в классе main, что происходит гораздо позже, чем zygote-start). При этом убедитесь, что zygote может работать на всех процессорах (неправильные настройки cpuset могут заставить zygote работать на определенных процессорах).
Как отключить энергосбережение
Во время загрузки устройства можно отключить режим энергосбережения для таких компонентов, как UFS и/или регулятор ЦП.
Внимание! В режиме зарядки для повышения эффективности следует включить энергосбережение.
on init # Disable UFS powersaving write /sys/devices/soc/${ro.boot.bootdevice}/clkscale_enable 0 write /sys/devices/soc/${ro.boot.bootdevice}/clkgate_enable 0 write /sys/devices/soc/${ro.boot.bootdevice}/hibern8_on_idle_enable 0 write /sys/module/lpm_levels/parameters/sleep_disabled Y on property:sys.boot_completed=1 # Enable UFS powersaving write /sys/devices/soc/${ro.boot.bootdevice}/clkscale_enable 1 write /sys/devices/soc/${ro.boot.bootdevice}/clkgate_enable 1 write /sys/devices/soc/${ro.boot.bootdevice}/hibern8_on_idle_enable 1 write /sys/module/lpm_levels/parameters/sleep_disabled N on charger # Enable UFS powersaving write /sys/devices/soc/${ro.boot.bootdevice}/clkscale_enable 1 write /sys/devices/soc/${ro.boot.bootdevice}/clkgate_enable 1 write /sys/devices/soc/${ro.boot.bootdevice}/hibern8_on_idle_enable 1 write /sys/class/typec/port0/port_type sink write /sys/module/lpm_levels/parameters/sleep_disabled N
Отложить инициализацию некритических функций
Необязательную инициализацию, например ZRAM, можно отложить до boot_complete.
on property:sys.boot_completed=1 # Enable ZRAM on boot_complete swapon_all /vendor/etc/fstab.${ro.hardware}
Как оптимизировать анимацию загрузки
Чтобы оптимизировать анимацию загрузки, следуйте приведенным ниже советам.
Как настроить ранний старт
В Android 8.0 анимацию загрузки можно запускать раньше, до монтирования раздела userdata. Однако даже при использовании новой цепочки инструментов ext4 в Android 8.0 fsck по-прежнему периодически запускается из соображений безопасности, что приводит к задержке запуска службы bootanimation.
Чтобы анимация загрузки запускалась раньше, разделите монтирование fstab на два этапа:
- На раннем этапе монтируйте только разделы (например,
system/иvendor/), которые не требуют проверок во время выполнения, а затем запустите сервисы анимации загрузки и их зависимости (например, servicemanager и surfaceflinger). - На втором этапе смонтируйте разделы (например,
data/), для которых требуется выполнить проверки.
Анимация загрузки будет запускаться намного быстрее (и всегда в одно и то же время) независимо от fsck.
Завершить очистку
После получения сигнала выхода bootanimation воспроизводит последнюю часть, длина которой может замедлить время загрузки. Если система загружается быстро, нет необходимости в длительных анимациях, которые могут скрыть любые улучшения. Мы рекомендуем сделать и повторяющийся фрагмент, и финал короткими.
Как оптимизировать SELinux
Используйте следующие советы, чтобы оптимизировать SELinux для сокращения времени загрузки.
- Используйте понятные регулярные выражения. Некорректно составленные регулярные выражения могут привести к значительным накладным расходам при сопоставлении правил SELinux для
sys/devicesвfile_contexts. Например, регулярное выражение/sys/devices/.*abc.*(/.*)?ошибочно заставляет сканировать все подкаталоги/sys/devices, содержащие "abc", что позволяет находить совпадения как для/sys/devices/abc, так и для/sys/devices/xyz/abc. Если изменить регулярное выражение на/sys/devices/[^/]*abc[^/]*(/.*)?, то соответствие будет найдено только для/sys/devices/abc. - Переместите ярлыки в genfscon. Эта функция SELinux передает префиксы, соответствующие файлам, в ядро в двоичном файле SELinux, где ядро применяет их к файловым системам, созданным ядром. Это также помогает исправить неправильно помеченные файлы, созданные ядром, предотвращая состояние гонки, которое может возникнуть между процессами пользовательского пространства, пытающимися получить доступ к этим файлам до того, как произойдет повторная маркировка.
Инструменты и методы
Чтобы собирать данные для целей оптимизации, используйте следующие инструменты:
Bootchart
Bootchart предоставляет информацию о загрузке ЦП и вводе-выводе для всех процессов в системе. Для этого не нужно пересобирать образ системы. Это можно сделать быстро, чтобы проверить работоспособность системы перед использованием systrace.
Чтобы включить bootchart:
adb shell 'touch /data/bootchart/enabled'adb reboot
После загрузки получите диаграмму загрузки:
$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
После этого удалите /data/bootchart/enabled, чтобы данные не собирались каждый раз.
bootchart.png не существует, выполните следующие действия:
- Выполните следующие команды:
sudo apt install python-is-python3cd ~/Documentsgit clone https://github.com/xrmx/bootchart.gitcd bootchart/pybootchartguimv main.py.in main.py - Измените
$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh, чтобы он указывал на локальную копиюpybootchartgui(находящуюся в~/Documents/bootchart/pybootchartgui.py).
Systrace
Systrace позволяет собирать данные трассировки ядра и Android во время загрузки. Визуализация данных systrace помогает анализировать определенные проблемы, возникающие при загрузке. Однако, чтобы проверить среднее или общее количество за всю загрузку, проще посмотреть трассировку ядра напрямую.
Чтобы включить systrace во время загрузки:
- В
frameworks/native/cmds/atrace/atrace.rcизмените:write /sys/kernel/debug/tracing/tracing_on 0 write /sys/kernel/tracing/tracing_on 0
Кому:
# write /sys/kernel/debug/tracing/tracing_on 0 # write /sys/kernel/tracing/tracing_on 0
- Добавьте в файл
device.mkследующую строку:PRODUCT_PROPERTY_OVERRIDES += debug.atrace.tags.enableflags=802922 PRODUCT_PROPERTY_OVERRIDES += persist.traced.enable=0
- В файл
BoardConfig.mkустройства добавьте следующее:BOARD_KERNEL_CMDLINE := ... trace_buf_size=64M trace_event=sched_wakeup,sched_switch,sched_blocked_reason,sched_cpu_hotplug
- Добавьте в файл
init.rcдля устройства следующий код:on property:sys.boot_completed=1 // This stops tracing on boot complete write /d/tracing/tracing_on 0 write /d/tracing/events/ext4/enable 0 write /d/tracing/events/f2fs/enable 0 write /d/tracing/events/block/enable 0
-
После загрузки получите трассировку:
adb root && adb shell atrace --async_stop -z -c -o /data/local/tmp/boot_traceadb pull /data/local/tmp/boot_trace$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py --from-file=boot_trace
Это позволит включить отслеживание (по умолчанию оно отключено).
Для подробного анализа операций ввода-вывода также добавьте block, ext4 и f2fs.