شناسایی لرزش‌های مربوط به پرش

لرزش رفتار تصادفی سیستم است که مانع از اجرای کار قابل‌درک می‌شود. این صفحه نحوه شناسایی و رفع مشکلات پرش مربوط به لرزش را شرح می‌دهد.

تأخیر زمان‌بندی رشته برنامه

تأخیر زمان‌بند واضح‌ترین نشانه لرزش است: فرایندی که باید اجرا شود، قابل‌اجرا می‌شود اما برای مدت زمان قابل‌توجهی اجرا نمی‌شود. اهمیت تأخیر بسته به زمینه متفاوت است. برای مثال:

  • رشته کمکی تصادفی در یک برنامه احتمالاً می‌تواند برای چند میلی‌ثانیه بدون مشکل به‌تأخیر بیفتد.
  • رشته واسط کاربر برنامه ممکن است بتواند ۱ تا ۲ میلی‌ثانیه لرزش را تحمل کند.
  • اگر رشته‌های هسته درایور که به‌عنوان SCHED_FIFO اجرا می‌شوند، قبل‌از اجرا به‌مدت ۵۰۰ میکروثانیه قابل‌اجرا باشند، ممکن است مشکلاتی ایجاد کنند.

زمان‌های قابل‌اجرا را می‌توان در systrace با نوار آبی‌رنگ قبل‌از بخش درحال اجرای رشته شناسایی کرد. زمان قابل‌اجرا را می‌توان با مدت زمان بین رویداد sched_wakeup برای یک رشته و رویداد sched_switch که شروع اجرای رشته را نشان می‌دهد نیز تعیین کرد.

رشته‌هایی که خیلی طولانی می‌شوند

رشته‌های میانای کاربر برنامه که برای مدت طولانی قابل اجرا هستند می‌توانند باعث بروز مشکل شوند. رشته‌های سطح پایین با زمان‌های اجراپذیر طولانی معمولاً دلایل متفاوتی دارند، اما تلاش برای کاهش زمان اجراپذیر رشته واسط کاربر به صفر ممکن است نیازمند رفع برخی‌از همان مشکلاتی باشد که باعث می‌شوند رشته‌های سطح پایین زمان‌های اجراپذیر طولانی داشته باشند. برای کاهش تأخیرها:

  1. از cpusets همان‌گونه که در Thermal throttling توضیح داده شده است استفاده کنید.
  2. مقدار CONFIG_HZ را افزایش دهید.
    • ازنظر تاریخی، مقدار در پلاتفرم‌های arm و arm64 روی ۱۰۰ تنظیم شده است. بااین‌حال، این یک اتفاق تاریخی است و مقدار خوبی برای استفاده در دستگاه‌های تعاملی نیست. ‫CONFIG_HZ=100 یعنی یک «جیفی» ۱۰ میلی‌ثانیه طول می‌کشد، که یعنی تراز کردن بار بین CPUها ممکن است ۲۰ میلی‌ثانیه (دو جیفی) طول بکشد. این می‌تواند به‌طور قابل‌توجهی به لرزش در سیستم بارگذاری‌شده کمک کند.
    • دستگاه‌های جدید (Nexus 5X،‏ Nexus 6P،‏ Pixel، و Pixel XL) با CONFIG_HZ=300 عرضه شده‌اند. این کار هزینه برق ناچیزی دارد و درعین‌حال زمان‌های اجرا را به‌طور قابل‌توجهی بهبود می‌بخشد. اگر پس‌از تغییر CONFIG_HZ شاهد افزایش قابل‌توجهی در مصرف برق یا مشکلات عملکردی بودید، احتمالاً یکی از درایورهای شما از زمان‌سنجی براساس jiffies خام به‌جای میلی‌ثانیه استفاده می‌کند و آن را به jiffies تبدیل می‌کند. این مشکل معمولاً به‌راحتی برطرف می‌شود (وصله‌ای را ببینید که مشکلات زمان‌سنج kgsl در Nexus 5X و 6P را هنگام تبدیل به CONFIG_HZ=300 برطرف کرد).
    • در نهایت، CONFIG_HZ=1000 را در Nexus/Pixel آزمایش کردیم و متوجه شدیم که به‌دلیل کاهش سربار RCU، عملکرد و مصرف برق به‌طور قابل‌توجهی بهبود می‌یابد.

