میتوانید ردیابی سیستم را برای ضبط یک CPU و نمایه رشته برنامه خود در مدت زمان کوتاهی پیکربندی کنید. سپس می توانید از گزارش خروجی از یک ردیابی سیستم برای بهبود عملکرد بازی خود استفاده کنید.
یک ردیابی سیستم مبتنی بر بازی را تنظیم کنید
ابزار Systrace به دو صورت در دسترس است:
Systrace یک ابزار سطح پایین است که:
- حقیقت زمینه را فراهم می کند . Systrace خروجی را مستقیماً از هسته دریافت می کند، بنابراین معیارهایی که می گیرد تقریباً با معیارهایی که یک سری فراخوانی سیستم گزارش می کنند یکسان است.
- منابع کمی را مصرف می کند . Systrace سربار بسیار کم را روی دستگاه وارد می کند، معمولاً کمتر از 1٪، زیرا داده ها را به یک بافر درون حافظه منتقل می کند.
تنظیمات بهینه
مهم است که به ابزار مجموعه ای معقول از آرگومان ها بدهید:
- دستهها: بهترین مجموعه دستهبندیها برای فعال کردن ردیابی سیستم مبتنی بر بازی عبارتند از: {
sched
,freq
,idle
,am
,wm
,gfx
,view
,sync
,binder_driver
,hal
,dalvik
}. اندازه بافر: یک قانون کلی این است که اندازه بافر 10 مگابایتی در هر هسته CPU اجازه می دهد تا ردیابی حدود 20 ثانیه طول بکشد. به عنوان مثال، اگر دستگاهی دارای دو CPU چهار هسته ای (در مجموع 8 هسته) باشد، مقدار مناسب برای ارسال به برنامه
systrace
80000 کیلوبایت (80 مگابایت) است.اگر بازی شما تغییرات زیادی در زمینه انجام می دهد، بافر را به 15 مگابایت در هر هسته 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 در دستگاهی استفاده میکنید، مراحل زیر را برای ثبت ردیابی سیستمی که بهترین روشها را برای مجموعه دستهها، اندازه بافر و رویدادهای سفارشی اعمال میکند، تکمیل کنید:
گزینه Trace debuggable applications را فعال کنید.
برای استفاده از این تنظیم، دستگاه باید 256 مگابایت یا 512 مگابایت در دسترس باشد (بسته به اینکه CPU دارای 4 یا 8 هسته است)، و هر قطعه حافظه 64 مگابایتی باید به صورت یک قطعه پیوسته در دسترس باشد.
دسته بندی ها را انتخاب کنید، سپس دسته ها را در لیست زیر فعال کنید:
-
am
: مدیر فعالیت -
binder_driver
: درایور هسته Binder -
dalvik
: دالویک وی ام -
freq
: فرکانس CPU -
gfx
: گرافیک -
hal
: ماژول های سخت افزار -
idle
: CPU بیکار -
sched
: برنامه ریزی CPU -
sync
: همگام سازی -
view
: View System -
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# شما فراهم می کند.
- بازی خود را چند رشته ای کنید. برای جزئیات بیشتر، بخش سازگاری نرخ فریم را ببینید.
سازگاری قاب
یکی از مهم ترین عناصر تجربه گیم پلی، دستیابی به نرخ فریم ثابت است. برای سهولت در دستیابی به این هدف، تکنیک های بهینه سازی مورد بحث در این بخش را دنبال کنید.
چند رشته ای
هنگام توسعه برای چندین پلتفرم، طبیعی است که تمام فعالیت های بازی خود را در یک رشته قرار دهید. اگرچه اجرای این روش در بسیاری از موتورهای بازی ساده است، اما هنگام اجرا بر روی دستگاههای اندرویدی بسیار مطلوب نیست. در نتیجه، بازی های تک رشته ای اغلب به کندی بارگذاری می شوند و نرخ فریم ثابتی ندارند.
Systrace نشان داده شده در شکل 1 رفتاری را نشان می دهد که نمونه ای از یک بازی است که در یک زمان فقط روی یک CPU اجرا می شود:
برای بهبود عملکرد بازی خود، بازی خود را چند رشته ای کنید . به طور معمول، بهترین مدل داشتن 2 رشته است:
- یک رشته بازی که شامل ماژول های اصلی بازی شما است و دستورات رندر را ارسال می کند.
- یک رشته رندر ، که دستورات رندر را دریافت می کند و آنها را به دستورات گرافیکی ترجمه می کند که GPU دستگاه می تواند از آن برای نمایش یک صحنه استفاده کند.
Vulkan API این مدل را با توجه به توانایی آن برای فشار دادن 2 بافر رایج به صورت موازی گسترش می دهد. با استفاده از این ویژگی، میتوانید رشتههای رندر متعدد را در چندین CPU توزیع کنید و زمان رندر صحنه را بیشتر بهبود ببخشید.
همچنین میتوانید برخی تغییرات خاص موتور را برای بهبود عملکرد چند رشتهای بازی خود ایجاد کنید:
- اگر بازی خود را با استفاده از موتور بازی Unity توسعه میدهید، گزینههای Multithreaded Rendering و GPU Skinning را فعال کنید.
- اگر از موتور رندر سفارشی استفاده می کنید، مطمئن شوید که خط لوله فرمان رندر و خط لوله فرمان گرافیکی به درستی تراز شده باشند. در غیر این صورت می توانید تاخیر در نمایش صحنه های بازی خود ایجاد کنید.
پس از اعمال این تغییرات، همانطور که در شکل 2 نشان داده شده است، باید ببینید که بازی شما حداقل 2 CPU را به طور همزمان اشغال می کند:
در حال بارگیری عنصر UI
هنگام ایجاد یک بازی پر ویژگی، وسوسه انگیز است که گزینه ها و اقدامات مختلف را به طور همزمان به بازیکن نشان دهید. با این حال، برای حفظ یک نرخ فریم ثابت، مهم است که اندازه نسبتا کوچک نمایشگرهای موبایل را در نظر بگیرید و رابط کاربری خود را تا حد امکان ساده نگه دارید.
گزارش Systrace که در شکل 3 نشان داده شده است نمونه ای از یک فریم رابط کاربری است که سعی دارد عناصر زیادی را نسبت به قابلیت های یک دستگاه تلفن همراه ارائه دهد.
یک هدف خوب این است که زمان به روز رسانی UI را به 2-3 میلی ثانیه کاهش دهید . با انجام بهینه سازی هایی مشابه موارد زیر می توانید به چنین به روز رسانی های سریعی دست یابید:
- فقط عناصر روی صفحه را که جابجا شده اند به روز کنید.
- تعداد بافتها و لایههای رابط کاربری را محدود کنید. ترکیب تماسهای گرافیکی مانند سایهزنها و بافتها که از مواد مشابهی استفاده میکنند را در نظر بگیرید.
- عملیات انیمیشن عنصر را به GPU موکول کنید.
- از بین بردن فروستوم و انسداد تهاجمی تر را انجام دهید.
- در صورت امکان، عملیات ترسیم را با استفاده از Vulkan API انجام دهید. سربار تماس قرعه کشی در Vulkan کمتر است.
مصرف برق
حتی پس از انجام بهینهسازیهای مورد بحث در بخش قبل، ممکن است متوجه شوید که نرخ فریم بازی شما در 45-50 دقیقه اول گیمپلی بدتر میشود. علاوه بر این، دستگاه ممکن است با گذشت زمان شروع به گرم شدن کند و باتری بیشتری مصرف کند.
در بسیاری از موارد، این مجموعه نامطلوب حرارتی و مصرف انرژی به نحوه توزیع حجم کاری بازی شما در CPU دستگاه مربوط می شود. برای افزایش راندمان مصرف انرژی بازی خود، بهترین شیوه های نشان داده شده در بخش های زیر را اعمال کنید.
موضوعات پر حافظه را روی یک CPU نگه دارید
در بسیاری از دستگاه های تلفن همراه، کش های L1 بر روی CPU های خاصی قرار دارند و کش های L2 در مجموعه ای از CPU هایی که یک ساعت مشترک دارند، قرار دارند. برای به حداکثر رساندن بازدیدهای حافظه نهان L1، عموماً بهتر است که رشته اصلی بازی خود را به همراه هر رشته ای که حافظه سنگینی دارد، روی یک CPU اجرا کنید.
کار کوتاه مدت را به CPU های کم مصرف موکول کنید
اکثر موتورهای بازی، از جمله Unity، میدانند که باید عملیات تایپ کارگر را به یک CPU متفاوت نسبت به رشته اصلی بازیتان موکول کنند. با این حال، موتور از معماری خاص دستگاه آگاه نیست و نمی تواند حجم کاری بازی شما را به خوبی پیش بینی کند.
اکثر دستگاههای سیستم روی یک تراشه حداقل ۲ ساعت مشترک دارند، یکی برای CPUهای سریع دستگاه و دیگری برای CPUهای کند دستگاه. نتیجه این معماری این است که اگر یک CPU سریع نیاز به کار با حداکثر سرعت داشته باشد، سایر CPUهای سریع نیز با حداکثر سرعت کار می کنند.
گزارش مثال نشان داده شده در شکل 4 یک بازی را نشان می دهد که از پردازنده های سریع بهره می برد. با این حال، این سطح فعالیت بالا مقدار زیادی انرژی و گرما را به سرعت تولید می کند.
برای کاهش مصرف کلی برق، بهتر است به زمانبندیکننده پیشنهاد کنید که کارهای کوتاهتر - مانند بارگیری صدا، اجرای تاپیکهای کارگر و اجرای طراح رقص - به مجموعه پردازندههای کند روی یک دستگاه موکول شود. تا جایی که می توانید این کار را به پردازنده های کند انتقال دهید و در عین حال نرخ فریم دلخواه خود را حفظ کنید.
اکثر دستگاه ها CPU های کند را قبل از CPU های سریع فهرست می کنند، اما نمی توانید فرض کنید که SOC دستگاه شما از این ترتیب استفاده می کند. برای بررسی، دستورات مشابه آنچه در این کد کشف توپولوژی 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);
استرس حرارتی
وقتی دستگاهها خیلی گرم میشوند، ممکن است CPU و/یا GPU را تحت تأثیر قرار دهند و این میتواند بازیها را بهصورت غیرمنتظرهای تحت تأثیر قرار دهد. بازی هایی که گرافیک پیچیده، محاسبات سنگین یا فعالیت پایدار شبکه را در خود جای داده اند، بیشتر با مشکلاتی مواجه می شوند.
از API حرارتی برای نظارت بر تغییرات دما در دستگاه استفاده کنید و برای حفظ مصرف انرژی کمتر و دمای دستگاه خنکتر اقدام کنید. هنگامی که دستگاه تنش حرارتی را گزارش میکند، برای کاهش مصرف برق از فعالیتهای مداوم خودداری کنید. برای مثال، نرخ فریم یا چند ضلعی را کاهش دهید.
ابتدا شی 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 کد بازی اصلی خود را فراخوانی کنید یا از API اصلی Thermal استفاده کنید.
کاتلین
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 ایجاد میکنند که در آن بافر فریم بیش از حد پر میشود. CPU باید منتظر GPU باشد، که باعث تأخیر قابل توجهی بین ورودی پخش کننده و تأثیر ورودی روی صفحه می شود.
برای تعیین اینکه آیا میتوانید سرعت فریم بازی خود را بهبود ببخشید، مراحل زیر را انجام دهید:
- یک گزارش Systrace ایجاد کنید که شامل دستههای
gfx
وinput
است. این دستهها شامل اندازهگیریهای مفیدی برای تعیین تأخیر لمس به صفحه نمایش هستند. بخش
SurfaceView
یک گزارش Systrace را بررسی کنید. همانطور که در شکل 5 نشان داده شده است، یک بافر بیش از حد باعث می شود که تعداد کشش های بافر معلق بین 1 و 2 در نوسان باشد:شکل 5. گزارش Systrace نشان می دهد که یک بافر بیش از حد پر است که به طور دوره ای آنقدر پر است که دستورات ترسیم را نمی پذیرد.
برای کاهش این ناهماهنگی در ریتم فریم، اقدامات شرح داده شده در بخش های زیر را کامل کنید:
Android Frame Pacing API را در بازی خود ادغام کنید
Android Frame Pacing API به شما کمک میکند تا فریمها را تعویض کنید و فاصلهای برای تعویض تعریف کنید تا بازی شما نرخ فریم ثابتتری داشته باشد.
وضوح دارایی های غیر UI بازی خود را کاهش دهید
نمایشگرهای دستگاههای تلفن همراه مدرن حاوی پیکسلهای بسیار بیشتری نسبت به پخشکنندهها هستند، بنابراین کمنمونهسازی بهگونهای که یک اجرا با 5 یا حتی 10 پیکسل همه شامل یک رنگ باشد، مشکلی ندارد. با توجه به ساختار اکثر کش های نمایشگر، بهتر است وضوح را تنها در یک بعد کاهش دهید .
با این حال، وضوح عناصر رابط کاربری بازی خود را کاهش ندهید. حفظ ضخامت خط روی این عناصر برای حفظ اندازه هدف لمسی کافی برای همه بازیکنانتان مهم است.
رندر صافی
هنگامی که SurfaceFlinger روی یک بافر نمایشگر میچسبد تا صحنهای را در بازی شما نشان دهد، فعالیت CPU به طور لحظهای افزایش مییابد. اگر این جهش ها در فعالیت CPU به طور ناهموار رخ دهد، ممکن است لکنت را در بازی خود مشاهده کنید. نمودار در شکل 6 دلیل این اتفاق را نشان می دهد:
اگر یک فریم خیلی دیر شروع به ترسیم کند، حتی با چند میلی ثانیه، ممکن است پنجره نمایش بعدی را از دست بدهد. سپس فریم باید منتظر باشد تا Vsync بعدی نمایش داده شود (33 میلی ثانیه هنگام اجرای بازی با سرعت 30 فریم در ثانیه)، که باعث تاخیر قابل توجهی از دید بازیکن می شود.
برای رسیدگی به این وضعیت، از Android Frame Pacing API استفاده کنید، که همیشه یک فریم جدید در یک جبهه موج VSync ارائه میکند.
وضعیت حافظه
هنگامی که بازی خود را برای مدت طولانی اجرا می کنید، ممکن است دستگاه با خطاهای کمبود حافظه مواجه شود.
در این شرایط، فعالیت CPU را در یک گزارش Systrace بررسی کنید و ببینید که سیستم هر چند وقت یکبار با دیمون kswapd
تماس می گیرد. اگر در طول اجرای بازی شما تماس های زیادی وجود دارد، بهتر است نگاه دقیق تری به نحوه مدیریت و پاکسازی حافظه بازی خود بیندازید.
برای اطلاعات بیشتر، به مدیریت موثر حافظه در بازی ها مراجعه کنید.
وضعیت نخ
هنگامی که در میان عناصر معمولی یک گزارش Systrace پیمایش می کنید، می توانید با انتخاب رشته در گزارش، مقدار زمانی را که یک رشته معین در هر وضعیت رشته ممکن صرف کرده است، مشاهده کنید، همانطور که در شکل 7 نشان داده شده است:
همانطور که شکل 7 نشان می دهد، ممکن است متوجه شوید که رشته های بازی شما آنطور که باید در حالت "اجرا" یا "قابل اجرا" نیستند. فهرست زیر چندین دلیل متداول را نشان میدهد که چرا یک رشته معین ممکن است به طور دورهای به حالت غیرعادی تغییر کند:
- اگر یک رشته برای مدت زمان طولانی در حالت خواب باشد، ممکن است از کشمکش قفل یا منتظر فعالیت GPU رنج ببرد.
- اگر یک رشته به طور مداوم در ورودی/خروجی مسدود می شود، شما یا داده های زیادی را در یک زمان از دیسک می خوانید یا بازی شما در حال شکست است.
منابع اضافی
برای کسب اطلاعات بیشتر در مورد بهبود عملکرد بازی خود، به منابع اضافی زیر مراجعه کنید:
ویدیوها
- ارائه Systrace for Games از Android Game Developer Summit 2018