بهینه سازی زمان بوت

این صفحه مجموعه ای از نکات را ارائه می دهد که می توانید از بین آنها برای بهبود زمان بوت انتخاب کنید.

نمادهای اشکال زدایی را از ماژول ها حذف کنید

مشابه نحوه حذف نمادهای اشکال زدایی از هسته در یک دستگاه تولیدی، مطمئن شوید که نمادهای اشکال زدایی را نیز از ماژول ها حذف کرده اید. حذف نمادهای اشکال زدایی از ماژول ها با کاهش موارد زیر به زمان راه اندازی کمک می کند:

  • زمان لازم برای خواندن باینری ها از فلش.
  • مدت زمانی که طول می کشد تا ramdisk را از حالت فشرده خارج کنید.
  • زمان بارگذاری ماژول ها.

حذف نماد اشکال زدایی از ماژول ها ممکن است چندین ثانیه در حین بوت شدن ذخیره شود.

حذف نمادها به طور پیش‌فرض در ساخت پلتفرم Android فعال است، اما برای فعال کردن صریح آن‌ها، BOARD_DO_NOT_STRIP_VENDOR_RAMDISK_MODULES را در پیکربندی خاص دستگاه خود در زیر دستگاه/ vendor / device تنظیم کنید.

از فشرده سازی LZ4 برای کرنل و ramdisk استفاده کنید

Gzip در مقایسه با LZ4 خروجی فشرده‌تری تولید می‌کند، اما LZ4 سریع‌تر از Gzip فشرده می‌شود. برای هسته و ماژول‌ها، کاهش حجم ذخیره‌سازی مطلق با استفاده از Gzip در مقایسه با مزیت زمان رفع فشرده‌سازی LZ4 چندان قابل توجه نیست.

پشتیبانی از فشرده سازی ramdisk LZ4 از طریق BOARD_RAMDISK_USE_LZ4 به ساخت پلتفرم اندروید اضافه شده است. می توانید این گزینه را در پیکربندی مخصوص دستگاه خود تنظیم کنید. فشرده سازی هسته را می توان از طریق defconfig کرنل تنظیم کرد.

جابجایی به LZ4 باید 500 میلی‌ثانیه تا 1000 میلی‌ثانیه زمان راه‌اندازی سریع‌تری داشته باشد.

از ورود بیش از حد به درایورهای خود اجتناب کنید

در ARM64 و ARM32، فراخوانی‌های تابعی که بیش از یک فاصله خاص از محل تماس هستند، به یک جدول پرش (به نام جدول پیوند فرآیند یا PLT) نیاز دارند تا بتوانند آدرس پرش کامل را رمزگذاری کنند. از آنجایی که ماژول ها به صورت پویا بارگذاری می شوند، این جدول های پرش باید در حین بارگذاری ماژول ثابت شوند. تماس هایی که نیاز به جابجایی دارند، ورودی های جابجایی با اضافات صریح (یا به اختصار RELA) ورودی در قالب ELF نامیده می شوند.

هسته لینوکس هنگام تخصیص PLT مقداری بهینه سازی اندازه حافظه را انجام می دهد (مانند بهینه سازی ضربه حافظه کش). با این commit بالادستی ، طرح بهینه‌سازی دارای پیچیدگی O(N^2) است که N تعداد RELA از نوع R_AARCH64_JUMP26 یا R_AARCH64_CALL26 است. بنابراین داشتن RELA کمتر از این نوع در کاهش زمان بارگذاری ماژول مفید است.

یکی از الگوهای کدگذاری رایج که تعداد R_AARCH64_CALL26 یا R_AARCH64_JUMP26 RELA ها را افزایش می دهد، ورود بیش از حد به یک درایور است. هر فراخوانی به printk() یا هر طرح ورود به سیستم دیگر معمولا یک ورودی CALL26 / JUMP26 RELA اضافه می کند. در متن commit در commit upstream ، توجه کنید که حتی با بهینه‌سازی، بارگیری شش ماژول حدود 250 میلی‌ثانیه طول می‌کشد - به این دلیل که آن شش ماژول شش ماژول برتر با بیشترین میزان ثبت گزارش بودند.

