نکات بهینه‌سازی CPU و GPU

این سند به شما نشان می‌دهد که چگونه با استفاده از ابزارهایی برای شناسایی و رفع گلوگاه‌های CPU و GPU، عملکرد بازی را بهینه کنید.

بهینه‌سازی پردازنده

اگر تجزیه و تحلیل نشان دهد که بازی به CPU وابسته است، بررسی بیشتر ضروری است. این امر مستلزم شناسایی رشته‌ها یا APIهای خاصی است که باعث ایجاد گلوگاه و کاهش FPS می‌شوند.

برای بهینه‌سازی پردازنده، یک راه‌حل جهانی معمولاً مؤثر نیست. در عوض، شما باید بر اساس بازی یا صحنه، سنگین‌ترین حجم کار را شناسایی کنید و سپس منطق و عملکردهای مربوطه را بهینه کنید.

ابزارهای ردیابی زمان موتور بازی

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

بینش‌های غیرواقعی

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

به عنوان مثال، GameThread معمولاً بیشترین سهم از زمان CPU را به خود اختصاص می‌دهد، که عمدتاً به زمان تیک (Tick Time) مربوط می‌شود. علاوه بر این، بخش قابل توجهی از زمان تیک توسط وظایف مرتبط با FActorComponentTickFunction مصرف می‌شود.

برای بهینه‌سازی FActorComponentTick ، ضروری است که محاسبات را حذف کرده و حذف کاراکترها و اشیاء واقع در خارج از میدان دید دوربین را پیاده‌سازی کنیم. علاوه بر این، استفاده از انیمیشن‌های مبتنی بر LOD (سطح جزئیات) می‌تواند بهبودهای بیشتری در عملکرد ایجاد کند.

جدول زمانی ردیابی Unreal Insight که زمان اجرای GameThread، RenderThread و RHIThread را نشان می‌دهد
ردیابی بینش غیرواقعی با GameThread، RenderThread و RHIThread (برای بزرگنمایی کلیک کنید).

پروفایلر یونیتی (یونیتی)

تجزیه و تحلیل با استفاده از Unity Profiler نشان می‌دهد که Thread اصلی بیش از ۴۵ میلی‌ثانیه زمان مصرف می‌کند، و PostLateUpdate.FinishFrameRendering با اشغال ۱۶.۲۳ میلی‌ثانیه، آن را به زمان‌برترین عملیات تبدیل می‌کند. در این میان، چندین فراخوانی Inl_RenderCameraStack مشاهده می‌شود. توصیه می‌شود که ضرورت فعال بودن دوربین‌ها مشخص شده و بر اساس آن بهینه‌سازی شوند.

جدول زمانی Unity Profiler که نشان می‌دهد Thread اصلی منتظر Gfx.WaitForPresentOnGfxThread است
مثال محدود شده با GPU برای Unity Profiler (برای بزرگنمایی کلیک کنید).

ابزارهای پروفایلینگ در سطح سیستم

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

پرفتو

با استفاده از Perfetto trace، می‌توانید تخصیص هسته‌های CPU و جزئیات اجرای هر رشته را در یک دستگاه مبتنی بر اندروید تعیین کنید. این به شما امکان می‌دهد با تجزیه و تحلیل داده‌های اجرای رشته، تنگناهای عملکرد را شناسایی کنید.

کیس بالای سر CPU

این ردیابی نشان می‌دهد که حجم کار روی GameThread و RenderThread باعث ایجاد تأخیر در صف RHI Thread می‌شود و بر اساس VSync منجر به سناریوی محدود به CPU می‌شود.

ردیابی کامل که زمان اجرای GameThread، RenderThread و RHIThread را نشان می‌دهد
ردیابی‌های بی‌نظیر با جزئیات اجرای CPU (برای بزرگنمایی کلیک کنید).

کیس بالای سر پردازنده گرافیکی

این ردیابی نشان می‌دهد که تکمیل خود GPU از 25 میلی‌ثانیه فراتر رفته است، که نشان‌دهنده‌ی سناریوی محدود به GPU است.

