במסמך הזה מפורטות הנחיות לשותפים לשיפור זמני ההפעלה של מכשירי Android זמן האתחול הוא מרכיב חשוב בביצועי המערכת המשתמשים חייבים להמתין לסיום ההפעלה לפני שיוכלו להשתמש במכשיר. למכשירים כמו מכוניות שבהן האתחול במצב התחלתי (cold start) קורה בתדירות גבוהה יותר, לאחר הפעלה מהירה הזמן הוא קריטי (אף אחד לא אוהב להמתין עשרות שניות רק כדי להזין יעד ניווט).
מערכת Android 8.0 מאפשרת לקצר את זמני האתחול על ידי תמיכה בכמה שיפורים במגוון רכיבים. הטבלה הבאה מסכמת את הביצועים האלה שיפורים (כפי שנמדדו במכשירי Google Pixel ו-Pixel XL).
רכיב | שיפור |
---|---|
תוכנת אתחול |
|
הליבה של המכשיר |
|
כוונון קלט/פלט |
|
init.*.rc |
|
אנימציית האתחול |
|
מדיניות SELinux | חיסכון של 0.2 שניות על ידי genfscon |
אופטימיזציה של תוכנת האתחול
כדי לבצע אופטימיזציה של תוכנת האתחול לזמני אתחול משופרים:
- לרישום ביומן:
- להשבית את כתיבת היומנים ב-UART כי זה יכול לקחת הרבה זמן עם הרבה רישום ביומן. (במכשירי Google Pixel, גילינו שהיא מאטה את תוכנת האתחול בגרסה 1.5).
- כדאי לרשום רק מצבי שגיאה ולשקול לשמור פרטים אחרים בזיכרון עם מנגנון נפרד לאחזור.
- לביטול דחיסה של הליבה, כדאי להשתמש ב-LZ4 לחומרה עכשווית במקום GZIP (לדוגמה patch). חשוב לזכור אפשרויות שונות של דחיסת ליבה עשויות להכיל טעינה שונה לפריסת דחיסה, ואפשרויות מסוימות עשויות לפעול טוב יותר מאחרות לחומרה ספציפית.
- יש לבדוק זמני המתנה מיותרים כדי לבטל עזיבה מהדף הראשון או להיכנס למצב מיוחד, ולמזער אותם.
- העברת זמן האתחול בתוכנת האתחול לליבה כ-cmdline.
- בדיקת שעון המעבד (CPU) ואפשרות להשתמש במקבילות (נדרשת תמיכה עם מספר ליבות) לטעינה של ליבה (kernel) ולאתחול קלט/פלט (I/O).
אופטימיזציה של יעילות הקלט/פלט
שיפור יעילות הקלט/פלט חיוני להאצה של זמן האתחול דבר שאינו הכרחי, עד לאחר האתחול (ב-Google Pixel, כ-1.2GB של נתונים נקראים בזמן האתחול).
כוונון מערכת הקבצים
ליבת ה-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.
ניתוח קלט/פלט (I/O)
כדי להבין פעילויות קלט/פלט (I/O) במהלך האתחול, צריך להשתמש בנתוני ftrace של ליבה (kernel) (גם משמשת systrace):
trace_event=block,ext4 in BOARD_KERNEL_CMDLINE
כדי לקבל פירוט של הגישה לקובץ של כל קובץ, מבצעים את השינויים הבאים בליבה (kernel) (ליבה (kernel) של פיתוח בלבד; אין להשתמש בליבה (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);
אפשר להשתמש בסקריפטים הבאים כדי לנתח את ביצועי ההפעלה.
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
. - זהו פעולות איטיות באתחול. המערכת רושמת ביומן את פקודת האתחול
exec/wait_for_PRO או כל פעולה שנמשכת זמן רב (ב-Android 8.0, כל פקודה שהיא
נמשך יותר מ-50 אלפיות השנייה). מוצרים לדוגמה:
init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms
בדיקת היומן הזה עשויה להצביע על הזדמנויות לשיפור.
- הפעלת השירותים והפעלת ציוד היקפי בנתיב קריטי. עבור לדוגמה, בחלק מהרשתות החברתיות נדרשת הפעלה של שירותי אבטחה לפני שמתחילים SurfaceFlinger. לבדוק את יומן המערכת כש-ServiceManager מחזיר את ההודעה "wait for" "service" – בדרך כלל זה סימן לכך שצריך להתחיל שירות תלוי קודם.
- מסירים שירותים ופקודות שלא בשימוש ב-init.*.rc. כל מה שלא נעשה בו שימוש ב- יש לדחות את האתחול עד שהאתחול הושלם.
הערה: שירות הנכס הוא חלק מתהליך ראשוני, לכן
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 ...
התחלת זיגוטה מוקדם
מכשירים עם הצפנה מבוססת-קבצים יכולים להתחיל בזיגוטה בשלב מוקדם יותר (כברירת מחדל, זיגוטה מופעלת במחלקה הראשית, מאוחר יותר בהרבה זיגוטה-התחלה). במהלך הפעולה, הקפידו לאפשר זיגוטה לרוץ בכל המעבדים (CPU), הגדרת ה-cpuset שגויה עלולה לאלץ זיגוטה לרוץ במעבדי CPU מסוימים).
השבתת התכונה 'חיסכון בסוללה'
במהלך הפעלת המכשיר, הגדרת חיסכון בסוללה לרכיבים כמו 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 אפשר להתחיל את אנימציית האתחול בשלב מוקדם, לפני שטוענים את נתוני המשתמשים מחיצה. עם זאת, גם כשמשתמשים בשרשרת הכלים החדשה ext4 ב-Android 8.0, fsck עדיין מופעלת מדי פעם מטעמי בטיחות, ולכן יש עיכוב בהפעלת שירות האתחול.
כדי שהאתחול יתחיל מוקדם, צריך לפצל את טעינת fstab לשני שלבים:
- בשלב המוקדם, יש לטעון רק את המחיצות (למשל
system/
ו-vendor/
) שלא מחייבים הרצה ולאחר מכן להפעיל את שירותי אנימציית האתחול ואת יחסי התלות שלהם (למשל servicemanager ו-screenfling). - בשלב השני, התקנת מחיצות (כמו
data/
) דורשות בדיקות הרצה.
אנימציית האתחול תתחיל מהר יותר (ובזמן קבוע), ללא קשר fsck.
סיום הניקיון
לאחר קבלת אות היציאה, האתחול הוא החלק האחרון, אורך מה שעלול להאט את זמן האתחול. כאשר מפעילים את האתחול במהירות אין צורך בפעולות אנימציות שיסתירו ביעילות את השיפורים שבוצעו. ההמלצות שלנו להפוך את הלולאה החוזרת וגם את הסוף לקצר.
אופטימיזציה של SELinux
היעזרו בטיפים הבאים כדי לבצע אופטימיזציה של SELinux לשיפור זמני האתחול.
- שימוש בביטויים רגולריים נקיים (regex). ביטוי רגולרי (regex) שהפורמט שלו שגוי
עלולה להוביל לתקורה רבה כשמבצעים התאמה למדיניות SELinux עבור
sys/devices
בfile_contexts
. לדוגמה, הביטוי הרגולרי (regex)/sys/devices/.*abc.*(/.*)?
כופה בטעות סריקה של כל הפריטים/sys/devices
ספריות משנה שמכילות את "abc", מה שמאפשר התאמות ל-/sys/devices/abc
וגם ל-/sys/devices/xyz/abc
. שיפור הביטוי הרגולרי ל-/sys/devices/[^/]*abc[^/]*(/.*)?
יוביל להפעיל התאמה רק עבור/sys/devices/abc
. - מעבירים תוויות אל genfscon. התכונה הקיימת של SELinux מעבירה קידומות להתאמת קבצים אל הליבה ב- בקובץ הבינארי SELinux, שבו הליבה (kernel) מחילה אותן על ליבה (kernel) מערכות קבצים. הפעולה הזו גם עוזרת לתקן קבצים שנוצרו באמצעות ליבה (kernel) שלא מסומנים בתווית, וכך מרוץ תהליכים שעלול להתרחש בין תהליכים במרחב המשתמשים שמנסים לגשת לפני שהתיוג מחדש יתבצע.
כלים ושיטות
אפשר להשתמש בכלים הבאים כדי לאסוף נתונים עבור יעדי אופטימיזציה.
תרשים אתחול
תרשים האתחול מספק פירוט של עומסי המעבד (CPU) והקלט/פלט (I/O) של כל התהליכים כולו המערכת. לא נדרשת בנייה מחדש של תמונת המערכת ואפשר להשתמש בה לבצע בדיקת שגרה לפני שממשיכים למערכת.
כדי להפעיל תרשים אתחול:
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-python3
cd ~/Documents
git clone https://github.com/xrmx/bootchart.git
cd bootchart/pybootchartgui
mv main.py.in main.py
- לעדכון
$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
כדי להצביע לעותק המקומי שלpybootchartgui
(נמצא ב-~/Documents/bootchart/pybootchartgui.py
)
מערכת
Systrace מאפשרת לאסוף גם ליבה (kernel) וגם מעקבי Android במהלך האתחול. תצוגה חזותית של מערכת הפעלה יכולה לעזור בניתוח בעיה ספציפית במהלך או אתחול. (עם זאת, כדי לבדוק את המספר הממוצע או את המספר שהצטבר במהלך את כל ההפעלה, קל יותר לבדוק ישירות את מעקב הליבה).
כדי להפעיל מערכת 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_trace
adb pull /data/local/tmp/boot_trace
$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py --from-file=boot_trace
כך יתאפשר מעקב (שמושבת כברירת מחדל).
כדי לבצע ניתוח קלט/פלט מפורט, כדאי להוסיף גם בלוקים, ext4 ו-f2fs.