מטב את זמני האתחול

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

סיים נקי

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

בצע אופטימיזציה של 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