ردیابی بی‌نقص که بلوک تکمیل GPU را در انتظار تکمیل GPU نشان می‌دهد
ردیابی‌های بی‌نظیر به همراه جزئیات سربار GPU (برای بزرگنمایی کلیک کنید).

سیمپل‌پرف

برای شناسایی توابعی که بیشترین میزان استفاده از CPU را دارند، می‌توان از simpleperf استفاده کرد. برای دستیابی به نتایج بهینه، توصیه می‌شود این توابع را اولویت‌بندی کرده و ابتدا به توابعی که بیشترین استفاده را دارند، رسیدگی کنید.

خروجی Simpleperf توابعی را نمایش می‌دهد که بیشترین میزان استفاده از CPU را دارند
پروفایل‌بندی CPU در Simpleperf: تحلیل سلسله مراتب فراخوانی تابع و میزان استفاده از منابع (برای بزرگنمایی کلیک کنید).

Simpleperf به شما کمک می‌کند تا داده‌های مربوط به توابعی را که بیشترین زمان CPU را استفاده می‌کنند، بررسی کنید. برای بهینه‌سازی استفاده از CPU، با توابعی که بیشترین CPU را استفاده می‌کنند شروع کنید. در این مثال، USkeletalMeshComponent که با انیمیشن در ActorComponentTickFunctions مرتبط است، بیشترین CPU را استفاده می‌کند.

بهینه‌سازی پردازنده گرافیکی

اگر تجزیه و تحلیل نشان دهد که بازی به GPU وابسته است، بررسی بیشتر ضروری است. این امر مستلزم استفاده از ابزارها و تکنیک‌های مختلف برای بهینه‌سازی و تجزیه و تحلیل GPU است.

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

بخش‌های بعدی روش‌ها و ابزارهای بهینه‌سازی پردازنده گرافیکی (GPU) را توضیح می‌دهند.

حذف RenderPass های غیرضروری

برای بهبود عملکرد رندر و کاهش بار کاری GPU، رندرهای غیرضروری را حذف کنید. این شامل هر رندری می‌شود که فاقد فراخوانی‌های ترسیم است یا خروجی آن در فریم نهایی استفاده نمی‌شود.

از یک اشکال‌زدای GPU، مانند RenderDoc ، برای تجزیه و تحلیل خط لوله رندر و شناسایی فرصت‌های بهینه‌سازی استفاده کنید.

  1. بدون فراخوانی‌های ترسیمی (Draw Calls): بررسی کنید که آیا مسیر رندر شامل فراخوانی‌های ترسیمی (Draw Calls) است یا خیر. اگر هیچ فراخوانی ترسیمی ندارد، مسیر را حذف کنید.

  2. خروجی‌های استفاده نشده: بررسی کنید که آیا مراحل بعدی به خروجی‌های مرحله رندر، مثلاً رنگ یا عمق، دسترسی دارند یا آنها را نمایش می‌دهند یا خیر. اگر این کار را نمی‌کنند، مرحله را حذف کنید.

  3. گذرگاه‌های قابل ادغام: گذرگاه‌هایی را که می‌توانید ادغام کنید، شناسایی کنید:

    • همان فریم بافر یا پیوست‌ها
    • عملیات بارگیری یا ذخیره سازی سازگار
    • هیچ مانع وابستگی بین آنها وجود ندارد
مرورگر رویداد RenderDoc که رندرهای Vulkan و فراخوانی‌های رسم را نمایش می‌دهد
توالی دستورات RenderPass و GPU در RenderDoc (برای بزرگنمایی کلیک کنید).

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

عملیات بارگذاری یا ذخیره به دلیل استفاده زیاد از حافظه، منابع زیادی را مصرف می‌کنند. عملیات بارگذاری-ذخیره غیرضروری را به حداقل برسانید. این اقدامات را فقط زمانی انجام دهید که پیوست‌ها در RenderPass مورد نیاز باشند. در غیر این صورت، آنها را با عملیات‌های Clear یا Don't care جایگزین کنید تا سربار کاهش یابد.

چگونه بهینه سازی کنیم

