Sistem izlemeyi, uygulamanızın kısa bir süre içindeki CPU ve iş parçacığı profilini yakalayacak şekilde yapılandırabilirsiniz. Ardından, oyununuzun performansını artırmak için sistem izlemesinden elde edilen çıkış raporunu kullanabilirsiniz.
Oyun tabanlı sistem izi ayarlama
Systrace aracı iki şekilde kullanılabilir:
Systrace, şu işlevleri sağlayan düşük seviyeli bir araçtır:
- Gerçek doğruyu sağlar. Systrace, çıkışı doğrudan çekirdekten alır. Bu nedenle, yakaladığı metrikler bir dizi sistem çağrısının bildireceği metriklerle neredeyse aynıdır.
- Az kaynak tüketir. Systrace, verileri bellek içi arabelleğe aktardığı için cihazda genellikle %1'den az olmak üzere çok düşük bir ek yük oluşturur.
En iyi ayarlar
Araca makul bir dizi bağımsız değişken verilmesi önemlidir:
- Kategoriler: Oyun tabanlı bir sistemde izleme için etkinleştirilecek en iyi kategori grubu: {
sched,freq,idle,am,wm,gfx,view,sync,binder_driver,hal,dalvik}. Arabellek boyutu: Genel bir kural olarak, CPU çekirdeği başına 10 MB arabellek boyutu yaklaşık 20 saniye süren bir izlemeye olanak tanır. Örneğin, bir cihazda iki dört çekirdekli CPU (toplam 8 çekirdek) varsa
systraceprogramına aktarılacak uygun değer 80.000 KB (80 MB) olur.Oyununuzda çok fazla bağlam değiştirme işlemi yapılıyorsa arabelleği CPU çekirdeği başına 15 MB'a çıkarın.
Özel etkinlikler: Oyununuzda toplanacak özel etkinlikler tanımlarsanız Systrace'in bu özel etkinlikleri çıkış raporuna dahil etmesine olanak tanıyan
-aişaretini etkinleştirin.
systrace komut satırı programını kullanıyorsanız kategori kümesi, arabellek boyutu ve özel etkinliklerle ilgili en iyi uygulamaları uygulayan bir sistem izi yakalamak için aşağıdaki komutu kullanın:
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
Bir cihazda Systrace sistem uygulamasını kullanıyorsanız kategori kümesi, arabellek boyutu ve özel etkinliklerle ilgili en iyi uygulamaları içeren bir sistem izleme kaydı almak için aşağıdaki adımları tamamlayın:
Hata ayıklanabilir uygulamaları izle seçeneğini etkinleştirin.
Bu ayarı kullanmak için cihazda 256 MB veya 512 MB kullanılabilir alan olmalıdır (CPU'nun 4 veya 8 çekirdekli olmasına bağlı olarak) ve 64 MB'lık her bellek parçası bitişik bir parça olarak kullanılabilir olmalıdır.
Kategoriler'i seçin ve aşağıdaki listedeki kategorileri etkinleştirin:
am: Etkinlik Yöneticisibinder_driver: Binder Kernel sürücüsüdalvik: Dalvik VMfreq: CPU Frekansıgfx: Grafikhal: Donanım Modülleriidle: CPU Boştasched: CPU Planlamasync: Senkronizasyonview: Sistemi Görüntülemewm: Pencere Yöneticisi
İzleme kaydını etkinleştirin.
Oyununuzu yükleyin.
Oyununuzda, cihaz performansını ölçmek istediğiniz oyun deneyimine karşılık gelen etkileşimleri gerçekleştirin.
Oyununuzda istenmeyen bir davranışla karşılaştıktan kısa süre sonra sistem izlemeyi kapatın.
Sorunu daha ayrıntılı bir şekilde analiz etmek için gereken performans istatistiklerini aldınız.
Disk alanından tasarruf etmek için cihaz üzerindeki sistem izleri, dosyaları sıkıştırılmış iz biçiminde (*.ctrace) kaydeder. Rapor oluştururken bu dosyayı açmak için komut satırı programını kullanın ve --from-file seçeneğini ekleyin:
python systrace.py --from-file=/data/local/traces/my_game_trace.ctrace \ -o my_systrace_report.html
Belirli performans alanlarını iyileştirme
Bu bölümde, mobil oyunlardaki yaygın performans sorunlarından bazıları vurgulanmakta ve oyununuzun bu yönlerini nasıl belirleyip iyileştireceğiniz açıklanmaktadır.
Yükleme hızı
Oyuncular, oyununuzdaki aksiyona mümkün olduğunca hızlı bir şekilde dahil olmak ister. Bu nedenle, oyununuzun yükleme sürelerini olabildiğince iyileştirmeniz önemlidir. Aşağıdaki önlemler genellikle yükleme sürelerini kısaltır:
- Geç yükleme gerçekleştirin. Oyununuzdaki ardışık sahnelerde veya seviyelerde aynı öğeleri kullanıyorsanız bu öğeleri yalnızca bir kez yükleyin.
- Öğelerinizin boyutunu küçültün. Bu sayede, bu öğelerin sıkıştırılmamış sürümlerini oyununuzun APK'sıyla birlikte paketleyebilirsiniz.
- Diskte verimli bir sıkıştırma yöntemi kullanın. Bu tür bir yönteme örnek olarak zlib verilebilir.
- Mono yerine IL2CPP'yi kullanın. (Yalnızca Unity kullanıyorsanız geçerlidir.) IL2CPP, C# komut dosyalarınız için daha iyi yürütme performansı sağlar.
- Oyununuzu çok iş parçacıklı hale getirin. Daha fazla bilgi için framerate consistency bölümüne bakın.
Kare hızı tutarlılığı
Oyun deneyiminin en önemli unsurlarından biri tutarlı bir kare hızı elde etmektir. Bu hedefe ulaşmayı kolaylaştırmak için bu bölümde ele alınan optimizasyon tekniklerini uygulayın.
Çoklu iş parçacığı
Birden fazla platform için geliştirme yaparken oyununuzdaki tüm etkinlikleri tek bir iş parçacığına yerleştirmek doğaldır. Bu yürütme yöntemi birçok oyun motorunda kolayca uygulanabilse de Android cihazlarda çalıştırıldığında optimum olmaktan uzaktır. Sonuç olarak, tek iş parçacıklı oyunlar genellikle yavaş yüklenir ve tutarlı bir kare hızına sahip olmaz.
Şekil 1'de gösterilen Systrace, yalnızca tek bir CPU'da çalışan bir oyuna özgü davranışı gösterir:
Oyununuzun performansını artırmak için oyununuzu çok iş parçacıklı hale getirin. Genellikle en iyi model, 2 iş parçacığına sahip olmaktır:
- Oyununuzun ana modüllerini içeren ve oluşturma komutları gönderen bir oyun iş parçacığı.
- Bir render iş parçacığı, oluşturma komutlarını alır ve bunları, bir sahneyi görüntülemek için cihazın GPU'sunun kullanabileceği grafik komutlarına çevirir.
Vulkan API, 2 ortak arabelleği paralel olarak gönderme özelliği sayesinde bu modeli genişletir. Bu özelliği kullanarak birden fazla oluşturma iş parçacığını birden fazla CPU'ya dağıtabilir ve böylece bir sahnenin oluşturma süresini daha da iyileştirebilirsiniz.
Oyununuzun çoklu iş parçacığı performansını artırmak için motora özel bazı değişiklikler de yapabilirsiniz:
- Oyununuzu Unity oyun motorunu kullanarak geliştiriyorsanız Çok İş Parçacıklı Oluşturma ve GPU Skinning seçeneklerini etkinleştirin.
- Özel bir oluşturma motoru kullanıyorsanız oluşturma komutu işlem hattı ile grafik komutu işlem hattının doğru şekilde hizalandığından emin olun. Aksi takdirde, oyununuzun sahnelerinin gösterilmesinde gecikmeler yaşanabilir.
Bu değişiklikleri uyguladıktan sonra, Şekil 2'de gösterildiği gibi oyununuzun aynı anda en az 2 CPU kullandığını görmeniz gerekir:
Kullanıcı arayüzü öğesi yükleniyor
Özelliklerle dolu bir oyun oluştururken oyuncuya aynı anda birçok farklı seçenek ve işlem göstermek cazip gelebilir. Ancak tutarlı bir kare hızı elde etmek için mobil ekranların nispeten küçük boyutunu göz önünde bulundurmanız ve kullanıcı arayüzünüzü mümkün olduğunca basit tutmanız önemlidir.
Şekil 3'te gösterilen Systrace raporu, mobil cihazın özelliklerine kıyasla çok fazla öğe oluşturmaya çalışan bir kullanıcı arayüzü çerçevesi örneğidir.
İyi bir hedef, kullanıcı arayüzü güncelleme süresini 2-3 milisaniyeye düşürmektir. Aşağıdakilere benzer optimizasyonlar yaparak bu tür hızlı güncellemeler elde edebilirsiniz:
- Yalnızca ekranda taşınmış olan öğeleri güncelleyin.
- Kullanıcı arayüzü dokularının ve katmanlarının sayısını sınırlayın. Aynı materyali kullanan grafik çağrılarını (ör. gölgelendiriciler ve dokular) birleştirmeyi düşünebilirsiniz.
- Öğe animasyonu işlemlerini GPU'ya erteleyin.
- Daha agresif bir şekilde görüş alanı ve tıkanma eleme işlemi gerçekleştirin.
- Mümkünse çizim işlemlerini Vulkan API'yi kullanarak gerçekleştirin. Vulkan'da çizim çağrısı ek yükü daha düşüktür.
Güç tüketimi
Önceki bölümde bahsedilen optimizasyonları yaptıktan sonra bile oyununuzun kare hızının, oyunun ilk 45-50 dakikasında düştüğünü görebilirsiniz. Ayrıca, cihaz zaman içinde ısınmaya ve daha fazla pil tüketmeye başlayabilir.
Çoğu durumda, bu istenmeyen termal ve güç tüketimi seti, oyununuzun iş yükünün bir cihazın CPU'larına nasıl dağıtıldığıyla ilgilidir. Oyununuzun güç tüketimi verimliliğini artırmak için aşağıdaki bölümlerde gösterilen en iyi uygulamaları uygulayın.
Bellek açısından yoğun ileti dizilerini tek bir CPU'da tutma
Birçok mobil cihazda L1 önbellekleri belirli CPU'larda, L2 önbellekleri ise bir saati paylaşan CPU'larda bulunur. L1 önbellek isabetlerini en üst düzeye çıkarmak için genellikle oyununuzun ana iş parçacığını ve diğer tüm bellek yoğun iş parçacıklarını tek bir CPU'da çalıştırmak en iyisidir.
Kısa süreli işleri daha düşük güçlü CPU'lara erteleme
Unity de dahil olmak üzere çoğu oyun motoru, çalışan iş parçacığı işlemlerini oyununuzun ana iş parçacığına göre farklı bir CPU'ya ertelemeyi bilir. Ancak motor, cihazın belirli mimarisinin farkında değildir ve oyununuzun iş yükünü sizin kadar iyi tahmin edemez.
Çoğu çip üzerinde sistem cihazında en az 2 paylaşılan saat bulunur. Bunlardan biri cihazın hızlı CPU'ları, diğeri ise cihazın yavaş CPU'ları içindir. Bu mimarinin bir sonucu olarak, hızlı bir CPU'nun maksimum hızda çalışması gerektiğinde diğer tüm hızlı CPU'lar da maksimum hızda çalışır.
Şekil 4'te gösterilen örnek raporda, hızlı CPU'lardan yararlanan bir oyun gösterilmektedir. Ancak bu yüksek aktivite seviyesi, kısa sürede çok fazla güç ve ısı üretir.
Genel güç kullanımını azaltmak için planlayıcıya, daha kısa süreli işlerin (ör. ses yükleme, çalışan iş parçacıklarını çalıştırma ve koreografı yürütme) cihazdaki yavaş CPU'lar kümesine ertelenmesini önermek en iyisidir. İstenen kare hızını korurken bu işin mümkün olduğunca büyük bir kısmını yavaş CPU'lara aktarın.
Çoğu cihaz, yavaş CPU'ları hızlı CPU'lardan önce listeler ancak cihazınızın SOC'sinin bu sırayı kullandığını varsayamazsınız. Kontrol etmek için GitHub'daki CPU topolojisi keşfi kodunda gösterilenlere benzer komutlar çalıştırın.
Cihazınızdaki yavaş CPU'ları öğrendikten sonra, cihazın planlayıcısının takip ettiği kısa süreli iş parçacıklarınız için yakınlıklar bildirebilirsiniz. Bunu yapmak için her ileti dizisine aşağıdaki kodu ekleyin:
#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);
Isı stresi
Cihazlar aşırı ısındığında CPU ve/veya GPU'nun hızını düşürebilir. Bu durum, oyunları beklenmedik şekillerde etkileyebilir. Karmaşık grafikler, yoğun hesaplama veya sürekli ağ etkinliği içeren oyunlarda sorunlarla karşılaşma olasılığı daha yüksektir.
Cihazdaki sıcaklık değişikliklerini izlemek ve daha düşük güç kullanımı ile daha düşük cihaz sıcaklığı sağlamak için termal API'yi kullanın. Cihaz, termal stres bildirdiğinde güç kullanımını azaltmak için devam eden etkinlikleri durdurun. Örneğin, kare hızını veya poligon tessellasyonunu azaltın.
Öncelikle PowerManager nesnesini tanımlayın ve onCreate() yönteminde ilk kullanıma hazırlayın. Nesneye termal durum dinleyicisi ekleyin.
Kotlin
class MainActivity : AppCompatActivity() { lateinit var powerManager: PowerManager override fun onCreate(savedInstanceState: Bundle?) { powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager powerManager.addThermalStatusListener(thermalListener) } }
Java
public class MainActivity extends AppCompatActivity { PowerManager powerManager; @Override protected void onCreate(Bundle savedInstanceState) { ... powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); powerManager.addThermalStatusListener(thermalListener); } }
Dinleyici bir durum değişikliği algıladığında yapılacak işlemleri tanımlayın. Oyununuzda C/C++ kullanılıyorsa onThermalStatusChanged() bölümündeki termal durum seviyelerine JNI kullanarak yerel oyun kodunuzu çağırmak için kod ekleyin veya yerel Thermal API'yi kullanın.
Kotlin
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 } } } }
Java
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; } } };
Dokunma-görüntüleme gecikmesi
Kareleri mümkün olduğunca hızlı şekilde işleyen oyunlar, kare arabelleğinin aşırı dolduğu GPU'ya bağlı bir senaryo oluşturur. CPU'nun GPU'yu beklemesi gerekir. Bu durum, oyuncunun girişi ile girişin ekranda etkili olması arasında belirgin bir gecikmeye neden olur.
Oyununuzun kare hızını iyileştirip iyileştiremeyeceğinizi belirlemek için aşağıdaki adımları tamamlayın:
gfxveinputkategorilerini içeren bir Systrace raporu oluşturun. Bu kategoriler, özellikle dokunma-görüntüleme gecikmesini belirlemek için yararlı ölçümler içerir.Systrace raporunun
SurfaceViewbölümünü kontrol edin. Aşırı dolu bir arabellek, Şekil 5'te gösterildiği gibi, bekleyen arabellek çizimlerinin sayısının 1 ile 2 arasında değişmesine neden olur:Şekil 5. Çizim komutlarını kabul edemeyecek kadar dolu olan arabelleği gösteren Systrace raporu
Kare hızındaki bu tutarsızlığı azaltmak için aşağıdaki bölümlerde açıklanan işlemleri tamamlayın:
Android Frame Pacing API'yi oyununuza entegre etme
Android Frame Pacing API, kare değişimleri yapmanıza ve oyununuzun daha tutarlı bir kare hızını koruyacak şekilde değişim aralığı tanımlamanıza yardımcı olur.
Oyununuzun kullanıcı arayüzü dışındaki öğelerinin çözünürlüğünü düşürme
Modern mobil cihazlardaki ekranlar, bir oynatıcının işleyebileceğinden çok daha fazla piksel içerir. Bu nedenle, 5 veya hatta 10 pikselin tek bir renk içerecek şekilde alt örnekleme yapılması uygundur. Çoğu görüntüleme önbelleğinin yapısı göz önüne alındığında, çözünürlüğü yalnızca bir boyutta azaltmak en iyisidir.
Ancak oyununuzun kullanıcı arayüzü öğelerinin çözünürlüğünü düşürmeyin. Tüm oyuncularınız için yeterince büyük bir dokunma hedefi boyutu sağlamak amacıyla bu öğelerdeki çizgi kalınlığını korumanız önemlidir.
Oluşturma düzgünlüğü
SurfaceFlinger, oyununuzda bir sahne göstermek için ekran arabelleğine bağlandığında CPU etkinliği anlık olarak artar. CPU etkinliğindeki bu ani artışlar eşit olmayan bir şekilde gerçekleşirse oyununuzda takılma olabilir. Şekil 6'daki diyagramda bunun neden olduğu gösterilmektedir:
Bir kare çizilmeye birkaç milisaniye bile geç başlarsa bir sonraki görüntüleme penceresini kaçırabilir. Çerçevenin görüntülenmesi için bir sonraki Vsync'e kadar beklemesi gerekir (30 FPS'de oyun oynarken 33 milisaniye). Bu durum, oyuncu açısından fark edilebilir bir gecikmeye neden olur.
Bu durumu düzeltmek için her zaman bir VSync dalgasında yeni bir kare sunan Android Frame Pacing API'yi kullanın.
Bellek durumu
Oyununuzu uzun süre çalıştırdığınızda cihazda bellek yetersiz hatası oluşabilir.
Bu durumda, Systrace raporundaki CPU etkinliğini kontrol edin ve sistemin kswapd daemon'ına ne sıklıkta çağrı yaptığını görün. Oyununuz çalışırken çok sayıda çağrı yapılıyorsa oyununuzun belleği nasıl yönettiğini ve temizlediğini daha yakından incelemeniz en iyisidir.
Daha fazla bilgi için Bellek yönetimi hakkında başlıklı makaleyi inceleyin.
İleti dizisi durumu
Systrace raporunun tipik öğeleri arasında gezinirken rapordaki iş parçacığını seçerek belirli bir iş parçacığının her olası iş parçacığı durumunda geçirdiği süreyi görüntüleyebilirsiniz (Şekil 7'de gösterildiği gibi):
Şekil 7'de gösterildiği gibi, oyununuzun iş parçacıklarının gerektiği sıklıkta "çalışıyor" veya "çalıştırılabilir" durumda olmadığını görebilirsiniz. Aşağıdaki listede, belirli bir iş parçacığının neden periyodik olarak olağan dışı bir duruma geçiş yapabileceğinin birkaç yaygın nedeni gösterilmektedir:
- Bir iş parçacığı uzun süre uyuyorsa kilit çekişmesinden veya GPU etkinliğini beklemekten etkileniyor olabilir.
- Bir iş parçacığı G/Ç'de sürekli olarak engelleniyorsa ya diskten aynı anda çok fazla veri okuyorsunuzdur ya da oyununuzda aşırı yüklenme vardır.
Ek kaynaklar
Oyununuzun performansını artırma hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara göz atın:
Videolar
- Android Game Developer Summit 2018'deki Systrace for Games sunumu