تنها با این دو تغییر، دستگاه باید ازنظر زمان اجرایی رشته واسط کاربر تحت بار بسیار بهتر به‌نظر برسد.

استفاده از sys.use_fifo_ui

می‌توانید با تنظیم کردن خصوصیت sys.use_fifo_ui روی ۱، زمان قابل‌اجرا بودن رشته میانای کاربر را به صفر برسانید.

هشدار: از این گزینه در پیکربندی‌های ناهمگن CPU استفاده نکنید، مگر اینکه زمان‌بند RT با ظرفیت آگاه داشته باشید. و در این لحظه، هیچ زمان‌بندی RT درحال ارسالی وجود ندارد که ظرفیت-آگاه باشد. درحال کار روی یکی برای EAS هستیم، اما هنوز دردسترس نیست. زمان‌بند پیش‌فرض RT صرفاً براساس اولویت‌های RT و اینکه آیا واحد پردازش مرکزی ازقبل رشته RT با اولویت برابر یا بالاتر دارد یا نه عمل می‌کند.

درنتیجه، زمان‌بند پیش‌فرض بی‌دردسر رشته رابط کاربری با زمان اجرای نسبتاً طولانی را از هسته بزرگ با فرکانس بالا به هسته کوچک با حداقل فرکانس منتقل می‌کند اگر رشته هسته FIFO با اولویت بالاتر در همان هسته بزرگ بیدار شود. این کار باعث پسرفت قابل‌توجه در عملکرد می‌شود. ازآنجایی‌که این گزینه هنوز در دستگاه Android ارسال‌شده استفاده نشده است، اگر می‌خواهید از آن استفاده کنید با تیم عملکرد Android تماس بگیرید تا به شما در اعتبارسنجی آن کمک کنند.

وقتی sys.use_fifo_ui فعال باشد، ActivityManager رشته میانای کاربر و RenderThread (دو رشته مهم ازنظر میانای کاربر) برنامه برتر را ردیابی می‌کند و آن رشته‌ها را به‌جای SCHED_OTHER به SCHED_FIFO تبدیل می‌کند. این کار به‌طور مؤثری لرزش را از UI و RenderThreads حذف می‌کند؛ ردیابی‌هایی که با فعال کردن این گزینه جمع‌آوری کرده‌ایم، زمان‌های قابل‌اجرا را در حد میکروثانیه به‌جای میلی‌ثانیه نشان می‌دهند.

بااین‌حال، چون ترازکننده بار RT ظرفیت‌آگاه نبود، عملکرد راه‌اندازی برنامه ۳۰٪ کاهش یافت زیرا رشته رابط کاربری مسئول راه‌اندازی برنامه از هسته طلایی Kryo با فرکانس ۲٫۱ گیگاهرتز به هسته نقره‌ای Kryo با فرکانس ۱٫۵ گیگاهرتز منتقل می‌شد. با ترازکننده بار RT آگاه از ظرفیت، عملکرد معادل در عملیات انبوه و کاهش ۱۰ تا ۱۵ درصدی در زمان‌های فریم صدک ۹۵ و ۹۹ در بسیاری از محک‌های رابط کاربری خود مشاهده می‌کنیم.

ترافیک قطع‌شده

ازآنجایی‌که پلاتفرم‌های ARM به‌طور پیش‌فرض فقط به CPU 0 وقفه ارائه می‌دهند، توصیه می‌کنیم از ترازکننده IRQ (irqbalance یا msm_irqbalance در پلاتفرم‌های Qualcomm) استفاده کنید.