از یک اشکال‌زدای GPU، مانند RenderDoc ، برای تجزیه و تحلیل خط لوله رندر و شناسایی فرصت‌های بهینه‌سازی زیر استفاده کنید:

  1. بارگذاری: اگر یک پیوست مرحله رندر از داده‌های مرحله یا پیوست قبلی استفاده نکند، عملیات بارگذاری غیرضروری است. در چنین مواردی، استفاده از Don't care یا Clear می‌تواند سربار را کاهش دهد.

  2. ذخیره: اگر پیوست مرحله رندر پس از مرحله رندر فعلی استفاده نشود، عملیات ذخیره غیرضروری است. در چنین مواردی، از Don't care یا Clear استفاده کنید.

  3. جایگزینی: تعیین کنید که آیا تنظیمات فعلی بارگذاری یا ذخیره‌سازی را می‌توان با Clear یا Don't Care بدون تأثیر بر فریم نهایی جایگزین کرد یا خیر.

مرورگر رویداد RenderDoc و بازرس منابع، طرح‌بندی تصویر و رندرهای مرحله‌ای را تجزیه و تحلیل می‌کنند.
تحلیل خط لوله رندر RenderDoc (برای بزرگنمایی کلیک کنید).

برای فعال کردن Early-Z از دور انداختن خودداری کنید

Early-Z عملکرد را در پلتفرم‌های موبایل بهبود می‌بخشد. با این حال، یک دستورالعمل discard در یک سایه‌زن به طور خودکار Early-Z را غیرفعال می‌کند. اگر دستورالعمل discard ضروری نیست، آن را حذف کنید.

شتاب اولیه-Z

این بهینه‌سازی به طور قابل توجهی عملیات سایه‌زن قطعه‌ای را کاهش داده و عملکرد پردازنده گرافیکی (GPU) را بهبود می‌بخشد.

آزمایش عمق و شابلون Early-Z

جدول مقایسه معیارهای عملکرد CPU و GPU هنگام فعال بودن در مقابل غیرفعال بودن Early-Z
تأثیر عملکرد شتاب Early-Z (برای بزرگنمایی کلیک کنید).

چگونه بهینه سازی کنیم

از یک اشکال‌زدای GPU، مانند RenderDoc ، برای تجزیه و تحلیل خط لوله رندر و شناسایی فرصت‌های بهینه‌سازی زیر استفاده کنید:

  1. استفاده از discard در سایه‌زن‌های قطعه: کلمه کلیدی discard مانع از انجام تست‌های عمق اولیه توسط GPU می‌شود زیرا میزان دید قطعه از قبل مشخص نیست.

  2. اصلاح gl_FragDepth : اصلاح پویای gl_FragDepth عمق یک قطعه را تغییر می‌دهد، که بهینه‌سازی Early-Z را غیرفعال می‌کند زیرا عمق نهایی قبل از پردازش قطعه ناشناخته است.

  3. فعال بودن پوشش آلفا به پوشش: وقتی پوشش آلفا به پوشش فعال باشد (که اغلب در رندر MSAA استفاده می‌شود)، پوشش قطعه به مقادیر آلفا بستگی دارد. این می‌تواند تست عمق را به تأخیر بیندازد و Early-Z را غیرفعال کند.

مقایسه قطعات به ازای هر پیکسل با و بدون کلمه کلیدی discard shader
اشکال‌زدای پردازنده گرافیکی RenderDoc برای تجزیه و تحلیل (برای بزرگنمایی کلیک کنید).

بهینه سازی فرمت بافت

انتخاب بهینه‌ی قالب بافت، مصرف حافظه را کاهش می‌دهد، بهره‌وری پهنای باند را افزایش می‌دهد و عملکرد رندر را بهبود می‌بخشد. استفاده از قالب‌های با دقت بسیار بالا می‌تواند منابع پردازنده‌ی گرافیکی (GPU) را بدون ارائه مزایای بصری هدر دهد.

چگونه بهینه سازی کنیم