کاهش لاگ می‌تواند باعث صرفه‌جویی در حدود 100 تا 300 میلی‌ثانیه در زمان راه‌اندازی شود، بسته به میزان بیش از حد گزارش موجود.

کاوش ناهمزمان را به صورت انتخابی فعال کنید

هنگامی که یک ماژول بارگذاری می شود، اگر دستگاهی که آن را پشتیبانی می کند قبلاً از DT (devicetree) پر شده و به هسته درایور اضافه شده است، آنگاه کاوش دستگاه در زمینه فراخوانی module_init() انجام می شود. هنگامی که یک کاوشگر دستگاه در زمینه module_init() انجام می شود، ماژول نمی تواند بارگذاری را تا زمانی که پروب کامل شود به پایان برساند. از آنجایی که بارگذاری ماژول عمدتاً به صورت سریالی است، دستگاهی که زمان نسبتاً طولانی برای بررسی طول می کشد، زمان بوت را کند می کند.

برای جلوگیری از کندتر شدن زمان راه‌اندازی، کاوش غیرهمزمان را برای ماژول‌هایی که مدتی طول می‌کشد تا دستگاه‌هایشان را بررسی کنند، فعال کنید. فعال کردن کاوشگر ناهمزمان برای همه ماژول‌ها ممکن است مفید نباشد، زیرا مدت زمانی که طول می‌کشد برای انشعاب نخ و پرتاب کردن کاوشگر ممکن است به اندازه زمان لازم برای بررسی دستگاه باشد.

دستگاه‌هایی که از طریق یک گذرگاه آهسته متصل می‌شوند مانند I2C، دستگاه‌هایی که بارگذاری میان‌افزار را در عملکرد پروب خود انجام می‌دهند و دستگاه‌هایی که مقداردهی اولیه سخت‌افزار را انجام می‌دهند، می‌توانند منجر به مشکل زمان‌بندی شوند. بهترین راه برای شناسایی زمان وقوع این اتفاق، جمع‌آوری زمان کاوش برای هر راننده و مرتب‌سازی آن است.

برای فعال کردن کاوشگر ناهمزمان برای یک ماژول، تنها تنظیم پرچم PROBE_PREFER_ASYNCHRONOUS در کد درایور کافی نیست . برای ماژول‌ها، همچنین باید module_name .async_probe=1 در خط فرمان هسته اضافه کنید یا هنگام بارگیری ماژول با استفاده از modprobe یا insmod async_probe=1 به‌عنوان پارامتر ماژول ارسال کنید.

فعال کردن کاوشگر ناهمزمان می‌تواند در حدود 100 تا 500 میلی‌ثانیه در زمان راه‌اندازی بسته به سخت‌افزار/درایور شما صرفه‌جویی کند.

هر چه زودتر درایور CPUfreq خود را بررسی کنید

هرچه درایور CPUfreq شما زودتر بررسی شود، زودتر می توانید فرکانس CPU را در هنگام بوت شدن به حداکثر (یا مقداری حداکثر از نظر حرارتی محدود) تغییر دهید. هرچه CPU سریعتر باشد، بوت سریعتر است. این دستورالعمل همچنین برای درایورهای devfreq که DRAM، حافظه و فرکانس اتصال را کنترل می کنند، اعمال می شود.

با ماژول ها، مرتب سازی بار می تواند به سطح initcall و کامپایل یا ترتیب پیوند درایورها بستگی داشته باشد. از نام مستعار MODULE_SOFTDEP() استفاده کنید تا مطمئن شوید که درایور cpufreq جزو اولین ماژول هایی است که بارگذاری می شود.