درطول توسعه Pixel، شاهد پرش‌هایی بودیم که مستقیماً به سربارگذاری CPU 0 با وقفه‌ها نسبت داده می‌شد. برای مثال، اگر رشته mdss_fb0 در CPU 0 زمان‌بندی شده بود، احتمال پرش تصویر بسیار بیشتر بود چون وقفه‌ای که توسط نمایشگر ایجاد می‌شود تقریباً بلافاصله قبل‌از اسکن‌اوت رخ می‌دهد. ‫mdss_fb0 درحال انجام کار خود با مهلتی بسیار فشرده بود، و سپس مقداری از زمان خود را به‌دلیل مدیریت‌کننده وقفه MDSS ازدست می‌داد. در ابتدا، سعی کردیم این مشکل را با تنظیم وابستگی CPU رشته mdss_fb0 به CPUهای ۱ تا ۳ برطرف کنیم تا از رقابت با وقفه جلوگیری کنیم، اما سپس متوجه شدیم که msm_irqbalance را هنوز فعال نکرده‌ایم. با فعال شدن msm_irqbalance، حتی زمانی که mdss_fb0 و وقفه MDSS در یک CPU بودند، به‌دلیل کاهش رقابت از سایر وقفه‌ها، لرزش به‌طور قابل‌توجهی بهبود یافت.

این را می‌توان در systrace با نگاه کردن به بخش sched و همچنین بخش irq شناسایی کرد. بخش زمان‌بندی نشان می‌دهد چه چیزی زمان‌بندی شده است، اما منطقه هم‌پوشانی در بخش irq به این معنی است که یک وقفه درطول آن زمان به‌جای فرایند زمان‌بندی‌شده عادی درحال اجرا است. اگر می‌بینید که بخش‌های قابل‌توجهی از زمان درطول وقفه گرفته می‌شود، گزینه‌های شما شامل موارد زیر است:

  • مدیر وقفه‌گر را سریع‌تر کنید.
  • از همان ابتدا از وقوع وقفه جلوگیری کنید.
  • بسامد وقفه را تغییر دهید تا با سایر کارهای معمولی که ممکن است با آن‌ها تداخل داشته باشد هم‌فاز نباشد (اگر وقفه معمولی است).
  • وابستگی واحد پردازش مرکزی وقفه را مستقیماً تنظیم کنید و از متعادل شدن آن جلوگیری کنید.
  • وابستگی واحد پردازش مرکزی رشته‌ای را که وقفه با آن تداخل دارد تنظیم کنید تا از وقفه جلوگیری شود.
  • به متعادل‌کننده وقفه تکیه کنید تا وقفه را به واحد پردازش مرکزی با بار کمتر منتقل کند.

تنظیم وابستگی CPU معمولاً توصیه نمی‌شود اما می‌تواند برای موارد خاص مفید باشد. به‌طورکلی، پیش‌بینی وضعیت سیستم برای بیشتر وقفه‌های رایج بسیار دشوار است، اما اگر مجموعه شرایط بسیار خاصی دارید که وقفه‌های خاصی را در جایی که سیستم محدودتر از حد معمول است (مانند VR) راه‌اندازی می‌کند، قرابت صریح CPU ممکن است راه‌حل خوبی باشد.

وقفه نرم طولانی

وقتی softirq درحال اجرا است، پیش‌گیری را غیرفعال می‌کند. softirqها همچنین می‌توانند در بسیاری از مکان‌های درون هسته راه‌اندازی شوند و می‌توانند در فرایند کاربر اجرا شوند. اگر فعالیت softirq کافی وجود داشته باشد، فرایندهای کاربر اجرای softirq را متوقف می‌کنند، و ksoftirqd برای اجرای softirq و تراز بار بیدار می‌شود. معمولاً این کار مشکلی ندارد. بااین‌حال، یک softirq بسیار طولانی می‌تواند سیستم را دچار اختلال کند.


