डिवाइस के चालू होने में लगने वाले समय को ऑप्टिमाइज़ करना

इस दस्तावेज़ में, पार्टनर को कुछ खास Android डिवाइसों के लिए, बूट होने में लगने वाले समय को बेहतर बनाने के बारे में बताया गया है. बूट होने में लगने वाला समय, सिस्टम की परफ़ॉर्मेंस का एक अहम कॉम्पोनेंट है. डिवाइस का इस्तेमाल करने से पहले, उपयोगकर्ताओं को बूट होने का इंतज़ार करना पड़ता है. कार जैसे डिवाइसों के लिए, तेज़ी से चालू होने की सुविधा ज़रूरी है. इन डिवाइसों में अक्सर कोल्ड बूट-अप होता है. किसी नेविगेशन डेस्टिनेशन को डालने के लिए, ज़्यादा सेकंड तक इंतज़ार करना किसी को पसंद नहीं आता.

Android 8.0 में कई कॉम्पोनेंट में सुधार किए गए हैं. इससे, डिवाइस को बूट होने में कम समय लगता है. यहां दी गई टेबल में, परफ़ॉर्मेंस में हुए इन सुधारों के बारे में खास जानकारी दी गई है. ये सुधार, Google Pixel और Pixel XL डिवाइसों पर किए गए हैं.

कॉम्पोनेंट सुधार
बूटलोडर
  • UART लॉग हटाकर 1.6 सेकंड बचाए गए
  • GZIP से LZ4 पर स्विच करके 0.4 सेकंड बचाए गए
डिवाइस का kernel
  • इस्तेमाल न किए गए कर्नेल कॉन्फ़िगरेशन हटाकर और ड्राइवर का साइज़ कम करके, 0.3 सेकंड की बचत हुई
  • dm-verity की मदद से, पहले से डेटा लोड करने की सुविधा को ऑप्टिमाइज़ करके 0.3 सेकंड बचाए गए
  • ड्राइवर में अनचाहे इंतज़ार/जांच को हटाने के लिए, 0.15 सेकंड बचाए गए
  • CONFIG_CC_OPTIMIZE_FOR_SIZE को हटाने में 0.12 सेकंड की बचत हुई
I/O ट्यूनिंग
  • सामान्य बूट में दो सेकंड की बचत
  • पहले बूट में 25 सेकंड की बचत हुई
init.*.rc
  • init कमांड को एक साथ चलाकर 1.5 सेकंड बचाए गए
  • ज़ाइगोट को जल्दी शुरू करके 0.25 सेकंड बचाए गए
  • cpuset ट्यून की मदद से 0.22 सेकंड बचाए गए
बूट ऐनिमेशन
  • fsck ट्रिगर किए बिना, बूट होने में दो सेकंड पहले शुरू हुआ, fsck ट्रिगर किए जाने पर बूट होने में काफ़ी ज़्यादा समय लगा
  • Pixel XL पर, बूट ऐनिमेशन को तुरंत बंद करके पांच सेकंड बचाए गए
SELinux नीति genfscon की मदद से 0.2 सेकंड बचाए गए

बूटलोडर को ऑप्टिमाइज़ करना

बूट होने में लगने वाले समय को कम करने के लिए, बूटलोडर को ऑप्टिमाइज़ करने के लिए:

  • लॉग करने के लिए:
    • UART में लॉग लिखने की सुविधा बंद करें, क्योंकि बहुत ज़्यादा लॉगिंग में काफ़ी समय लग सकता है. (हमें पता चला है कि Google Pixel डिवाइसों पर, यह बूटलोडर को 1.5 सेकंड तक धीमा कर देता है).
    • सिर्फ़ गड़बड़ी की स्थितियों को लॉग करें और अन्य जानकारी को मेमोरी में सेव करें. साथ ही, उसे वापस पाने के लिए अलग से कोई तरीका अपनाएं.
  • कर्नेल को डिकंप्रेस करने के लिए, GZIP के बजाय, ज़्यादा बेहतर हार्डवेयर के लिए LZ4 का इस्तेमाल करें (उदाहरण के लिए, पैच). ध्यान रखें कि अलग-अलग कर्नेल कंप्रेसन विकल्पों के लोड होने और डिकंप्रेस होने में अलग-अलग समय लग सकता है. साथ ही, हो सकता है कि आपके खास हार्डवेयर के लिए, कुछ विकल्प दूसरों के मुकाबले बेहतर काम करें.
  • डिबाउंसिंग/खास मोड में एंट्री के लिए, ग़ैर-ज़रूरी इंतज़ार का समय देखें और उसे कम करें.
  • बूटलोडर में बूट होने में लगने वाले समय को, cmdline के तौर पर कर्नेल को पास करें.
  • सीपीयू क्लॉक की जांच करें और कर्नेल लोड करने और I/O को शुरू करने के लिए, पैरलललाइज़ेशन (मल्टी-कोर सपोर्ट की ज़रूरत होती है) पर विचार करें.