جدا از بارگذاری زودهنگام ماژول، باید مطمئن شوید که تمام وابستگی‌ها برای بررسی درایور CPUfreq نیز بررسی شده‌اند. به عنوان مثال، اگر برای کنترل فرکانس CPU خود به یک ساعت یا دسته تنظیم کننده نیاز دارید، ابتدا مطمئن شوید که آنها را بررسی کنید. یا ممکن است نیاز داشته باشید که درایورهای حرارتی قبل از درایور CPUfreq بارگذاری شوند، اگر ممکن است پردازنده‌های شما در هنگام راه‌اندازی بیش از حد داغ شوند. بنابراین، هر کاری که می توانید انجام دهید تا مطمئن شوید که CPUfreq و درایورهای devfreq مربوطه در اسرع وقت بررسی می شوند.

صرفه جویی حاصل از کاوش زود هنگام درایور CPUfreq شما می تواند بسیار کوچک تا بسیار زیاد باشد، بسته به اینکه چقدر زود می توانید آنها را به کاوش بپردازید و بوت لودر با چه فرکانسی CPU ها را در آن جا می گذارد.

ماژول ها را به مرحله دوم init، vendor یا پارتیشن vendor_dlkm منتقل کنید

از آنجایی که فرآیند شروع مرحله اول سریالی است، فرصت های زیادی برای موازی کردن فرآیند بوت وجود ندارد. اگر ماژول برای اتمام مرحله اول نیاز نیست، ماژول را با قرار دادن آن در پارتیشن vendor یا vendor_dlkm به مرحله دوم منتقل کنید.

شروع مرحله اول برای رسیدن به مرحله دوم نیازی به کاوش چندین دستگاه ندارد. برای یک جریان بوت معمولی فقط به عملکرد کنسول و حافظه فلش نیاز است.

درایورهای ضروری زیر را بارگیری کنید:

  • سگ نگهبان
  • تنظیم مجدد
  • cpufreq

برای بازیابی و فضای کاربر حالت fastbootd ، مرحله اول init به دستگاه های بیشتری برای کاوش (مانند USB) و نمایش نیاز دارد. یک کپی از این ماژول ها را در مرحله اول ramdisk و در پارتیشن vendor یا vendor_dlkm نگه دارید. این به آنها اجازه می دهد تا در مرحله اول برای بازیابی یا جریان بوت فست fastbootd بارگذاری شوند. با این حال، ماژول‌های حالت بازیابی را در مرحله اول در طول جریان بوت معمولی بارگذاری نکنید. ماژول های حالت بازیابی را می توان به مرحله دوم موکول کرد تا زمان بوت کاهش یابد. همه ماژول‌های دیگری که در مرحله اول مورد نیاز نیستند باید به پارتیشن vendor یا vendor_dlkm منتقل شوند.

اسکریپت dev needs.sh با توجه به فهرستی از دستگاه‌های برگ (مثلاً UFS یا سریال)، همه درایورها، دستگاه‌ها و ماژول‌های مورد نیاز برای وابستگی‌ها یا تأمین‌کنندگان (مثلاً ساعت‌ها، تنظیم‌کننده‌ها یا gpio ) را برای بررسی پیدا می‌کند.

انتقال ماژول ها به مرحله دوم اینیت زمان بوت را به روش های زیر کاهش می دهد:

  • کاهش اندازه Ramdisk
    • هنگامی که بوت لودر ramdisk را بارگذاری می کند (مرحله بوت سریالی) این باعث می شود فلش سریعتر خوانده شود.
    • هنگامی که هسته ramdisk را از حالت فشرده خارج می کند (مرحله بوت سریالی) این سرعت فشرده سازی سریع تری را به همراه دارد.
  • مرحله دوم init به صورت موازی کار می کند، که زمان بارگذاری ماژول را با کار انجام شده در مرحله دوم مخفی می کند.

انتقال ماژول ها به مرحله دوم بسته به تعداد ماژول هایی که می توانید به مرحله دوم منتقل کنید، می تواند 500 تا 1000 میلی ثانیه در زمان بوت شدن صرفه جویی کند.

لجستیک بارگذاری ماژول

