אופטימיזציה של זמני האתחול

מסמך זה מספק הנחיות לשותפים לשיפור זמני האתחול עבור מכשירי Android ספציפיים. זמן האתחול הוא מרכיב חשוב בביצועי המערכת מכיוון שמשתמשים צריכים לחכות לסיום האתחול לפני שהם יכולים להשתמש במכשיר. עבור מכשירים כגון מכוניות שבהן אתחול קר מתרחש בתדירות גבוהה יותר, זמן אתחול מהיר הוא קריטי (אף אחד לא אוהב לחכות עשרות שניות רק כדי להזין יעד ניווט).

אנדרואיד 8.0 מאפשר זמני אתחול מופחתים על ידי תמיכה במספר שיפורים במגוון רכיבים. הטבלה הבאה מסכמת את שיפורי הביצועים הללו (כפי שנמדד במכשירי Google Pixel ו-Pixel XL).

רְכִיב הַשׁבָּחָה
טוען אתחול
  • נשמר 1.6 שניות על ידי הסרת יומן UART
  • חסך 0.4 שניות על ידי שינוי ל-LZ4 מ-GZIP
ליבת המכשיר
  • נשמר 0.3 שניות על ידי הסרת תצורות ליבה שאינן בשימוש והקטנת גודל מנהל ההתקן
  • נשמר 0.3 שניות עם אופטימיזציה של אחזור מראש של dm-verity
  • נשמר 0.15 שניות כדי להסיר המתנה/בדיקה מיותרת במנהל ההתקן
  • נשמר 0.12 שניות כדי להסיר CONFIG_CC_OPTIMIZE_FOR_SIZE
כוונון I/O
  • חסך 2 שניות באתחול רגיל
  • חסך 25 שניות באתחול הראשון
init.*.rc
  • נשמר 1.5 שניות על ידי פקודות init מקבילות
  • חסך 0.25 שניות על ידי התחלת זיגוטה מוקדם
  • נשמר 0.22 שניות על ידי מנגינת cpuset
אתחול אנימציה
  • התחיל 2 שניות קודם לכן באתחול ללא הפעלת fsck, הרבה יותר גדול באתחול עם הפעלת fsck
  • נשמר 5 שניות ב-Pixel XL עם כיבוי מיידי של אנימציית האתחול
מדיניות SELinux נשמר 0.2 שניות על ידי genfscon

אופטימיזציה של טוען האתחול

כדי לייעל את טוען האתחול לזמני אתחול משופרים:

  • לרישום:
    • השבת כתיבת יומן ל-UART מכיוון שזה יכול לקחת הרבה זמן עם רישום רב. (במכשירי גוגל פיקסל, מצאנו שזה מאט את טוען האתחול 1.5s).
    • רשום רק מצבי שגיאה ושקול אחסון מידע אחר בזיכרון עם מנגנון נפרד לאחזור.
  • עבור פירוק ליבה, שוקל להשתמש ב-LZ4 עבור חומרה עכשווית במקום ב-GZIP ( תיקון לדוגמה). זכור שלאפשרויות דחיסה שונות של ליבה יכולות להיות זמני טעינה ופירוק דחיסה שונים, ואפשרויות מסוימות עשויות לעבוד טוב יותר מאחרות עבור החומרה הספציפית שלך.
  • בדוק זמני המתנה מיותרים ליציאה/כניסה למצב מיוחד ומזער אותם.
  • העבירו את זמן האתחול שבילה ב-bootloader לקרנל כ-cmdline.
  • בדוק את שעון המעבד ושקול מקבילה (דורש תמיכה מרובת ליבות) לטעינת ליבה ולאתחול I/O.

אופטימיזציה של יעילות I/O

שיפור יעילות ה-I/O הוא קריטי להפיכת זמן האתחול מהיר יותר, ויש לדחות את הקריאה של כל דבר שאינו נחוץ עד לאחר האתחול (ב-Google Pixel, כ-1.2GB של נתונים נקראים בעת האתחול).

כוונון מערכת הקבצים

קריאת ליבת לינוקס מתחילה כאשר קובץ נקרא מתחילתו או כאשר בלוקים נקראים ברצף, מה שהופך את הצורך לכוונן פרמטרים של מתזמן I/O במיוחד לאתחול (שיש לו אפיון עומס עבודה שונה מאשר יישומים רגילים).