‫softirq در بخش irq ردیابی قابل‌مشاهده است، بنابراین اگر مشکل درحین ردیابی قابل بازتولید باشد، به‌راحتی می‌توان آن را تشخیص داد. چون softirq می‌تواند در فرایند کاربر اجرا شود، softirq بد می‌تواند به‌صورت زمان اجرای اضافی در فرایند کاربر بدون دلیل مشخصی ظاهر شود. اگر این را می‌بینید، بخش irq را بررسی کنید تا ببینید آیا softirq مقصر است یا نه.

درایورهایی که پیش‌دستی را ترک می‌کنند یا IRQها برای مدت طولانی غیرفعال شده‌اند

غیرفعال کردن پیش‌گیری یا وقفه‌ها برای مدت طولانی (ده‌ها میلی‌ثانیه) منجر به لرزش می‌شود. معمولاً، این تأخیر زمانی ظاهر می‌شود که رشته‌ای قابل‌اجرا می‌شود اما روی CPU خاصی اجرا نمی‌شود، حتی اگر اولویت رشته قابل‌اجرا به‌طور قابل‌توجهی بالاتر از رشته دیگر باشد (یا SCHED_FIFO).

چند دستورالعمل:

  • اگر رشته اجراشدنی SCHED_FIFO و رشته درحال اجرا SCHED_OTHER باشد، رشته درحال اجرا پیش‌گیری یا وقفه‌های غیرفعال دارد.
  • اگر اولویت رشته اجراشدنی (۱۰۰) به‌طور قابل‌توجهی بالاتر از رشته درحال اجرا (۱۲۰) باشد، اگر رشته اجراشدنی در دو جیفی اجرا نشود، احتمالاً رشته درحال اجرا پیش‌گیری یا وقفه‌های غیرفعال دارد.
  • اگر رشته قابل‌اجرا و رشته درحال‌اجرا اولویت یکسانی داشته باشند، احتمالاً رشته درحال‌اجرا پیش‌دستی یا وقفه غیرفعال دارد اگر رشته قابل‌اجرا ظرف ۲۰ میلی‌ثانیه اجرا نشود.

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


گزینه دیگر برای شناسایی مناطق متخلف، ردیاب preemptirqsoff است (به استفاده از ftrace پویا مراجعه کنید). این ردیاب می‌تواند اطلاعات بسیار بیشتری درباره علت اصلی منطقه بدون وقفه (مثل نام تابع) ارائه دهد، اما برای فعال کردن آن باید کار تهاجمی‌تری انجام شود. اگرچه ممکن است تأثیر بیشتری بر عملکرد داشته باشد، اما قطعاً ارزش امتحان کردن را دارد.

استفاده نادرست از صف‌های کاری

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

بااین‌حال، صف‌های کار تقریباً همیشه پاسخ اشتباهی برای این مشکل هستند زیرا همیشه SCHED_OTHER هستند. بسیاری از وقفه‌های سخت‌افزاری در مسیر بحرانی عملکرد قرار دارند و باید فوراً اجرا شوند. صف‌های کار هیچ تضمینی درباره زمان اجرا ندارند. هر بار که صف کار را در مسیر بحرانی عملکرد دیده‌ایم، صرف‌نظر از دستگاه، منبعی برای لرزش‌های پراکنده بوده است. در Pixel، با پردازنده پرچم‌دار، دیدیم که اگر دستگاه تحت بار باشد، یک صف کار می‌تواند تا ۷ میلی‌ثانیه تأخیر داشته باشد، که این تأخیر به رفتار زمان‌بند و سایر موارد درحال اجرا در سیستم بستگی دارد.

به‌جای صف کاری، درایورهایی که باید کارهای شبیه به وقفه را در رشته‌ای جداگانه مدیریت کنند، باید kthread‏ SCHED_FIFO خودشان را ایجاد کنند. برای کمک به انجام این کار با توابع kthread_work، به این وصله مراجعه کنید.

درگیری بر سر قفل چارچوب