از یک اشکال‌زدای GPU، مانند RenderDoc ، برای تجزیه و تحلیل خط لوله رندر و شناسایی فرصت‌های بهینه‌سازی زیر استفاده کنید:

  1. برای بافرهای depth-stencil از D24S8 به جای D32S8 استفاده کنید: استفاده از D24S8 برای بافرهای depth-stencil در مقایسه با D32S8 ، مصرف حافظه را 20٪ کاهش می‌دهد، بدون اینکه تفاوت قابل توجهی در کیفیت بصری در اکثر برنامه‌ها وجود داشته باشد یا بسیار کم باشد.
  2. استفاده از فشرده‌سازی ASTC برای بافت‌های رنگی: فشرده‌سازی ASTC به طور قابل توجهی استفاده از حافظه بافت را کاهش می‌دهد - تا 8 برابر در مقایسه با فرمت‌های فشرده نشده - در حالی که کیفیت بصری بالا را حفظ می‌کند.
  3. از فرمت‌های نیم‌شناور به جای تمام‌شناور استفاده کنید: از R16F یا RG16F برای کاهش پهنای باند حافظه و مصرف فضای ذخیره‌سازی استفاده کنید. این فرمت‌ها برای بافرهای پس‌پردازش بسیار مناسب هستند.

بهینه‌سازی پیچیدگی هندسه

به حداقل رساندن پیچیدگی هندسی، عملکرد رندرینگ را بهبود می‌بخشد، به خصوص در دستگاه‌های تلفن همراه با قابلیت‌های محدود GPU. این شامل استفاده از تعداد کمتری از رئوس و مثلث‌ها، ادغام اشیاء برای کاهش فراخوانی‌های ترسیم و حذف هندسه رندر نشده یا غیرضروری است. تکنیک‌هایی مانند ساده‌سازی مش، سطح جزئیات (LOD) و حذف frustum یا occlusion می‌تواند به طور قابل توجهی بار کاری GPU را کاهش داده و نرخ فریم را افزایش دهد.

چگونه بهینه سازی کنیم

از ابزارهای پروفایلینگ و اشکال‌زداهای GPU مانند RenderDoc ، Android GPU Inspector یا سایر تحلیل‌گرهای عملکرد برای شناسایی گلوگاه‌های عملکرد مرتبط با هندسه استفاده کنید.

  1. کاهش تعداد مثلث‌ها: استفاده از چندضلعی‌ها را به حداقل برسانید، به خصوص برای اشیاء کوچک یا دور.

  2. استفاده از سطح جزئیات (LOD): بر اساس فاصله دوربین، مش‌های ساده‌تر به طور خودکار استفاده می‌شوند.

  3. ادغام مش‌های کوچک: اشیاء ثابت را تجمیع کنید تا فراخوانی‌های ترسیم و سربار CPU کاهش یابد.

  4. حذف ناقص و انسداد: از رندر کردن اشیایی که خارج از دید هستند یا توسط عناصر دیگر پنهان شده‌اند، خودداری کنید.

پیوست‌های غیرضروری را حذف کنید

پیوست‌های رندر (مثلاً رنگ، عمق، شابلون) پهنای باند حافظه و منابع GPU را مصرف می‌کنند، حتی اگر استفاده نشوند. حذف پیوست‌های غیرضروری یا اضافی، عملکرد را بهبود می‌بخشد و مصرف برق را کاهش می‌دهد، به‌ویژه در پلتفرم‌های موبایل.

چگونه بهینه سازی کنیم

از ابزارهای پروفایلینگ و اشکال‌زداهای GPU مانند RenderDoc ، Android GPU Inspector یا سایر تحلیل‌گرهای عملکرد برای شناسایی گلوگاه‌های عملکرد مرتبط با هندسه استفاده کنید.

  1. میزان استفاده واقعی را بررسی کنید: آیا فراخوانی‌های ترسیمی یا سایه‌زن‌هایی وجود دارند که در حال نوشتن یا خواندن از فایل پیوست باشند؟
  2. تحلیل خروجی فریم: RenderDoc یا ابزارهای مشابه برای تعیین اینکه آیا فایل پیوست در تصویر نهایی نقش دارد یا خیر، استفاده کنید.
  3. پیوست‌های موقت یا ساختگی را در نظر بگیرید: پیوست‌های موقت یا عملیات ذخیره‌سازی «اهمیت ندهید» باید برای داده‌های موقت که نیازی به ذخیره‌سازی مداوم ندارند، استفاده شوند.

