میتوانید ردیابی سیستم را پیکربندی کنید تا نمایه CPU و رشته برنامه خود را در یک دوره زمانی کوتاه ضبط کنید. سپس میتوانید از گزارش برونداد ردیابی سیستم برای بهبود عملکرد بازیتان استفاده کنید.
راهاندازی ردیابی سیستم مبتنی بر بازی
ابزار Systrace به دو روش دردسترس است:
Systrace ابزاری سطح پایین است که:
- واقعیت عینی را ارائه میدهد. Systrace برونداد را مستقیماً از هسته ضبط میکند، بنابراین سنجههایی که ضبط میکند تقریباً با سنجههایی که مجموعهای از فراخوانهای سیستم گزارش میکنند یکسان است.
- منابع کمی مصرف میکند. Systrace سربار بسیار کمی روی دستگاه ایجاد میکند، معمولاً کمتر از ۱٪، زیرا دادهها را در یک بافر درونحافظهای جاریسازی میکند.
تنظیمات بهینه
مهم است که مجموعه معقولی از آرگومانها را به ابزار بدهید:
- دستهها: بهترین مجموعه دستهها برای فعال کردن ردیابی سیستم مبتنی بر بازی عبارتاند از: {
sched،freq،idle،am،wm،gfx،view،sync،binder_driver،hal،dalvik}. اندازه بافر: قانون کلی این است که اندازه بافر ۱۰ مگابایت در هر هسته CPU اجازه میدهد ردیابی حدود ۲۰ ثانیه طول بکشد. برای مثال، اگر دستگاهی دارای دو CPU چهار هستهای (مجموعاً ۸ هسته) باشد، مقدار مناسب برای انتقال به برنامه
systrace، ۸۰٬۰۰۰ کیلوبایت (۸۰ مگابایت) است.اگر بازی شما مقدار زیادی تغییر بافت انجام میدهد، بافر را به ۱۵ مگابایت در هر هسته CPU افزایش دهید.
رویدادهای سفارشی: اگر رویدادهای سفارشی تعریف میکنید تا در بازیتان ضبط کنید، پرچم
-aرا فعال کنید، که به Systrace اجازه میدهد این رویدادهای سفارشی را در گزارش برونداد بگنجاند.
اگر از برنامه خط فرمان systrace استفاده میکنید، از فرمان زیر برای گرفتن ردیابی سیستم استفاده کنید که روالهای مطلوب را برای مجموعه دستهها، اندازه بافر، و رویدادهای سفارشی اعمال میکند:
python systrace.py -a com.example.myapp -b 80000 -o my_systrace_report.html \ sched freq idle am wm gfx view sync binder_driver hal dalvik
اگر از برنامه سیستم Systrace در دستگاه استفاده میکنید، مراحل زیر را برای ضبط ردیابی سیستم که روالهای مطلوب را برای مجموعه دستهها، اندازه بافر، و رویدادهای سفارشی اعمال میکند تکمیل کنید:
گزینه ردیابی برنامههای اشکالزداییشدنی را فعال کنید.
برای استفاده از این تنظیم، دستگاه باید ۲۵۶ مگابایت یا ۵۱۲ مگابایت فضای دردسترس داشته باشد (بسته به اینکه CPU دارای ۴ یا ۸ هسته باشد)، و هر بخش ۶۴ مگابایتی از حافظه باید بهعنوان یک بخش پیوسته دردسترس باشد.
دستهها را انتخاب کنید، سپس دستههای فهرست زیر را فعال کنید:
-
am: مدیر فعالیت -
binder_driver: درایور هسته Binder -
dalvik: Dalvik VM -
freq: بسامد واحد پردازش مرکزی gfx: گرافیک-
hal: واحدهای سختافزاری -
idle: واحد پردازش مرکزی بیکار -
sched: زمانبندی واحد پردازش مرکزی sync: همگامسازی-
view: مشاهده سیستم -
wm: مدیر پنجره
-
ردیابی ضبط را فعال کنید.
بازیتان را بار کنید.
تعاملهای مربوط به شیوه بازی را که میخواهید عملکرد دستگاه را برای آن اندازهگیری کنید در بازیتان انجام دهید.
کمی پساز مواجهه با رفتار نامطلوب در بازی، ردیابی سیستم را خاموش کنید.
آمار عملکرد موردنیاز برای تجزیهوتحلیل بیشتر مشکل را ضبط کردهاید.
برای صرفهجویی در فضای دیسک، ردیابیهای سیستم دروندستگاهی فایلها را در قالب ردیابی فشرده (*.ctrace) ذخیره میکنند. برای ازفشردهسازی کردن این فایل هنگام تولید گزارش، از برنامه خط فرمان استفاده کنید و گزینه --from-file را اضافه کنید:
python systrace.py --from-file=/data/local/traces/my_game_trace.ctrace \ -o my_systrace_report.html
بهبود دادن زمینههای عملکرد خاص
این بخش چندین مشکل رایج در عملکرد بازیهای تلفن همراه را برجسته میکند و نحوه شناسایی و بهبود این جنبههای بازی را شرح میدهد.
سرعت بار کردن
بازیکنان میخواهند در اسرع وقت وارد اکشن بازی شما شوند، بنابراین بهبود زمان بارگذاری بازی تا حد امکان مهم است. اقدامات زیر معمولاً به زمان بارگیری کمک میکنند:
- بارکردن کُند را انجام دهید. اگر در صحنهها یا سطوح متوالی بازیتان از داراییهای یکسانی استفاده میکنید، این داراییها را فقط یکبار بار کنید.
- اندازه داراییهایتان را کاهش دهید. به این ترتیب، میتوانید نسخههای فشردهنشده این داراییها را با فایل APK بازیتان دستهبندی کنید.
- از روش فشردهسازی کارآمد دیسک استفاده کنید. نمونهای از چنین روشی zlib است.
- بهجای mono از IL2CPP استفاده کنید. (فقط درصورتی اعمال میشود که از Unity استفاده میکنید.) IL2CPP عملکرد اجرای بهتری برای دستورگان C# شما ارائه میدهد.
- بازی خود را چندرشتهای کنید. برای جزئیات بیشتر، بخش ثبات نرخ فریم را ببینید.
یکنواختی نرخ فریم
یکی از مهمترین عناصر تجربه بازی، دستیابی به نرخ فریم ثابت است. برای آسانتر کردن دستیابی به این هدف، تکنیکهای بهینهسازی که در این بخش بحث شده است را دنبال کنید.
چندریسمانی
هنگام توسعه برای چند پلاتفرم، طبیعی است که همه فعالیتها را در بازی خود در یک رشته قرار دهید. اگرچه این روش اجرا در بسیاری از موتورهای بازی ساده است، اما هنگام اجرا در دستگاههای Android بهینه نیست. در نتیجه، بازیهای تکرشتهای اغلب بهکندی بار میشوند و نرخ فریم ثابتی ندارند.
Systrace نشاندادهشده در «شکل ۱» رفتاری را نمایش میدهد که برای بازیای که در هر زمان فقط روی یک CPU اجرا میشود معمول است:
برای بهبود عملکرد بازی، بازیتان را چندرشتهای کنید. معمولاً، بهترین مدل داشتن ۲ رشته است:
- رشته بازی که شامل واحدهای اصلی بازی شما است و دستورات پردازنده ارسال میکند.
- رشته پرداز که فرمانهای پرداز را دریافت میکند و آنها را به فرمانهای گرافیکی ترجمه میکند که GPU دستگاه میتواند از آنها برای نمایش صحنه استفاده کند.
Vulkan API با قابلیت ارسال موازی ۲ بافر مشترک، این مدل را گسترش میدهد. بااستفاده از این ویژگی، میتوانید چندین رشته رندر را در چندین CPU توزیع کنید و زمان رندر صحنه را بیشتر بهبود دهید.
همچنین میتوانید تغییرات خاص موتور را برای بهبود عملکرد چندرشتهای بازی خود انجام دهید:
- اگر بازیتان را بااستفاده از موتور بازی Unity توسعه میدهید، گزینههای پردازش چندرشتهای و پوستگذاری GPU را فعال کنید.
- اگر از موتور پردازش سفارشی استفاده میکنید، مطمئن شوید که خط لوله فرمان پردازش و خط لوله فرمان گرافیک بهدرستی تراز شده باشند؛ درغیراینصورت، ممکن است در نمایش صحنههای بازی تأخیر ایجاد شود.
پساز اعمال این تغییرات، باید ببینید که بازی شما حداقل ۲ واحد پردازش مرکزی را بهطور همزمان اشغال میکند، همانطور که در «شکل ۲» نشان داده شده است:
درحال بار کردن عنصر میانای کاربر
هنگام ساختن بازیای با ویژگیهای غنی، وسوسهانگیز است که گزینهها و کنشهای مختلف زیادی را بهطور همزمان به بازیکن نشان دهید. بااینحال، برای حفظ نرخ فریم ثابت، توجه به اندازه نسبتاً کوچک نمایشگرهای تلفن همراه و ساده نگه داشتن میانای کاربر تا حد امکان مهم است.
گزارش Systrace نشاندادهشده در «شکل ۳» نمونهای از چارچوب رابط کاربری است که تلاش میکند عناصر زیادی را نسبت به قابلیتهای دستگاه همراه پردازش کند.
هدف خوب این است که زمان بهروزرسانی واسط کاربر را به ۲ تا ۳ میلیثانیه کاهش دهید. با انجام بهینهسازیهای مشابه موارد زیر میتوانید به چنین بهروزرسانیهای سریعی دست یابید:
- فقط عناصر روی صفحه را که جابهجا شدهاند بهروزرسانی کنید.
- تعداد بافتها و لایههای رابط کاربری را محدود کنید. تماسهای گرافیکی، مثل سایهزنها و بافتها، را که از مواد یکسان استفاده میکنند ترکیب کنید.
- عملیات پویانمایی عنصر را به GPU واگذار کنید.
- حذف کردن مخروط دید و انسداد را بهصورت تهاجمیتر انجام دهید.
- درصورت امکان، عملیات طراحی را بااستفاده از Vulkan API انجام دهید. سربار فراخوانی طراحی در Vulkan کمتر است.
مصرف برق
حتی پساز انجام بهینهسازیهایی که در بخش قبلی بحث شد، ممکن است متوجه شوید که نرخ فریم بازی شما در ۴۵ تا ۵۰ دقیقه اول بازی کاهش مییابد. علاوهبراین، دستگاه ممکن است بهمرور زمان شروع به گرم شدن کند و باتری بیشتری مصرف کند.
در بسیاری از موارد، این مجموعه نامطلوب از دما و مصرف برق مربوط به نحوه توزیع بار کاری بازی شما در واحدهای پردازش مرکزی دستگاه است. برای افزایش کارایی مصرف انرژی بازیتان، روالهای مطلوب نشاندادهشده در بخشهای زیر را اعمال کنید.
رشتههای سنگین حافظه را در یک واحد پردازش مرکزی نگه دارید
در بسیاری از دستگاههای همراه، حافظههای نهان L1 در CPUهای خاصی قرار دارند و حافظههای نهان L2 در مجموعه CPUهایی قرار دارند که ساعت مشترک دارند. برای بیشینه کردن بازدیدهای حافظه نهان L1، معمولاً بهتر است رشته اصلی بازیتان را همراه با هر رشته سنگین حافظه دیگری در یک CPU اجرا کنید.
بهتعویق انداختن کارهای کوتاهمدت به واحدهای پردازش مرکزی با توان کمتر
اکثر موتورهای بازی، ازجمله Unity، میدانند که عملیات رشته کارگر را به واحد پردازش مرکزی دیگری نسبت به رشته اصلی بازی شما واگذار کنند. بااینحال، موتور از معماری خاص دستگاه آگاه نیست و نمیتواند بار کاری بازی شما را بهخوبی شما پیشبینی کند.
اکثر دستگاههای سیستم روی تراشه حداقل ۲ ساعت مشترک دارند، یکی برای CPUهای سریع دستگاه و دیگری برای CPUهای کند دستگاه. یکی از پیامدهای این معماری این است که اگر یک واحد پردازش مرکزی سریع نیاز داشته باشد با حداکثر سرعت کار کند، همه واحدهای پردازش مرکزی سریع دیگر نیز با حداکثر سرعت کار میکنند.
گزارش نمونه نشاندادهشده در «شکل ۴» بازیای را نشان میدهد که از CPUهای سریع استفاده میکند. بااینحال، این سطح فعالیت بالا بهسرعت مقدار زیادی نیرو و گرما تولید میکند.
برای کاهش مصرف کلی انرژی، بهتر است به زمانبند پیشنهاد دهید که کار با مدت کوتاهتر—مثل بار کردن صدا، اجرای رشتههای کارگر، و اجرای هماهنگکننده—به مجموعه CPUهای کند در دستگاه موکول شود. تا جایی که میتوانید این کار را به CPUهای کند منتقل کنید و درعینحال نرخ فریم موردنظر را حفظ کنید.
اکثر دستگاهها واحدهای پردازش مرکزی (CPU) کند را قبلاز واحدهای پردازش مرکزی سریع فهرست میکنند، اما نمیتوانید فرض کنید که «سیستم روی تراشه» دستگاهتان از این ترتیب استفاده میکند. برای بررسی، دستوراتی مشابه دستورات نشاندادهشده در این کد شناسایی توپولوژی CPU در GitHub اجرا کنید.
پساز اینکه متوجه شدید کدام CPUها در دستگاهتان کند هستند، میتوانید وابستگیهایی را برای رشتههای کوتاه مدت خود تعریف کنید که زمانبند دستگاه از آنها پیروی میکند. برای انجام این کار، کد زیر را به هر رشته اضافه کنید:
#include <sched.h> #include <sys/types.h> #include <unistd.h> pid_t my_pid; // PID of the process containing your thread. // Assumes that cpu0, cpu1, cpu2, and cpu3 are the "slow CPUs". cpu_set_t my_cpu_set; CPU_ZERO(&my_cpu_set); CPU_SET(0, &my_cpu_set); CPU_SET(1, &my_cpu_set); CPU_SET(2, &my_cpu_set); CPU_SET(3, &my_cpu_set); sched_setaffinity(my_pid, sizeof(cpu_set_t), &my_cpu_set);
تنش حرارتی
وقتی دستگاهها بیشازحد گرم میشوند، ممکن است واحد پردازش مرکزی و/یا واحد پردازش گرافیکی را محدود کنند و این میتواند به روشهای غیرمنتظرهای بر بازیها تأثیر بگذارد. بازیهایی که از گرافیک پیچیده، محاسبات سنگین، یا فعالیت شبکه پایدار استفاده میکنند، بیشتر احتمال دارد با مشکل مواجه شوند.
از «میانای برنامه کاربردی حرارتی» برای نظارت بر تغییرات دمای دستگاه و اقدام برای حفظ مصرف انرژی کمتر و دمای خنکتر دستگاه استفاده کنید. وقتی دستگاه استرس حرارتی را گزارش میکند، از فعالیتهای جاری عقبنشینی کنید تا مصرف برق کاهش یابد. برای مثال، نرخ فریم یا شطرنجی چندضلعی را کاهش دهید.
ابتدا، شیء PowerManager را تعریف کنید و آن را در روش onCreate() مقداردهی اولیه کنید. شنودگر وضعیت حرارتی را به شیء اضافه کنید.
کاتلین
class MainActivity : AppCompatActivity() { lateinit var powerManager: PowerManager override fun onCreate(savedInstanceState: Bundle?) { powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager powerManager.addThermalStatusListener(thermalListener) } }
جاوا
public class MainActivity extends AppCompatActivity { PowerManager powerManager; @Override protected void onCreate(Bundle savedInstanceState) { ... powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); powerManager.addThermalStatusListener(thermalListener); } }
کنشهایی را که باید وقتی شنونده تغییر وضعیت را تشخیص میدهد انجام شود تعریف کنید. اگر بازیتان از C/C++ استفاده میکند، به سطوح وضعیت حرارتی در
onThermalStatusChanged() کد اضافه کنید تا بااستفاده از JNI به کد بازی بومیتان فراخوانی کنید
یا از Thermal API بومی استفاده کنید.
کاتلین
val thermalListener = object : PowerManager.OnThermalStatusChangedListener() { override fun onThermalStatusChanged(status: Int) { when (status) { PowerManager.THERMAL_STATUS_NONE -> { // No thermal status, so no action necessary } PowerManager.THERMAL_STATUS_LIGHT -> { // Add code to handle light thermal increase } PowerManager.THERMAL_STATUS_MODERATE -> { // Add code to handle moderate thermal increase } PowerManager.THERMAL_STATUS_SEVERE -> { // Add code to handle severe thermal increase } PowerManager.THERMAL_STATUS_CRITICAL -> { // Add code to handle critical thermal increase } PowerManager.THERMAL_STATUS_EMERGENCY -> { // Add code to handle emergency thermal increase } PowerManager.THERMAL_STATUS_SHUTDOWN -> { // Add code to handle immediate shutdown } } } }
جاوا
PowerManager.OnThermalStatusChangedListener thermalListener = new PowerManager.OnThermalStatusChangedListener () { @Override public void onThermalStatusChanged(int status) { switch (status) { case PowerManager.THERMAL_STATUS_NONE: // No thermal status, so no action necessary break; case PowerManager.THERMAL_STATUS_LIGHT: // Add code to handle light thermal increase break; case PowerManager.THERMAL_STATUS_MODERATE: // Add code to handle moderate thermal increase break; case PowerManager.THERMAL_STATUS_SEVERE: // Add code to handle severe thermal increase break; case PowerManager.THERMAL_STATUS_CRITICAL: // Add code to handle critical thermal increase break; case PowerManager.THERMAL_STATUS_EMERGENCY: // Add code to handle emergency thermal increase break; case PowerManager.THERMAL_STATUS_SHUTDOWN: // Add code to handle immediate shutdown break; } } };
تأخیر لمس تا نمایش
بازیهایی که فریمها را در سریعترین زمان ممکن پردازش میکنند، سناریویی با محدودیت GPU ایجاد میکنند که در آن بافر فریم بیشازحد پر میشود. «واحد پردازش مرکزی» باید منتظر «واحد پردازش گرافیکی» بماند، که باعث تأخیر قابلتوجهی بین ورودی بازیکن و تأثیر ورودی روی صفحه میشود.
برای تعیین اینکه آیا میتوانید گام فریم بازیتان را بهبود دهید، مراحل زیر را تکمیل کنید:
- گزارش Systrace تولید کنید که شامل دستههای
gfxوinputباشد. این دستهها شامل اندازهگیریهای بسیار مفیدی برای تعیین تأخیر لمس تا نمایش هستند. بخش
SurfaceViewگزارش Systrace را بررسی کنید. بافر بیشازحد پرشده باعث میشود تعداد طراحیهای بافر معلقه بین ۱ و ۲ نوسان کند، همانطور که در شکل ۵ نشان داده شده است:شکل ۵. گزارش Systrace که نشان میدهد بافر بیشازحد پر است و بهصورت دورهای برای پذیرفتن فرمانهای طراحی خیلی پر است
برای کاهش این ناهمگونی در گام قاب، اقدامات شرحدادهشده در بخشهای زیر را تکمیل کنید:
ادغام کردن Android Frame Pacing API در بازی
Android Frame Pacing API به شما کمک میکند جایگزینی قابها را انجام دهید و فاصله جایگزینی را بهگونهای تعریف کنید که بازی شما نرخ فریم یکنواختتری داشته باشد.
وضوح داراییهای غیررابط کاربری بازیتان را کاهش دهید
نمایشگرهای دستگاههای همراه مدرن پیکسلهای بسیار بیشتری نسبتبه آنچه پخشکننده میتواند پردازش کند دارند، بنابراین نمونهگیری پایین بهگونهای که یک رشته ۵ یا حتی ۱۰ پیکسلی همگی یک رنگ داشته باشند مشکلی ندارد. با توجه به ساختار اکثر حافظههای نهان نمایشگر، بهتر است وضوح را فقط در یک بُعد کاهش دهید.
بااینحال، وضوح عناصر رابط کاربری بازی خود را کاهش ندهید. حفظ ضخامت خط در این عناصر برای حفظ اندازه هدف لمسی بهاندازه کافی بزرگ برای همه بازیکنان شما مهم است.
روانی پرداز زدن
وقتی SurfaceFlinger به بافر نمایشگر متصل میشود تا صحنهای را در بازی شما نشان دهد، فعالیت CPU بهطور لحظهای افزایش مییابد. اگر این جهشهای فعالیت CPU بهصورت ناهموار رخ دهند، ممکن است در بازی خود شاهد لکنت باشید. نمودار در «شکل ۶» دلیل وقوع این اتفاق را نشان میدهد:
اگر قاب خیلی دیر شروع به طراحی کند، حتی چند میلیثانیه دیرتر، ممکن است پنجره نمایش بعدی را ازدست بدهد. سپس قاب باید تا «همگامسازی عمودی» بعدی منتظر بماند تا نمایش داده شود (۳۳ میلیثانیه هنگام اجرای بازی با ۳۰ قاب در ثانیه)، که باعث تأخیر قابلتوجهی از دیدگاه بازیکن میشود.
برای رسیدگی به این وضعیت، از Android Frame Pacing API استفاده کنید که همیشه قاب جدیدی را در موج VSync ارائه میدهد.
وضعیت حافظه
وقتی بازیتان را برای مدت طولانی اجرا میکنید، ممکن است دستگاه با خطاهای کمبود حافظه مواجه شود.
در این وضعیت، فعالیت CPU را در گزارش Systrace بررسی کنید و ببینید سیستم هر چند وقت یکبار با
کارگزار kswapd تماس میگیرد. اگر درطول اجرای بازیتان تماسهای زیادی وجود دارد، بهتر است نگاه دقیقتری به نحوه مدیریت و پاکسازی حافظه در بازیتان بیندازید.
برای اطلاعات بیشتر، درباره مدیریت حافظه را ببینید.
وضعیت رشته
هنگام پیمایش در عناصر معمول گزارش Systrace، میتوانید مقدار زمانی را که یک رشته معین در هر وضعیت رشته ممکن صرف کرده است با انتخاب رشته در گزارش مشاهده کنید، همانطور که در «شکل ۷» نشان داده شده است:
همانطور که «شکل ۷» نشان میدهد، ممکن است متوجه شوید که رشتههای بازی شما بهاندازه کافی در وضعیت «درحال اجرا» یا «اجرایی» نیستند. فهرست زیر چند دلیل رایج را نشان میدهد که چرا یک رشته معین ممکن است بهطور دورهای به حالت غیرعادی تغییر کند:
- اگر رشتهای برای مدت طولانی در حالت خواب باشد، ممکن است دچار رقابت قفل یا انتظار برای فعالیت واحد پردازش گرافیکی شده باشد.
- اگر رشتهای بهطور مداوم در ورودی/خروجی مسدود شود، یا در هر زمان دادههای زیادی از دیسک میخوانید یا بازیتان درحال تلاطم است.
منابع بیشتر
برای کسب اطلاعات بیشتر درباره بهبود عملکرد بازی، منابع تکمیلی زیر را ببینید:
ویدیوها
- ارائه Systrace ویژه بازیها از «نشست توسعهدهندگان بازی Android» در سال ۲۰۱۸