رقابت برای قفل چارچوب می‌تواند منبع لرزش یا مشکلات دیگر عملکرد باشد. این خطا معمولاً به‌دلیل قفل ActivityManagerService رخ می‌دهد اما ممکن است در قفل‌های دیگر نیز دیده شود. برای مثال، قفل PowerManagerService می‌تواند بر عملکرد روشن بودن صفحه‌نمایش تأثیر بگذارد. اگر این را در دستگاهتان می‌بینید، راه‌حل خوبی وجود ندارد زیرا فقط ازطریق بهبودهای معماری در چارچوب می‌توان آن را بهبود بخشید. اما اگر کدی را که در system_server اجرا می‌شود اصلاح می‌کنید، بسیار مهم است که از نگه داشتن قفل‌ها برای مدت طولانی، به‌ویژه قفل ActivityManagerService، اجتناب کنید.

درگیری بر سر قفل دستگاه صحافی

ازنظر تاریخی، binder یک قفل سراسری واحد داشته است. اگر رشته‌ای که تراکنش binder را اجرا می‌کند درحین نگه داشتن قفل پیش‌دستی شده باشد، هیچ رشته دیگری نمی‌تواند تراکنش binder را انجام دهد تا زمانی که رشته اصلی قفل را آزاد کند. این بد است؛ رقابت برای اتصال می‌تواند همه چیز را در سیستم مسدود کند، ازجمله ارسال به‌روزرسانی‌های میانای کاربر به نمایشگر (رشته‌های میانای کاربر ازطریق اتصال با SurfaceFlinger ارتباط برقرار می‌کنند).

‫Android 6.0 شامل چندین وصله برای بهبود این عملکرد با غیرفعال کردن پیش‌گیری درحین نگه داشتن قفل چسباننده است. این کار فقط به این دلیل ایمن بود که قفل پیوند باید برای چند میکروثانیه از زمان اجرای واقعی نگه داشته شود. این کار عملکرد را در شرایط بدون رقابت به‌طور چشمگیری بهبود بخشید و با جلوگیری از اکثر تغییرات زمان‌بندی‌کننده درحین نگه داشتن قفل پیونددهنده، از رقابت جلوگیری کرد. بااین‌حال، پیش‌گیری برای کل زمان اجرای نگهداری قفل پیوند غیرفعال نشد، یعنی پیش‌گیری برای توابعی که می‌توانستند بخوابند (مثل copy_from_user) فعال بود، که می‌توانست باعث همان پیش‌گیری مورد اصلی شود. وقتی وصله‌ها را به بالادست ارسال کردیم، آن‌ها بلافاصله به ما گفتند که این بدترین ایده در تاریخ است. (ما با آن‌ها موافق بودیم، اما نمی‌توانستیم با کارایی وصله‌ها در جلوگیری از لرزش مخالفت کنیم.)

رقابت fd در یک فرایند

این اتفاق به‌ندرت می‌افتد. احتمالاً این مورد باعث لرزش نمی‌شود.

بااین‌حال، اگر چندین رشته در یک فرایند درحال نوشتن fd یکسان باشند، ممکن است در این fd رقابت ببینید، اما تنها زمانی که این مورد را درطول راه‌اندازی Pixel دیدیم درطول آزمایشی بود که در آن رشته‌های با اولویت پایین تلاش می‌کردند تمام زمان CPU را اشغال کنند درحالی‌که یک رشته با اولویت بالا در همان فرایند درحال اجرا بود. همه رشته‌ها درحال نوشتن در نشانگر ردگیری fd بودند و رشته با اولویت بالا می‌توانست در نشانگر ردگیری fd مسدود شود اگر رشته با اولویت پایین قفل fd را نگه می‌داشت و سپس پیش‌دستی می‌کرد. وقتی ردیابی از رشته‌های کم‌اولویت غیرفعال شد، مشکل عملکردی وجود نداشت.

نتوانستیم این مشکل را در هیچ وضعیت دیگری بازتولید کنیم، اما بهتر است به‌عنوان یکی از دلایل احتمالی مشکلات عملکرد درحین ردیابی به آن اشاره کنیم.

