最佳化開機時間

本文件將為 Android 裝置。開機時間是系統效能的重要元素 使用者必須等到啟動作業完成後才能使用裝置。裝置 例如較常啟動冷啟動的車輛、 時間至關重要 (沒有人喜歡等候數十秒以輸入 導航目的地)。

Android 8.0 支援多項改善措施,縮短啟動時間 方便您快速進行擴充下表摘要列出這些成效 (評估依據為 Google Pixel 和 Pixel XL 裝置)。

元件 提升幅度
系統啟動載入程式
  • 透過移除 UART 記錄儲存 1.6 秒
  • 從 GZIP 變更為 LZ4 即可省下 0.4 秒
裝置核心
  • 移除未使用的核心設定並縮減驅動程式大小,因此節省了 0.3 秒
  • 透過 dmverity 預先擷取最佳化功能節省了 0.3 秒
  • 儲存 0.15 秒,以便在驅動程式中移除不必要的等待/測試
  • 已儲存 0.12 秒,移除 CONFIG_CC_OPTIMIZE_FOR_SIZE
I/O 調整
  • 一般開機已儲存 2 秒
  • 首次開機時節省 25 秒
init.*.rc
  • 平行處理 init 指令節省了 1.5 秒
  • 及早啟動 zygote,省下 0.25 秒
  • 由 cpuset 調整儲存 0.22 秒
開機動畫
  • 啟動 2 秒,在未觸發任何網站的情況下啟動,且在開機時 未觸發的啟動程序
  • 在 Pixel XL 上儲存 5 秒,同時播放開機動畫立即關閉
SELinux 政策 genfscon 節省了 0.2 秒

最佳化系統啟動載入程式

如要改善系統啟動載入程式以縮短啟動時間,請按照下列步驟操作:

  • 記錄功能:
    • 停用將記錄寫入 UART 的功能,因為可能需要大量時間 。(我們發現 Google Pixel 裝置的系統啟動載入程式 1.5s 變慢)。
    • 僅記錄錯誤情況,並考慮將其他資訊儲存至記憶體 並使用不同的擷取機制
  • 針對核心解壓縮,請考慮將 LZ4 用於現代硬體 (範例:patch),而非 GZIP。注意事項 不同的核心壓縮選項可以有不同的載入方式 某些選項的效果可能會比其他選項來得好 特定硬體
  • 檢查去彈跳/特殊模式進入不必要的等待時間,並盡可能減少 具體做法是指示 Kubernetes 建立並維護 一或多個代表這些 Pod 的物件
  • 將系統啟動載入程式的啟動時間,以 cmdline 的形式傳遞到核心。
  • 檢查 CPU 時脈並考慮採用平行處理功能 (需要多核心支援) 用於核心載入和初始化 I/O。

最佳化 I/O 效率

如要加快啟動時間及讀取資料,關鍵在於改善 I/O 效率 所有非必要的資產都應延後到啟動 (在 Google Pixel 上 啟動時讀取約 1.2GB 的資料)。

調整檔案系統

開始讀取檔案或檔案開始讀取時,Linux kernel 讀取作業會開始執行 系統會依序讀取區塊,因此必須調整 I/O 排程器 參數,專供啟動程序使用 而非一般應用程式) 的特性

支援流暢 (A/B) 更新功能的裝置可從檔案系統中大獲益 在首次啟動時調整 (例如 Google Pixel 上的 20 秒)舉例來說 下列 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)。
  • 為提高檔案系統的穩定性,並降低以下裝置的強制檢查機制: 每次啟動時,您都必須將 TARGET_USES_MKE2FS BoardConfig.mk。

分析 I/O

如要瞭解啟動期間的 I/O 活動,請使用核心 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, 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);

使用下列指令碼有助於分析啟動效能。

  • 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 init 採用了較新的執行緒程序 仍可同時執行某些工作

  • 在 Shell 指令碼服務中執行慢指令,之後再依據 等待特定資源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」動作時,查看系統記錄。 「服務」:通常代表必須啟動相依服務 首先。
  • 移除 init.*.rc 中所有未使用的服務和指令。未使用 早期階段 init 應延遲以完成啟動。

