Android 8.0 boot time optimization
background
In the embedded industry, some devices equipped with Android system cannot be accepted due to the long boot time.
introduce
Startup time is an important part of system performance, because users must wait for startup to complete before using the device.
For devices such as cars that often have cold starts, a shorter start-up time is crucial (no one likes to wait for tens of seconds before entering the navigation destination).
Android 8.0 supports several improvements to a range of components, which can reduce startup time.
The following table summarizes these performance improvements (measured on Google Pixel and Pixel XL devices).
assembly | improvement |
---|---|
Boot loader | 1.6 seconds saved by removing UART logs 0.4 seconds saved by changing from GZIP to LZ4 |
Device kernel | 0.3 seconds saved by removing unused kernel configurations and reducing driver size 0.3 seconds saved by DM verity pre extraction optimization Save 0.15 seconds by removing unnecessary wait / test from the driver By removing CONFIG_CC_OPTIMIZE_FOR_SIZE, saving 0.12 seconds |
I/O adjustment | The normal startup time is saved by 2 seconds, and the first startup time is saved by 25 seconds |
init.*.rc | Save 1.5 seconds by running the init command in parallel 0.25 seconds saved by starting zygote early 0.22 seconds saved by cpuset adjustment |
Start animation | When fsck is not triggered, the start time of the start animation is 2 seconds earlier, while when fsck is triggered, the start animation is much larger Saves 5 seconds on Pixel XL by immediately turning off startup animation |
SELinux policy | 0.2 seconds saved by genfscon |
Optimize boot loader
To optimize the boot loader to reduce startup time, follow these steps:
1. For logging:
- Stop writing logs to UART because it may take a long time to process if there are many log records. (on Google Pixel devices, we found that this slows down the boot loader by 1.5 seconds), and the production software turns off the UART log.
- Record only error conditions and consider storing other information in memory with a separate retrieval mechanism.
2. For kernel decompression, consider using LZ4 instead of GZIP for contemporary hardware (for example Patch).
Note that different kernel compression options have different loading and decompression times, and some options may be more appropriate for specific hardware than others.
3. Check whether there is unnecessary waiting time during entering de dithering / special mode, and minimize such time.
4. Pass the startup time spent in the boot loader to the kernel in the form of command line.
5. Check the CPU clock and consider that kernel loading and initialization I/O are carried out in parallel (multi-core support is required).
Optimize kernel
Follow these tips to optimize the kernel to reduce startup time.
Minimize device defconfig
Minimizing the kernel configuration can reduce the kernel size, so as to load, decompress, initialize and reduce the attack surface more quickly.
To optimize device defconfig:
1. Identify unused drivers. Look at the / dev and / sys directories and look for nodes with regular SELinux tags that indicate that the corresponding nodes are not configured for user space access. If such a node is found, remove it.
2. Unset unused configurations. View the generated by the kernel version config file to explicitly unset all configurations that are enabled by default but not used. For example, we removed the following unused configurations from Google Pixel:
CONFIG_ANDROID_LOGGER=y CONFIG_IMX134=y CONFIG_IMX132=y CONFIG_OV9724=y CONFIG_OV5648=y CONFIG_GC0339=y CONFIG_OV8825=y CONFIG_OV8865=y CONFIG_s5k4e1=y CONFIG_OV12830=y CONFIG_USB_EHCI_HCD=y CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST=y CONFIG_IKCONFIG=y CONFIG_RD_BZIP2=y CONFIG_RD_LZMA=y CONFIG_TI_DRV2667=y CONFIG_CHR_DEV_SCH=y CONFIG_MMC=y CONFIG_MMC_PERF_PROFILING=y CONFIG_MMC_CLKGATE=y CONFIG_MMC_PARANOID_SD_INIT=y CONFIG_MMC_BLOCK_MINORS=32 CONFIG_MMC_TEST=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_MSM=y CONFIG_MMC_SDHCI_MSM_ICE=y CONFIG_MMC_CQ_HCI=y CONFIG_MSDOS_FS=y # CONFIG_SYSFS_SYSCALL is not set CONFIG_EEPROM_AT24=y # CONFIG_INPUT_MOUSEDEV_PSAUX is not set CONFIG_INPUT_HBTP_INPUT=y # CONFIG_VGA_ARB is not set CONFIG_USB_MON=y CONFIG_USB_STORAGE_DATAFAB=y CONFIG_USB_STORAGE_FREECOM=y CONFIG_USB_STORAGE_ISD200=y CONFIG_USB_STORAGE_USBAT=y CONFIG_USB_STORAGE_SDDR09=y CONFIG_USB_STORAGE_SDDR55=y CONFIG_USB_STORAGE_JUMPSHOT=y CONFIG_USB_STORAGE_ALAUDA=y CONFIG_USB_STORAGE_KARMA=y CONFIG_USB_STORAGE_CYPRESS_ATACB=y CONFIG_SW_SYNC_USER=y CONFIG_SEEMP_CORE=y CONFIG_MSM_SMEM_LOGGING=y CONFIG_IOMMU_DEBUG=y CONFIG_IOMMU_DEBUG_TRACKING=y CONFIG_IOMMU_TESTS=y CONFIG_MOBICORE_DRIVER=y # CONFIG_DEBUG_PREEMPT is not set
3. Remove the configuration that causes unnecessary tests to run on each startup. While such configurations (i.e., CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST) are useful during development, they should be removed from the official kernel.
Minimize driver size
If the corresponding functions are not used, some drivers in the device kernel can be removed to further reduce the kernel size.
For example, if WLAN is connected through PCIe, SDIO support will not be used, so it should be removed at compile time.
For details, see Google Pixel kernel: Network: Wireless: CNSS: add options to disable SDIO support.
Remove compiler optimizations for size
Remove config_ CC_ OPTIMIZE_ FOR_ Kernel configuration of size. This flag was introduced when it was initially assumed that a smaller code size would produce a hot cache hit (and therefore faster). However, as modern mobile SoC becomes more powerful, this assumption no longer holds.
In addition, removing this flag allows the compiler to warn against uninitialized variables when config exists_ CC_ OPTIMIZE_ FOR_ This feature is disabled in the Linux kernel when the size flag is used (this change alone has helped us find a lot of meaningful errors in some Android device drivers).
Delay initialization
Many processes start during device startup, but only components in the critical path (bootloader > kernel > init > file system mount > zygote > system server) will directly affect the startup time. Execute initcall during kernel startup to identify peripherals / components that start slowly and are not important for starting the init process, and then
Start by moving these peripherals / components into loadable kernel modules and delaying them until later in the startup process. The move in asynchronous device / driver probe also helps to start slow components in the kernel > init critical path in parallel.
BoardConfig - common . mk : BOARD_KERNEL_CMDLINE += initcall_debug ignore_loglevel driver : . probe_type = PROBE_PREFER_ASYNCHRONOUS ,
Note: eprobeder support must be added to properly solve the driver dependency problem.
Optimize I/O efficiency
Improving I/O efficiency is very important to shorten the startup time. Reading any unnecessary content should be delayed until after startup (on Google Pixel, about 1.2GB of data should be read at startup).
Adjust file system
When a file is read from the beginning or blocks are read in sequence, the pre read Linux kernel will start, which requires adjusting the I/O scheduler parameters specially used for startup (different from the workload characteristics of ordinary applications).
Devices that support seamless (A/B) updates benefit greatly from file system tuning when they are first started (for example, Google Pixel's startup time is reduced by 20 seconds). For example, we adjusted the following parameters for Google Pixel:
# 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 ...
other
- Configuring DM with kernel_ VERITY_ HASH_ PREFETCH_ MIN_ Size (the default size is 128) to enable DM verity hash prefetch size.
- In order to improve the stability of the file system and cancel the mandatory check at each startup, please go to boardconfig Set target in MK_ USES_ Mke2fs to use the new ext4 generation tool.
Analyze I/O
To understand I/O activity during startup, use the kernel ftrace data (which is also used by systrace):
trace_event=block,ext4 in BOARD_KERNEL_CMDLINE
To subdivide file access permissions for each file, make the following changes to the kernel (development kernel only; do not apply these changes in the official kernel):
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 , int flags , int mode , 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 , flags , 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 );
Use the following script to help analyze startup performance.
- packages/services/Car/tools/bootanalyze/bootanalyze.py: be responsible for measuring the start-up time and analyzing the important steps in the start-up process in detail.
- packages/services/Car/tools/io_analysis/check_file_read.py boot_trace: provides access information for each file.
- packages/services/Car/tools/io_analysis/check_io_trace_all.py boot_trace: provides system level segmentation information.
Optimize init. * rc
Init is the connection process from the kernel to the framework. Devices usually spend a few seconds in different init stages.
Running tasks in parallel
Although the current Android init is almost a single threaded process, you can still perform some tasks in parallel.
-
Execute slow commands in the Shell script service, and then join later by waiting for specific properties. Android 8.0 passes the new wait_ for_ The property command supports this use case.
-
Identify slow operations in init. The system will record the init command exec/wait_for_prop or any operation that takes a long time (in Android 8.0, any command that takes more than 50 milliseconds). For example:
init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms
Viewing this log may identify opportunities for improvement.
-
Start the service and enable peripherals in the critical path early. For example, some SOC S need to start security related services before starting SurfaceFlinger. View the system log when ServiceManager returns "wait for service" - this usually indicates that the dependent service must be started first.
-
Remove init. * All unused services and commands in RC. As long as the services and commands not used in init in the early stage should be postponed until the startup is completed.
Note: the "properties" service is part of the init process, so calling setproperty during startup may cause a long delay (if init is busy executing built-in commands).
Use scheduler to adjust
Use the scheduler to adjust to start the device early. The following is an example from Google Pixel:
on init # update cpusets now that processors are up write /dev/cpuset/top-app/cpus 0-3 write /dev/cpuset/foreground/cpus 0-3 write /dev/cpuset/foreground/boost/cpus 0-3 write /dev/cpuset/background/cpus 0-3 write /dev/cpuset/system-background/cpus 0-3 # set default schedTune value for foreground/top-app (only affects EAS) write /dev/stune/foreground/schedtune.prefer_idle 1 write /dev/stune/top-app/schedtune.boost 10 write /dev/stune/top-app/schedtune.prefer_idle 1
Some services may need to be prioritized during startup. For example:
init.zygote64.rc: service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server class main priority -20 user root ...
Start zygote early
Devices with file level encryption can start zygote at an early stage of the zygote start trigger (by default, zygote starts in the main class, much later than zygote start).
When doing so, make sure that zygote is allowed to run on all CPUs (because the wrong cpuset setting may force zygote to run on a specific CPU).
Disable power saving settings
During device startup, the power saving settings of components such as UFS and / or CPU regulator can be disabled.
Please note: in order to improve efficiency, the power saving setting should be enabled in charger mode.
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
Postpone non critical initialization
Non critical initialization (such as ZRAM) can be postponed to boot_complete.
on property : sys . boot_completed = 1 # Enable ZRAM on boot_complete swapon_all / vendor / etc / fstab . $ { ro . hardware }
Optimize startup animation
Follow these tips to optimize the startup animation.
Configured to start early
Android 8.0 supports starting animation early before loading user data partitions. However, even if the new ext4 tool chain is used in Android 8.0, the system will trigger fsck regularly for security reasons, resulting in a delay in starting the bootanimation service.
To enable bootanimation to start early, divide fstab loading into the following two stages:
- In the early stages, only the partitions that do not need to run checks (such as system / and vendor /) are loaded, and then the animation service and its dependencies (such as service manager and surface linker) are started.
- In the second stage, the partition that needs to run the check (for example, data /) is loaded.
Starting the animation will start more quickly (with a constant start time) and will not be affected by fsck.
End neatly
After receiving the exit signal, bootanimation will play the last part, and the length of this part will prolong the startup time. The quick start system does not need a long animation. If the animation is long, it will not reflect any improvement to a great extent. We recommend shortening the loop playback and ending time.
Optimize SELinux
Follow these tips to optimize SELinux to reduce startup time.
- Use concise regular expressions (regex). File in_ When sys/devices in contexts matches SELinux policy, poorly formed regular expressions may cause a lot of overhead. For example, the regular expression / sys/devices /* abc.* (/.*)? Incorrectly forced scanning of all / sys/devices subdirectories containing "ABC", resulting in / sys/devices/abc and / sys/devices/xyz/abc becoming matches. If you modify this regular expression to / sys/devices/[^/]*abc [^ /] * (/. *), Only / sys/devices/abc will be a match.
- Move label to genfscon . This existing SELinux function passes file matching prefixes to the kernel of SELinux binaries, and the kernel applies these prefixes to the file system generated by the kernel. This also helps fix files created by the incorrectly marked kernel, preventing possible contention between user space processes (trying to access these files before re marking).
Tools and methods
Use the following tools to help you collect data for your optimization goals.
bootchart
bootchart can provide CPU and I/O load segmentation of all processes for the whole system. The tool does not need to rebuild the system image, and can be used as a quick health check before entering systrace.
To enable bootchart, run the following command:
adb shell 'touch /data/bootchart/enabled' adb reboot
After the device starts, obtain the startup chart:
$ANDROID_BUILD_TOP / system / core / init / grab - bootchart . sh
When finished, delete / data/bootchart/enabled to prevent date data from being collected every time.
systrace
Systrace allows kernel and Android trace records to be collected during startup. The visualization of systrace can help analyze the specific problems in the startup process. (however, to view the average or cumulative number during the whole startup process, it is more convenient to directly view the kernel trace record).
To enable systrace during startup (this will enable the tracing function, which is disabled by default), in frameworks / native / atrace / atrace RC, set
write /sys/kernel/debug/tracing/tracing_on 0
Change to:
#write /sys/kernel/debug/tracing/tracing_on 0
In device MK file, add the following line:
PRODUCT_PROPERTY_OVERRIDES += debug . atrace . tags . enableflags = 802922
In the device boardconfig MK file, add the following:
BOARD_KERNEL_CMDLINE := ... trace_buf_size = 64M trace_event = sched_wakeup , sched_switch , sched_blocked_reason , sched_cpu_hotplug
To get a detailed I/O analysis, you also need to add blocks and ext4; In the device specific init RC file, make the following changes:
- on property:sys.boot_completed=1 (this stops tracing after startup is complete)
- write /d/tracing/tracing_on 0
- write /d/tracing/events/ext4/enable 0
- write /d/tracing/events/block/enable 0
After the device is started, obtain the tracking record:
adb root && adb shell "cat /d/tracing/trace" < boot_trace ./external/chromium-trace/catapult/tracing/bin/trace2html boot_trace --output boot_trace.html
Note: Chrome cannot handle files that are too large. Consider using tail, head, or grep to split the boot_trace file to get the necessary part. Due to too many events, I/O analysis usually needs to directly analyze the obtained boot_trace