انتقال‌های غیرضروری واحد پردازش مرکزی به حالت بیکار

هنگام کار با IPC، به‌ویژه خطوط لوله چندفرایندی، معمولاً تغییراتی در رفتار زمان اجرای زیر مشاهده می‌شود:

  1. رشته A در CPU 1 اجرا می‌شود.
  2. رشته A رشته B را بیدار می‌کند.
  3. رشته B در واحد پردازش مرکزی ۲ شروع به اجرا می‌کند.
  4. رشته A بلافاصله به حالت خواب می‌رود تا زمانی که رشته B کار فعلی خود را تمام کند، توسط رشته B بیدار شود.

منبع رایج سربار بین مراحل ۲ و ۳ است. اگر CPU 2 در حالت آماده‌به‌کار باشد، قبل‌از اینکه رشته B بتواند اجرا شود، باید به حالت فعال برگردانده شود. بسته به SOC و میزان عمیق بودن حالت بیکار، این زمان می‌تواند ده‌ها میکروثانیه قبل‌از شروع اجرای رشته B باشد. اگر زمان اجرای واقعی هر طرف IPC به سربار نزدیک باشد، عملکرد کلی آن خط لوله می‌تواند به‌طور قابل‌توجهی توسط انتقال‌های بیکاری CPU کاهش یابد. رایج‌ترین مکان برای Android برای مواجهه با این مشکل در تراکنش‌های binder است، و بسیاری از سرویس‌هایی که از binder استفاده می‌کنند درنهایت شبیه وضعیت توصیف‌شده در بالا می‌شوند.

ابتدا، از تابع wake_up_interruptible_sync() در درایورهای هسته استفاده کنید و از هر زمان‌بند سفارشی از آن پشتیبانی کنید. این را به‌عنوان الزامی درنظر بگیر، نه راهنمایی. ‫Binder امروز از این ویژگی استفاده می‌کند و این ویژگی در تراکنش‌های هم‌زمان binder بسیار مفید است و از انتقال‌های غیرضروری CPU به حالت آماده به‌کار جلوگیری می‌کند.

دوم، مطمئن شوید زمان‌های انتقال cpuidle شما واقع‌بینانه باشد و cpuidle governor این موارد را به‌درستی درنظر بگیرد. اگر SOC شما در عمیق‌ترین حالت آماده‌به‌کار خود درحال رفت‌وآمد باشد، با رفتن به عمیق‌ترین حالت آماده‌به‌کار، در مصرف انرژی صرفه‌جویی نخواهید کرد.

گزارش‌گیری

ثبت گزارش برای چرخه‌های CPU یا حافظه رایگان نیست، بنابراین با ارسال هرزنامه به بافر گزارش، آن را پر نکنید. ثبت گزارش چرخه‌های هزینه را در برنامه شما (مستقیماً) و در سرپرست گزارش ثبت می‌کند. قبل‌از ارسال دستگاه، همه گزارش‌های اشکال‌زدایی را بردارید.

مشکلات ورودی/خروجی