آخرین نسخه اندروید دارای تنظیمات برد است که کنترل می کند کدام ماژول ها در هر مرحله کپی می شوند و کدام ماژول ها بارگیری می شوند. این بخش بر روی زیرمجموعه زیر تمرکز دارد:

  • BOARD_VENDOR_RAMDISK_KERNEL_MODULES . این لیست از ماژول هایی که باید در ramdisk کپی شوند.
  • BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD . این لیست از ماژول ها در مرحله اول بارگذاری می شود.
  • BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD . این لیست از ماژول هایی که باید هنگام بازیابی یا fastbootd از ramdisk انتخاب شوند، بارگذاری می شوند.
  • BOARD_VENDOR_KERNEL_MODULES . این لیست از ماژول‌هایی که باید در پارتیشن vendor یا vendor_dlkm در فهرست /vendor/lib/modules/ کپی شوند.
  • BOARD_VENDOR_KERNEL_MODULES_LOAD . این لیست از ماژول ها در مرحله دوم بارگذاری می شود.

ماژول های بوت و بازیابی در ramdisk نیز باید در vendor یا پارتیشن vendor_dlkm در /vendor/lib/modules کپی شوند. کپی کردن این ماژول‌ها در پارتیشن فروشنده تضمین می‌کند که ماژول‌ها در مرحله دوم نامرئی نباشند، که برای اشکال‌زدایی و جمع‌آوری modinfo برای گزارش‌های اشکال مفید است.

تا زمانی که مجموعه ماژول بوت به حداقل رسیده باشد، تکثیر باید حداقل فضا را در پارتیشن vendor یا vendor_dlkm هزینه داشته باشد. مطمئن شوید که فایل modules.list فروشنده دارای یک لیست فیلتر شده از ماژول ها در /vendor/lib/modules است. لیست فیلتر شده تضمین می کند که زمان بوت تحت تأثیر بارگذاری مجدد ماژول ها قرار نمی گیرد (که فرآیند گران قیمتی است).

اطمینان حاصل کنید که ماژول های حالت بازیابی به صورت گروهی بارگیری می شوند. بارگذاری ماژول های حالت بازیابی را می توان در حالت بازیابی یا در ابتدای مرحله دوم در هر جریان بوت انجام داد.

می توانید از فایل های دستگاه Board.Config.mk برای انجام این اقدامات همانطور که در مثال زیر مشاهده می شود استفاده کنید:

# All kernel modules
KERNEL_MODULES := $(wildcard $(KERNEL_MODULE_DIR)/*.ko)
KERNEL_MODULES_LOAD := $(strip $(shell cat $(KERNEL_MODULE_DIR)/modules.load)

# First stage ramdisk modules
BOOT_KERNEL_MODULES_FILTER := $(foreach m,$(BOOT_KERNEL_MODULES),%/$(m))

# Recovery ramdisk modules
RECOVERY_KERNEL_MODULES_FILTER := $(foreach m,$(RECOVERY_KERNEL_MODULES),%/$(m))
BOARD_VENDOR_RAMDISK_KERNEL_MODULES += \
     $(filter $(BOOT_KERNEL_MODULES_FILTER) \
                $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES))

# ALL modules land in /vendor/lib/modules so they could be rmmod/insmod'd,
# and modules.list actually limits us to the ones we intend to load.
BOARD_VENDOR_KERNEL_MODULES := $(KERNEL_MODULES)
# To limit /vendor/lib/modules to just the ones loaded, use:
# BOARD_VENDOR_KERNEL_MODULES := $(filter-out \
#     $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES))

# Group set of /vendor/lib/modules loading order to recovery modules first,
# then remainder, subtracting both recovery and boot modules which are loaded
# already.
BOARD_VENDOR_KERNEL_MODULES_LOAD := \
        $(filter-out $(BOOT_KERNEL_MODULES_FILTER), \
        $(filter $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD)))
BOARD_VENDOR_KERNEL_MODULES_LOAD += \
        $(filter-out $(BOOT_KERNEL_MODULES_FILTER) \
            $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))

# NB: Load order governed by modules.load and not by $(BOOT_KERNEL_MODULES)
BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD := \
        $(filter $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))

# Group set of /vendor/lib/modules loading order to boot modules first,
# then the remainder of recovery modules.
BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD := \
    $(filter $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))
BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD += \
    $(filter-out $(BOOT_KERNEL_MODULES_FILTER), \
    $(filter $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD)))

این مثال زیرمجموعه‌ای از BOOT_KERNEL_MODULES و RECOVERY_KERNEL_MODULES را با مدیریت آسان‌تر نشان می‌دهد تا به صورت محلی در فایل‌های پیکربندی برد مشخص شوند. اسکریپت قبلی هر یک از ماژول‌های زیرمجموعه را از ماژول‌های هسته موجود انتخاب شده پیدا کرده و پر می‌کند و ماژول‌های reaming را برای شروع مرحله دوم باقی می‌گذارد.

برای شروع مرحله دوم، توصیه می کنیم بارگذاری ماژول را به عنوان یک سرویس اجرا کنید تا جریان بوت را مسدود نکند. از یک اسکریپت پوسته برای مدیریت بارگذاری ماژول استفاده کنید تا در صورت لزوم، سایر تدارکات، مانند مدیریت خطا و کاهش خطا، یا تکمیل بار ماژول، گزارش داده شوند (یا نادیده گرفته شوند).

می‌توانید شکست بارگذاری ماژول اشکال‌زدایی را که در ساخت‌های کاربر وجود ندارد، نادیده بگیرید. برای نادیده گرفتن این شکست، ویژگی vendor.device.modules.ready را تنظیم کنید تا مراحل بعدی راه‌اندازی اسکریپت init rc را برای ادامه روی صفحه راه‌اندازی راه‌اندازی کند. اگر کد زیر را در /vendor/etc/init.insmod.sh دارید به اسکریپت مثال زیر مراجعه کنید:

#!/vendor/bin/sh
. . .
if [ $# -eq 1 ]; then
  cfg_file=$1
else
  # Set property even if there is no insmod config
  # to unblock early-boot trigger
  setprop vendor.common.modules.ready
  setprop vendor.device.modules.ready
  exit 1
fi

if [ -f $cfg_file ]; then
  while IFS="|" read -r action arg
  do
    case $action in
      "insmod") insmod $arg ;;
      "setprop") setprop $arg 1 ;;
      "enable") echo 1 > $arg ;;
      "modprobe") modprobe -a -d /vendor/lib/modules $arg ;;
     . . .
    esac
  done < $cfg_file
fi

در فایل rc سخت افزاری، سرویس one shot را می توان با موارد زیر مشخص کرد:

service insmod-sh /vendor/etc/init.insmod.sh /vendor/etc/init.insmod.<hw>.cfg
    class main
    user root
    group root system
    Disabled
    oneshot

پس از انتقال ماژول ها از مرحله اول به مرحله دوم می توان بهینه سازی های اضافی را انجام داد. می‌توانید از ویژگی modprobe blocklist برای تقسیم جریان بوت مرحله دوم برای بارگذاری ماژول‌های غیرضروری به تعویق افتاده استفاده کنید. بارگذاری ماژول‌هایی که منحصراً توسط یک HAL خاص استفاده می‌شوند را می‌توان برای بارگذاری ماژول‌ها تنها زمانی به تعویق انداخت که HAL شروع به کار کرد.

برای بهبود زمان‌های ظاهری بوت، می‌توانید به طور خاص ماژول‌هایی را در سرویس بارگیری ماژول انتخاب کنید که برای بارگیری پس از صفحه راه‌اندازی مناسب‌تر هستند. به عنوان مثال، می‌توانید ماژول‌ها را برای رمزگشای ویدیو یا وای‌فای پس از پاک شدن جریان راه‌اندازی اولیه، به‌صراحت با تأخیر بارگذاری کنید (برای مثال، سیگنال ویژگی Android sys.boot_complete ). مطمئن شوید که HAL ها برای ماژول های بارگذاری دیرهنگام زمانی که درایورهای هسته وجود ندارند، به اندازه کافی مسدود می شوند.

از طرف دیگر، می‌توانید از دستور init's wait<file>[<timeout>] در اسکریپت‌نویسی جریان راه‌اندازی rc استفاده کنید تا منتظر بمانید تا ورودی‌های sysfs انتخابی نشان دهد که ماژول‌های درایور عملیات probe را کامل کرده‌اند. نمونه ای از این مورد انتظار برای تکمیل بارگذاری درایور نمایشگر در پس زمینه بازیابی یا fastbootd ، قبل از ارائه گرافیک منو است.

فرکانس CPU را به مقدار معقولی در بوت لودر مقداردهی کنید

ممکن است همه SoC ها/محصولات نتوانند CPU را با بالاترین فرکانس بوت کنند، زیرا نگرانی های حرارتی یا برق در طول تست های حلقه راه اندازی وجود دارد. با این حال، مطمئن شوید که بوت لودر فرکانس تمام CPU های آنلاین را تا حد امکان ایمن برای یک SoC/محصول تنظیم می کند. این بسیار مهم است زیرا با یک هسته کاملا ماژولار، فشرده سازی ramdisk اولیه قبل از بارگیری درایور CPUfreq انجام می شود. بنابراین، اگر CPU در انتهای پایین فرکانس خود توسط بوت لودر رها شود، زمان فشرده سازی ramdisk می تواند بیشتر از یک هسته کامپایل شده استاتیکی طول بکشد (پس از تنظیم تفاوت اندازه رام دیسک) زیرا فرکانس CPU هنگام انجام فشرده CPU بسیار پایین خواهد بود. کار (فشرده سازی). همین امر در مورد فرکانس حافظه/اتصال نیز صدق می کند.

فرکانس CPU CPU های بزرگ را در بوت لودر راه اندازی کنید

قبل از بارگیری درایور CPUfreq ، هسته از فرکانس‌های کوچک و بزرگ CPU بی‌اطلاع است و ظرفیت برنامه‌ریزی شده پردازنده‌ها را برای فرکانس فعلی‌شان اندازه نمی‌گیرد. اگر بار روی CPU کوچک به اندازه کافی زیاد باشد، هسته ممکن است رشته ها را به CPU بزرگ منتقل کند.

مطمئن شوید که CPU های بزرگ حداقل به اندازه CPU های کوچک برای فرکانسی که بوت لودر آنها را وارد می کند کارایی دارند. به عنوان مثال، اگر CPU بزرگ 2 برابر عملکرد CPU کوچک برای همان فرکانس باشد، اما بوت لودر آن را تنظیم می کند. فرکانس CPU کوچک به 1.5 گیگاهرتز و فرکانس CPU بزرگ به 300 مگاهرتز، سپس اگر هسته یک رشته را به CPU بزرگ منتقل کند، عملکرد بوت کاهش می یابد. در این مثال، اگر بوت کردن CPU بزرگ با فرکانس 750 مگاهرتز ایمن است، باید این کار را انجام دهید، حتی اگر قصد استفاده صریح از آن را ندارید.

درایورها نباید سیستم عامل را در مرحله اول بارگذاری کنند

ممکن است برخی موارد اجتناب ناپذیر وجود داشته باشد که در آن سیستم عامل باید در مرحله اول بارگیری شود. اما به طور کلی، درایورها نباید هیچ سیستم‌افزاری را در مرحله اول، به‌ویژه در زمینه پروب دستگاه، بارگذاری کنند. بارگذاری سیستم عامل در مرحله اول باعث می شود در صورتی که سیستم عامل در مرحله اول ramdisk در دسترس نباشد، کل فرآیند بوت متوقف شود. و حتی اگر سیستم عامل در مرحله اول ramdisk وجود داشته باشد، باز هم باعث تاخیر غیر ضروری می شود.