I/O की क्षमता को ऑप्टिमाइज़ करना

डिवाइस को तेज़ी से चालू करने के लिए, I/O की परफ़ॉर्मेंस को बेहतर बनाना ज़रूरी है. साथ ही, डिवाइस चालू होने के बाद ही ज़रूरी चीज़ें पढ़ी जानी चाहिए. Google Pixel पर, डिवाइस चालू होने के दौरान करीब 1.2 जीबी डेटा पढ़ा जाता है.

फ़ाइल सिस्टम को ट्यून करना

Linux kernel read ahead, फ़ाइल को शुरू से पढ़ने या ब्लॉक को क्रम से पढ़ने पर चालू होता है. इससे, खास तौर पर बूट करने के लिए 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
    ...

अन्य चीज़ें

  • kernel config का इस्तेमाल करके, dm-verity हैश प्रीफ़ेच साइज़ को चालू करें DM_VERITY_HASH_PREFETCH_MIN_SIZE (डिफ़ॉल्ट साइज़ 128 है).
  • फ़ाइल सिस्टम को बेहतर बनाने और हर बार बूट होने पर होने वाली ज़बरदस्ती की जांच को हटाने के लिए, नए ext4 जनरेशन टूल का इस्तेमाल करें. इसके लिए, BoardConfig.mk में TARGET_USES_MKE2FS सेट करें.

I/O का विश्लेषण करना

बूट के दौरान I/O गतिविधियों को समझने के लिए, kernel 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 के अलग-अलग चरणों में कुछ सेकंड लगते हैं.

एक साथ कई टास्क चलाना

Android के मौजूदा init प्रोसेस में, एक ही थ्रेड का इस्तेमाल किया जाता है. हालांकि, अब भी कुछ टास्क एक साथ किए जा सकते हैं.

  • शेल स्क्रिप्ट सेवा में धीमे निर्देशों को लागू करें और बाद में किसी खास प्रॉपर्टी के लिए इंतज़ार करके उसमें शामिल हों. Android 8.0, इस्तेमाल के इस उदाहरण के लिए, एक नए wait_for_property कमांड के साथ काम करता है.
  • init में धीमी कार्रवाइयों की पहचान करना. सिस्टम, init command/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 service" दिखाता है, तो सिस्टम लॉग की समीक्षा करें — आम तौर पर, यह इस बात का संकेत होता है कि किसी दूसरी सेवा को पहले शुरू करना ज़रूरी है.
  • init.*.rc में, इस्तेमाल नहीं की जा रही सेवाओं और निर्देशों को हटाएं. शुरुआती चरण में शुरू नहीं की गई किसी भी चीज़ को, बूट पूरा होने के बाद शुरू किया जाना चाहिए.

ध्यान दें: प्रॉपर्टी सेवा, 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-start ट्रिगर होने पर zygote को पहले से शुरू किया जा सकता है. डिफ़ॉल्ट रूप से, zygote को class main पर लॉन्च किया जाता है, जो zygote-start के बाद होता है. ऐसा करते समय, पक्का करें कि zygote को सभी सीपीयू में चलने की अनुमति दी गई हो. ऐसा इसलिए, क्योंकि गलत cpuset सेटिंग से zygote को कुछ खास सीपीयू में चलने के लिए मजबूर किया जा सकता है.

बैटरी सेवर मोड बंद करना

डिवाइस के बूट होने के दौरान, यूएफ़एस और/या सीपीयू गवर्नर जैसे कॉम्पोनेंट के लिए, बैटरी बचाने की सेटिंग बंद की जा सकती है.