عملیات ورودی/خروجی منابع رایج لرزش هستند. اگر رشته‌ای به فایل نگاشت‌شده در حافظه دسترسی پیدا کند و صفحه در حافظه نهان صفحه نباشد، خطا می‌دهد و صفحه را از دیسک می‌خواند. این کار رشته را مسدود می‌کند (معمولاً برای بیش‌از ۱۰ میلی‌ثانیه) و اگر در مسیر حیاتی پرداز UI اتفاق بیفتد، می‌تواند منجر به لرزش شود. دلایل زیادی برای عملیات ورودی/خروجی وجود دارد که در اینجا نمی‌توان به همه آن‌ها پرداخت، اما هنگام تلاش برای بهبود عملکرد ورودی/خروجی، مکان‌های زیر را بررسی کنید:

  • PinnerService. ‫PinnerService که در Android 7.0 اضافه شده است به چارچوب امکان می‌دهد برخی‌از فایل‌ها را در حافظه نهان صفحه قفل کند. این کار حافظه را برای استفاده توسط هر فرایند دیگری برمی‌دارد، اما اگر فایل‌هایی وجود داشته باشند که ازقبل مشخص باشد به‌طور منظم استفاده می‌شوند، قفل کردن آن فایل‌ها می‌تواند مؤثر باشد.

    در دستگاه‌های Pixel و Nexus 6P دارای Android 7.0، چهار فایل را قفل کردیم:
    • /system/framework/arm64/boot-framework.oat
    • /system/framework/oat/arm64/services.odex
    • /system/framework/arm64/boot.oat
    • /system/framework/arm64/boot-core-libart.oat
    این فایل‌ها به‌طور مداوم توسط اکثر برنامه‌ها و system_server استفاده می‌شوند، بنابراین نباید صفحه‌بندی شوند. به‌ویژه متوجه شده‌ایم که اگر هرکدام از آن‌ها صفحه‌بندی شوند، دوباره صفحه‌بندی خواهند شد و هنگام جابه‌جایی از یک برنامه سنگین باعث ایجاد لرزش می‌شوند.
  • رمزگذاری. علت احتمالی دیگر برای مشکلات ورودی/خروجی. ما متوجه شدیم که رمزگذاری درون‌خطی درمقایسه با رمزگذاری مبتنی بر CPU یا استفاده از بلوک سخت‌افزاری قابل‌دسترسی ازطریق DMA بهترین عملکرد را ارائه می‌دهد. مهم‌تر از همه، رمزگذاری درون‌خطی لرزش مرتبط با ورودی/خروجی را کاهش می‌دهد، به‌ویژه وقتی با رمزگذاری مبتنی بر CPU مقایسه شود. ازآنجایی‌که واکشی‌های حافظه نهان صفحه اغلب در مسیر بحرانی پردازش واسط کاربر قرار دارند، رمزگذاری مبتنی بر CPU بار CPU اضافی را در مسیر بحرانی معرفی می‌کند که لرزش بیشتری نسبت‌به واکشی I/O اضافه می‌کند.

    موتورهای رمزگذاری سخت‌افزاری مبتنی بر DMA مشکل مشابهی دارند، زیرا هسته باید چرخه‌هایی را برای مدیریت آن کار صرف کند، حتی اگر کار مهم دیگری برای اجرا دردسترس باشد. به همه فروشندگان SOC که سخت‌افزار جدید می‌سازند اکیداً توصیه می‌کنیم که از رمزگذاری درخط پشتیبانی کنند.

بسته‌بندی تهاجمی تکلیف کوچک

برخی زمان‌بندها از بسته‌بندی کردن وظایف کوچک در هسته‌های واحد پردازش مرکزی پشتیبانی می‌کنند تا با بیکار نگه داشتن واحدهای پردازش مرکزی بیشتر برای مدت طولانی‌تر، مصرف برق را کاهش دهند. اگرچه این روش برای توان عملیاتی و مصرف برق خوب است، اما می‌تواند برای تأخیر فاجعه‌بار باشد. چندین رشته کوتاه در مسیر بحرانی پردازش میانای کاربری وجود دارد که می‌توان آن‌ها را کوچک درنظر گرفت؛ اگر این رشته‌ها به‌دلیل انتقال آهسته به CPUهای دیگر با تأخیر مواجه شوند، باعث ایجاد پرش می‌شوند. توصیه می‌کنیم از بسته‌بندی تکلیف کوچک بسیار محتاطانه استفاده کنید.

کوبش حافظه نهان صفحه

دستگاهی که حافظه آزاد کافی ندارد ممکن است هنگام انجام عملیات طولانی‌مدت، مانند باز کردن یک برنامه جدید، ناگهان بسیار کند شود. ردگیری برنامه ممکن است نشان دهد که به‌طور مداوم در ورودی/خروجی درطول یک اجرای خاص مسدود شده است، حتی زمانی که اغلب در ورودی/خروجی مسدود نیست. این معمولاً نشانه‌ای از کوبش حافظه نهان صفحه است، به‌ویژه در دستگاه‌هایی که حافظه کمتری دارند.