注意:屬性服務屬於 init 程序的一部分,因此,請呼叫 如果 init 處於忙碌狀態,setproperty 可能會長時間延遲 內建的指令

使用排程器調整功能

使用排程器調整功能進行早期啟動。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-start 時自動啟動 zygote 觸發 (預設情況下,zygote 會在類別主要系統啟動, zygote-start)。如要執行這項操作,請務必允許 zygote 在所有 CPU 中執行 (即 錯誤的 cpuset 設定可能會強制在特定 CPU 執行 zygote)。

停用省電功能

在裝置啟動時,UFS 和/或 CPU 等元件的省電設定 您可以停用主機端。

注意:省電模式的啟用時間應為 以提升效率

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 可在掛接使用者資料之前,提早啟動啟動動畫 不過,即使在 Android 8.0 中使用新的 ext4 工具鍊 但因安全原因而會定期觸發,導致 啟動啟動服務

若要提早啟動啟動程序,請將 fstab 掛接分為兩個階段:

  • 在早期階段,只掛接分區 (例如 system/vendor/),不需要執行 然後啟動啟動動畫服務及其依附元件 (例如 servicemanager 和 Surfaceflinger)。
  • 在第二階段中,掛接該分區 (例如 data/) 的分區 執行檢查

無論如何 指怪

完成清潔

收到離開信號後,啟動動作會播放最後一個部分, 導致啟動時間變慢無須長時間即可開機的系統 可以有效隱藏任何改善效果的動畫建議做法 同時盡量減少重複循環和結尾

最佳化 SELinux

請使用下列提示將 SELinux 最佳化,以縮短啟動時間。

  • 使用簡潔的規則運算式 (regex)。規則運算式的格式有誤 比對 SELinux 政策 sys/devicesfile_contexts。例如 /sys/devices/.*abc.*(/.*)? 不小心強制掃描全部 有 /sys/devices 個包含「abc」的子目錄,可供比對 /sys/devices/abc/sys/devices/xyz/abc 兩者皆是如此。 如果將這個規則運算式提升為 /sys/devices/[^/]*abc[^/]*(/.*)?, 只啟用 /sys/devices/abc 的比對。
  • 將標籤移至 genfscon。 這個現有的 SELinux 功能會將檔案比對前置字串傳遞至核心: SELinux 二進位檔,其中核心會將其套用至由核心產生的 檔案系統這也有助於修正標記核心建立錯誤的檔案 嘗試存取的使用者空間程序之間發生的競爭狀況 才會重新加上標籤。

工具和方法

請使用下列工具收集要最佳化目標的資料。

開機圖

Bootchart 會針對整個程序提供全部的 CPU 和 I/O 負載細目 有些人會將 Cloud Storage 視為檔案系統 但實際上不是無須重新建構系統映像檔,只要快速套用即可 執行例行性檢查

如何啟用 bootchart:

adb shell 'touch /data/bootchart/enabled'
adb reboot

啟動後,擷取開機圖表:

$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh

完成後,請刪除 /data/bootchart/enabled,以免收集到 因此資料的規模

如果 bootchart 無法運作,且您收到指出 bootchart.png 不存在的錯誤訊息,請執行 包括:
  1. 執行下列指令:
          sudo apt install python-is-python3
          cd ~/Documents
          git clone https://github.com/xrmx/bootchart.git
          cd bootchart/pybootchartgui
          mv main.py.in main.py
        
  2. 更新「$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
  • 如需詳細的 I/O 分析,請同時加入區塊和 ext4 與 f2fs。

  • 在裝置專屬的 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_trace
    adb pull /data/local/tmp/boot_trace
    $ANDROID_BUILD_TOP/external/chromium-trace/systrace.py --from-file=boot_trace
    
,瞭解如何調查及移除這項存取權。