מכשירים התומכים בעדכונים חלקים (A/B) נהנים מאוד מכוונון מערכת הקבצים באתחול בפעם הראשונה (למשל 20s ב-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.

ניתוח 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 הוא הגשר מהקרנל ועד להקמת המסגרת, והתקנים בדרך כלל מבלים כמה שניות בשלבי אינט שונים.

הפעלת משימות במקביל

בעוד שה-Init הנוכחי של אנדרואיד הוא פחות או יותר תהליך משורשר בודד, אתה עדיין יכול לבצע כמה משימות במקביל.

  • בצע פקודות איטיות בשירות סקריפטים של מעטפת והצטרף אליו מאוחר יותר על ידי המתנה למאפיין ספציפי. אנדרואיד 8.0 תומך במקרה שימוש זה עם פקודת wait_for_property חדשה.
  • זהה פעולות איטיות ב-init. המערכת רושמת את הפקודה init exec/wait_for_prop או כל פעולה שלוקחת זמן רב (באנדרואיד 8.0, כל פקודה לוקחת יותר מ-50 אלפיות השנייה). לדוגמה:
    init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms

    עיון ביומן זה עשוי להצביע על הזדמנויות לשיפורים.

  • התחל שירותים ואפשר התקנים היקפיים בנתיב קריטי מוקדם. לדוגמה, חלק מה-SOCs דורשים הפעלת שירותים הקשורים לאבטחה לפני הפעלת SurfaceFlinger. עיין ביומן המערכת כאשר ServiceManager מחזיר "המתנה לשירות" - זה בדרך כלל סימן לכך שיש להפעיל תחילה שירות תלוי.
  • הסר שירותים ופקודות שאינם בשימוש ב-init.*.rc. כל דבר שלא נעשה בו שימוש בשלב מוקדם יש לדחות לסיום האתחול.

הערה: שירות נכסים הוא חלק מתהליך 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-start (כברירת מחדל, zygote מופעל ב-class main, שהוא הרבה יותר מאוחר מה-zygote-start). כאשר אתה עושה זאת, הקפד לאפשר לזיגוטה לפעול בכל המעבדים (כיוון שהגדרת cpuset שגויה עלולה לאלץ את הזיגוטה לפעול במעבדים ספציפיים).

בטל חיסכון בחשמל

במהלך אתחול המכשיר, ניתן להשבית את הגדרת החיסכון בחשמל עבור רכיבים כמו 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}

אופטימיזציה של אנימציית אתחול

השתמש בעצות הבאות כדי לייעל את הנפשת האתחול.

הגדרת התחלה מוקדמת

אנדרואיד 8.0 מאפשר התחלת אנימציית אתחול מוקדם, לפני הטעינה של מחיצת נתוני משתמש. עם זאת, גם בעת שימוש בשרשרת הכלים החדשה ext4 באנדרואיד 8.0, fsck עדיין מופעל מעת לעת מסיבות בטיחות, מה שגורם לעיכוב בהפעלת שירות האתחול.

כדי לגרום לאתחול להתחיל מוקדם, פצל את ה-fstab mount לשני שלבים:

  • בשלב המוקדם, העלה רק את המחיצות (כגון 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 מספק פירוט עומסי CPU ו-I/O של כל התהליכים עבור המערכת כולה. זה לא מצריך בנייה מחדש של תמונת המערכת ויכול לשמש כבדיקת שפיות מהירה לפני הצלילה ל-systrace.

כדי להפעיל את תרשים האתחול:

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

לאחר האתחול, אחזר את טבלת האתחול:

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

בסיום, מחק /data/bootchart/enabled כדי למנוע איסוף הנתונים בכל פעם.

אם אתחול לא עובד ואתה מקבל שגיאה האומרת ש- 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 יכולה לעזור בניתוח בעיה ספציפית במהלך האתחול. (עם זאת, כדי לבדוק את המספר הממוצע או המספר המצטבר במהלך כל האתחול, קל יותר לבדוק ישירות את מעקב ליבה).

כדי להפעיל את 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
  • לניתוח קלט/פלט מפורט, הוסף גם בלוק ו-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