เอกสารนี้ให้คำแนะนำแก่พันธมิตรในการปรับปรุงเวลาบูตสำหรับอุปกรณ์ Android บางประเภท เวลาบูตเป็นองค์ประกอบสำคัญของประสิทธิภาพของระบบ เนื่องจากผู้ใช้ต้องรอให้บูตเสร็จก่อนจึงจะสามารถใช้อุปกรณ์ได้ สำหรับอุปกรณ์เช่นรถยนต์ที่มีการบูตเครื่องขณะเย็นบ่อยกว่า การมีเวลาบูตอย่างรวดเร็วถือเป็นสิ่งสำคัญ (ไม่มีใครชอบการรอหลายสิบวินาทีเพื่อป้อนปลายทางการนำทาง)
Android 8.0 ช่วยลดเวลาในการบูตโดยรองรับการปรับปรุงหลายประการในส่วนประกอบต่างๆ ตารางต่อไปนี้สรุปการปรับปรุงประสิทธิภาพเหล่านี้ (วัดจากอุปกรณ์ Google Pixel และ Pixel XL)
ส่วนประกอบ | การปรับปรุง |
---|---|
บูตโหลดเดอร์ |
|
เคอร์เนลของอุปกรณ์ |
|
การปรับ I/O |
|
init.*.rc |
|
แอนิเมชั่นการบูต |
|
นโยบายของ SELinux | บันทึก 0.2 วินาทีโดย genfscon |
เพิ่มประสิทธิภาพ bootloader
เพื่อเพิ่มประสิทธิภาพ bootloader เพื่อปรับปรุงเวลาการบูต:
- สำหรับการบันทึก:
- ปิดใช้งานการเขียนบันทึกไปยัง UART เนื่องจากอาจใช้เวลานานและมีการบันทึกจำนวนมาก (บนอุปกรณ์ Google Pixel เราพบว่ามันทำให้ bootloader ช้าลง 1.5 วินาที)
- บันทึกเฉพาะสถานการณ์ข้อผิดพลาด และพิจารณาจัดเก็บข้อมูลอื่นๆ ลงในหน่วยความจำด้วยกลไกที่แยกต่างหากเพื่อดึงข้อมูล
- สำหรับการบีบอัดเคอร์เนล ให้พิจารณาใช้ LZ4 สำหรับฮาร์ดแวร์ร่วมสมัยแทน GZIP ( แพทช์ ตัวอย่าง) โปรดทราบว่าตัวเลือกการบีบอัดเคอร์เนลที่แตกต่างกันอาจมีเวลาในการโหลดและคลายการบีบอัดที่แตกต่างกัน และบางตัวเลือกอาจทำงานได้ดีกว่าตัวเลือกอื่นๆ สำหรับฮาร์ดแวร์เฉพาะของคุณ
- ตรวจสอบเวลารอที่ไม่จำเป็นสำหรับการดีเด้ง/เข้าสู่โหมดพิเศษและย่อให้เล็กสุด
- ส่งผ่านเวลาบูตที่ใช้ใน bootloader ไปยังเคอร์เนลเป็น cmdline
- ตรวจสอบนาฬิกา CPU และพิจารณาการทำงานแบบขนาน (ต้องรองรับมัลติคอร์) สำหรับการโหลดเคอร์เนลและการเริ่มต้น I/O
ปรับประสิทธิภาพ I/O ให้เหมาะสม
การปรับปรุงประสิทธิภาพ 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 ระหว่างการบู๊ต ให้ใช้ข้อมูล 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 ในปัจจุบันจะเป็นกระบวนการแบบเธรดเดียวไม่มากก็น้อย แต่คุณยังคงสามารถทำงานบางอย่างควบคู่กันไปได้
- ดำเนินการคำสั่งที่ช้าในบริการเชลล์สคริปต์และเข้าร่วมในภายหลังโดยรอคุณสมบัติเฉพาะ Android 8.0 รองรับกรณีการใช้งานนี้ด้วยคำสั่ง
wait_for_property
ใหม่ - ระบุการดำเนินการที่ช้าใน init ระบบจะบันทึกคำสั่ง init exec/wait_for_prop หรือการดำเนินการใดๆ ที่ใช้เวลานาน (ใน Android 8.0 คำสั่งใดๆ ที่ใช้เวลานานกว่า 50 ms) ตัวอย่างเช่น:
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 ดังนั้นการเรียก 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 ...
เริ่มไซโกตตั้งแต่เนิ่นๆ
อุปกรณ์ที่มีการเข้ารหัสแบบไฟล์สามารถเริ่มไซโกตได้เร็วกว่าที่ทริกเกอร์ไซโกต-สตาร์ท (โดยค่าเริ่มต้น ไซโกตจะถูกเปิดใช้งานที่คลาสหลัก ซึ่งช้ากว่าไซโกเท-สตาร์ทมาก) เมื่อทำเช่นนี้ ตรวจสอบให้แน่ใจว่าได้อนุญาตให้ไซโกตทำงานใน 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 ยังคงถูกทริกเกอร์เป็นระยะเนื่องจากเหตุผลด้านความปลอดภัย ทำให้เกิดความล่าช้าในการเริ่มบริการ bootanimation
หากต้องการให้ bootanimation เริ่มต้นเร็ว ให้แบ่ง fstab mount ออกเป็นสองเฟส:
- ในช่วงเริ่มต้น ให้เมาต์เฉพาะพาร์ติชัน (เช่น
system/
และvendor/
) ที่ไม่ต้องการการตรวจสอบการรัน จากนั้นเริ่มบริการแอนิเมชั่นสำหรับบูตและการขึ้นต่อกัน (เช่น servicemanager และ surfaceflinger) - ในระยะที่สอง ติดตั้งพาร์ติชัน (เช่น
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
ได้ ปรับปรุง regex นี้เป็น/sys/devices/[^/]*abc[^/]*(/.*)?
จะเปิดใช้งานการจับคู่เฉพาะสำหรับ/sys/devices/abc
- ย้ายป้ายกำกับไปที่ genfscon คุณสมบัติ SELinux ที่มีอยู่นี้ส่งผ่านคำนำหน้าการจับคู่ไฟล์ไปยังเคอร์เนลในไบนารี SELinux โดยที่เคอร์เนลนำไปใช้กับระบบไฟล์ที่สร้างโดยเคอร์เนล นอกจากนี้ยังช่วยแก้ไขไฟล์ที่สร้างเคอร์เนลที่มีป้ายกำกับไม่ถูกต้อง ป้องกันสภาวะการแข่งขันที่อาจเกิดขึ้นระหว่างกระบวนการ userspace ที่พยายามเข้าถึงไฟล์เหล่านี้ก่อนที่จะเกิดการติดป้ายกำกับใหม่
เครื่องมือและวิธีการ
ใช้เครื่องมือต่อไปนี้เพื่อช่วยคุณรวบรวมข้อมูลสำหรับเป้าหมายการเพิ่มประสิทธิภาพ
แผนภูมิบูต
Bootchart ให้รายละเอียดโหลด CPU และ I/O ของกระบวนการทั้งหมดสำหรับทั้งระบบ ไม่จำเป็นต้องสร้างอิมเมจระบบขึ้นใหม่ และสามารถใช้เป็นการตรวจสอบสุขภาพอย่างรวดเร็วก่อนที่จะเข้าสู่ระบบซิสเต็มเทรซ
หากต้องการเปิดใช้งาน Bootchart:
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 อนุญาตให้รวบรวมทั้งการติดตามเคอร์เนลและ 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
สิ่งนี้จะเปิดใช้งานการติดตาม (ซึ่งถูกปิดใช้งานโดยค่าเริ่มต้น)
สำหรับการวิเคราะห์ I/O โดยละเอียด ให้เพิ่มบล็อกและ ext4 และ f2fs ด้วย