بهینه سازی دقت سایه زن

استفاده از دقت بیش از حد بالا (برای مثال، highp به جای mediump یا lowp ) در سایه‌زن‌ها، حجم کار GPU، مصرف برق و فشار بر رجیستر را افزایش می‌دهد، به خصوص در GPUهای موبایل. با استفاده از کمترین دقت کافی برای متغیرها (برای مثال، موقعیت‌ها، رنگ‌ها، UVها)، می‌توانید عملکرد را بدون تأثیر بصری قابل تشخیص بهبود بخشید.

جدول مقایسه معیارهای عملکرد CPU و GPU هنگام استفاده از دقت سایه‌زن mediump در مقابل highp
تأثیر دقت سایه‌زن بر عملکرد (برای بزرگنمایی کلیک کنید).

چگونه بهینه سازی کنیم

از ابزارهای پروفایلینگ و اشکال‌زداهای GPU مانند RenderDoc، Android GPU Inspector یا سایر تحلیل‌گرهای عملکرد برای شناسایی گلوگاه‌های عملکرد مرتبط با هندسه استفاده کنید.

  1. بررسی کد سایه‌زن: متغیرهای سایه‌زن را ارزیابی کنید و تأیید کنید که دقت بالا فقط در صورت لزوم، مانند محاسبات عمق یا فضای صفحه، استفاده می‌شود. برای رنگ‌ها، مختصات UV یا مقادیری که به دقت بالا نیاز ندارند، از دقت متوسط ​​یا کم استفاده کنید.

  2. استفاده از اشکال‌زداهای پردازنده گرافیکی (GPU debuggers): ابزارهای تشخیصی، مانند RenderDoc یا پروفایلرهای پردازنده گرافیکی موبایل (برای مثال، AGI، Mali/GPU Inspector)، استفاده از رجیستر بالا یا توقف‌های سایه‌زن مرتبط با مشکلات دقت را شناسایی می‌کنند.

نمایشگر میزان استفاده متغیر Mali که درون‌یابی ۱۶ بیتی را در کنار کد سایه‌زن با استفاده از mediump نمایش می‌دهد
نمونه‌ای از ابزارهای پروفایلینگ و اشکال‌زداهای GPU (برای بزرگنمایی کلیک کنید).

فعال کردن حذف از پشت صفحه

رندر کردن مثلث‌هایی که پشت به دوربین هستند (back-faces) اغلب برای اشیاء جامد غیرضروری است.

چگونه بهینه سازی کنیم

استفاده از VK_CULL_MODE_NONE می‌تواند تأثیر منفی بر عملکرد داشته باشد، زیرا GPU را مجبور می‌کند تا هم سطوح جلویی و هم سطوح پشتی را رندر کند، که این امر حجم کار رندر را افزایش می‌دهد.

گزارش دستورات Vulkan که vkCmdSetCullMode را روی VK_CULL_MODE_NONE تنظیم شده است را نشان می‌دهد.
اشکال‌زدایی لاگ‌ها با حذف از پشت (برای بزرگنمایی کلیک کنید).

به حداقل رساندن overdraw در صحنه‌های رابط کاربری

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

چگونه بهینه سازی کنیم

از یک اشکال‌زدای GPU، مانند RenderDoc ، برای تجزیه و تحلیل خط لوله رندر و شناسایی فرصت‌های بهینه‌سازی زیر استفاده کنید:

  1. عدم وجود overdraw اضافی را تأیید کنید. در زمینه‌های رابط کاربری، که ممکن است کل صفحه نمایش رندر شود، تأیید کنید که مراحل رندر قبلی بی‌جهت overdraw نشده باشند.
  2. برای بهینه‌سازی عملکرد، تست عمقی و حذف را فعال کنید.
  3. ترتیب رندر را از جلو به عقب در نظر بگیرید.
مرورگر رویداد RenderDoc و نمایشگر بافت، یک گذر رندر overdraw غیرضروری را شناسایی می‌کنند.
مثالی برای حذف فراخوانی‌های ترسیم و رندر اضافی (برای بزرگنمایی کلیک کنید).