चेतावनी: बेहतर परफ़ॉर्मेंस के लिए, चार्जर मोड में बैटरी बचाने की सुविधा चालू होनी चाहिए.

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 में, उपयोगकर्ता डेटा के partition को माउंट करने से पहले, बूट ऐनिमेशन को शुरू करने की सुविधा उपलब्ध है. हालांकि, Android 8.0 में नए ext4 टूल चेन का इस्तेमाल करने पर भी, सुरक्षा की वजहों से fsck समय-समय पर ट्रिगर होता रहता है. इस वजह से, बूटऐनिमेशन सेवा शुरू होने में देरी होती है.

बूटऐनिमेशन को जल्दी शुरू करने के लिए, fstab माउंट को दो चरणों में बांटें:

  • शुरुआती चरण में, सिर्फ़ उन पार्टीशन (जैसे कि system/ और vendor/) को माउंट करें जिनके लिए रन करने से पहले जांच करने की ज़रूरत नहीं होती. इसके बाद, बूट ऐनिमेशन सेवाओं और उनकी डिपेंडेंसी (जैसे कि servicemanager और surfaceflinger) को शुरू करें.
  • दूसरे चरण में, ऐसे पार्टिशन (जैसे कि data/) माउंट करें जिनके लिए जांच की ज़रूरत है.

fsck के बावजूद, बूट ऐनिमेशन बहुत तेज़ी से (और एक ही समय में) शुरू हो जाएगा.

फ़िनिश क्लीन

डिवाइस बंद करने का सिग्नल मिलने के बाद, बूटऐनिमेशन का आखिरी हिस्सा चलता है. इस हिस्से की अवधि ज़्यादा होने पर, डिवाइस को चालू होने में ज़्यादा समय लग सकता है. तेज़ी से बूट होने वाले सिस्टम के लिए, लंबी अवधि वाले ऐनिमेशन की ज़रूरत नहीं होती. इनसे, सिस्टम में किए गए सुधारों को छिपाया जा सकता है. हमारा सुझाव है कि आप बार-बार चलने वाले लूप और फ़ाइनल, दोनों को छोटा बनाएं.

SELinux को ऑप्टिमाइज़ करना

बेहतर बूट समय के लिए, SELinux को ऑप्टिमाइज़ करने के लिए इन सुझावों का इस्तेमाल करें.

  • क्लीन रेगुलर एक्सप्रेशन (रेगुलर एक्सप्रेशन) का इस्तेमाल करें. file_contexts में sys/devices के लिए, SELinux नीति से मैच करते समय, गलत तरीके से बनाए गए रेगुलर एक्सप्रेशन की वजह से काफ़ी ओवरहेड हो सकता है. उदाहरण के लिए, रेगुलर एक्सप्रेशन/sys/devices/.*abc.*(/.*)?, "abc" वाली सभी/sys/devices सबडायरेक्ट्री को गलती से स्कैन करता है. इससे /sys/devices/abc और /sys/devices/xyz/abc, दोनों के लिए मैच की सुविधा चालू हो जाती है. इस रेगुलर एक्सप्रेशन को /sys/devices/[^/]*abc[^/]*(/.*)? में बदलने पर, सिर्फ़ /sys/devices/abc के लिए मैच की सुविधा चालू हो जाएगी.
  • लेबल को genfscon में ले जाएं. SELinux की मौजूदा सुविधा, फ़ाइल से मैच करने वाले प्रीफ़िक्स को SELinux बाइनरी में, कर्नेल में भेजती है. इसके बाद, कर्नेल उन्हें कर्नेल से जनरेट किए गए फ़ाइल सिस्टम पर लागू करता है. इससे, गलत लेबल वाली कर्नेल से बनाई गई फ़ाइलों को ठीक करने में भी मदद मिलती है. साथ ही, रीलेबल करने से पहले, उपयोगकर्ता स्पेस की प्रोसेस के बीच इन फ़ाइलों को ऐक्सेस करने की कोशिश करने पर, रेस कंडीशन से भी बचा जा सकता है.

टूल और तरीके

ऑप्टिमाइज़ेशन टारगेट के लिए डेटा इकट्ठा करने में मदद पाने के लिए, नीचे दिए गए टूल का इस्तेमाल करें.

Bootchart

Bootchart, पूरे सिस्टम के लिए सभी प्रोसेस के सीपीयू और I/O लोड का ब्रेकडाउन उपलब्ध कराता है. इसके लिए, सिस्टम इमेज को फिर से बनाने की ज़रूरत नहीं होती. साथ ही, systrace का इस्तेमाल करने से पहले, इसकी मदद से तुरंत जांच की जा सकती है.

बूटचार्ट चालू करने के लिए:

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