یکی از راه‌های شناسایی این مورد این است که بااستفاده از برچسب pagecache یک systrace بگیرید و آن ردیابی را در system/extras/pagecache/pagecache.py به اسکریپت بدهید. ‫pagecache.py درخواست‌های تکی برای نگاشت فایل‌ها در حافظه پنهان صفحه را به آمار کلی برای هر فایل ترجمه می‌کند. اگر متوجه شدید که تعداد بایت‌های خوانده‌شده از یک فایل بیشتر از اندازه کل آن فایل در دیسک است، قطعاً با مشکل «تخریب حافظه نهان صفحه» مواجه هستید.

این یعنی مجموعه کاری موردنیاز حجم کار شما (معمولاً یک برنامه به‌علاوه system_server) بیشتر از مقدار حافظه دردسترس برای حافظه نهان صفحه در دستگاهتان است. درنتیجه، وقتی بخشی از بار کاری داده‌های موردنیاز خود را در حافظه نهان صفحه دریافت می‌کند، بخش دیگری که در آینده نزدیک استفاده خواهد شد از حافظه نهان خارج می‌شود و باید دوباره واکشی شود، که باعث می‌شود مشکل تا زمان تکمیل بار دوباره رخ دهد. این دلیل اصلی مشکلات عملکردی است که وقتی حافظه کافی در دستگاه دردسترس نباشد رخ می‌دهد.

هیچ روش مطمئنی برای رفع مشکل «تخریب حافظه نهان صفحه» وجود ندارد، اما چند روش برای تلاش جهت بهبود این وضعیت در یک دستگاه خاص وجود دارد.

  • در فرایندهای ماندگار از حافظه کمتری استفاده کنید. هرچه حافظه کمتری توسط فرایندهای پایدار استفاده شود، حافظه بیشتری برای برنامه‌ها و حافظه پنهان صفحه دردسترس خواهد بود.
  • بخش‌های اختصاصی دستگاهتان را ممیزی کنید تا مطمئن شوید حافظه را به‌طور غیرضروری از سیستم‌عامل حذف نمی‌کنید. مواردی را دیده‌ایم که کاربردهای جانبی که برای اشکال‌زدایی استفاده می‌شدند به‌طور تصادفی در پیکربندی‌های هسته ارسال باقی مانده بودند و ده‌ها مگابایت حافظه مصرف می‌کردند. این می‌تواند تفاوت بین رسیدن به حافظه نهان صفحه و نرسیدن به آن باشد، به‌ویژه در دستگاه‌هایی با حافظه کمتر.
  • اگر در system_server برای فایل‌های حیاتی با «تخریب حافظه نهان صفحه» مواجه هستید، آن فایل‌ها را سنجاق کنید. این کار فشار حافظه را در جاهای دیگر افزایش می‌دهد، اما ممکن است رفتار را به‌اندازه کافی تغییر دهد تا از تلاطم جلوگیری شود.
  • ‫lowmemorykiller را دوباره تنظیم کنید تا حافظه بیشتری را آزاد نگه دارد. آستانه‌های lowmemorykiller براساس حافظه آزاد مطلق و حافظه نهان صفحه است، بنابراین افزایش آستانه‌ای که در آن فرایندهای سطح oom_adj معین متوقف می‌شوند ممکن است منجر به عملکرد بهتر به‌قیمت افزایش توقف برنامه پس‌زمینه‌ای شود.
  • از ZRAM استفاده کنید. ما از ZRAM در Pixel استفاده می‌کنیم، حتی اگر Pixel دارای ۴ گیگابایت حافظه باشد، زیرا می‌تواند به صفحات کثیف که به‌ندرت استفاده می‌شوند کمک کند.