Mengonfigurasi pelacakan sistem

Anda dapat mengonfigurasi pelacakan sistem untuk merekam profil CPU dan thread aplikasi Anda dalam jangka waktu singkat. Kemudian, Anda dapat menggunakan laporan output dari pelacakan sistem untuk meningkatkan performa game.

Menyiapkan pelacakan sistem berbasis game

Alat Systrace tersedia dalam dua cara:

Systrace adalah alat level rendah yang:

  • Memberikan kebenaran dasar. Systrace merekam output langsung dari kernel, sehingga metrik yang direkamnya nyaris identik dengan yang dilaporkan oleh serangkaian panggilan sistem.
  • Menggunakan sedikit resource. Systrace memberikan overhead yang sangat rendah di perangkat, biasanya kurang dari 1%, karena mengalirkan data ke buffering dalam memori.

Setelan yang optimal

Penting untuk memberi alat ini sekumpulan argumen yang wajar:

  • Kategori: Kumpulan kategori terbaik yang dapat diaktifkan untuk pelacakan sistem berbasis game adalah: {sched, freq, idle, am, wm, gfx, view, sync, binder_driver, hal, dalvik}.
  • Ukuran buffer: Aturan umumnya adalah ukuran buffer sebesar 10 MB per core CPU dapat mendukung pelacakan berdurasi sekitar 20 detik. Misalnya, jika perangkat memiliki dua CPU quad core (total 8 core), nilai yang sesuai untuk diteruskan ke program systrace adalah 80.000 KB (80 MB).

    Jika game Anda melakukan banyak peralihan konteks, tingkatkan buffering menjadi 15 MB per core CPU.

  • Peristiwa kustom: Jika Anda menentukan peristiwa kustom yang akan direkam di dalam game, aktifkan flag -a, yang memungkinkan Systrace menyertakan peristiwa kustom tersebut dalam laporan output.

Jika Anda menggunakan program command line systrace, gunakan perintah berikut untuk merekam pelacakan sistem yang menerapkan praktik terbaik untuk kumpulan kategori, ukuran buffer, dan peristiwa kustom:

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

Jika Anda menggunakan aplikasi sistem Systrace di perangkat, selesaikan langkah-langkah berikut untuk merekam pelacakan sistem yang menerapkan praktik terbaik untuk kumpulan kategori, ukuran buffer, dan peristiwa kustom:

  1. Aktifkan opsi Aplikasi pelacakan yang dapat di-debug.

    Agar dapat menggunakan setelan ini, perangkat harus menyediakan memori sebesar 256 MB atau 512 MB (bergantung pada apakah CPU-nya memiliki 4 atau 8 core), dan setiap 64 MB dari memori tersebut harus tersedia sebagai bagian yang berdekatan.

  2. Pilih Kategori, lalu aktifkan kategori dalam daftar berikut:

    • am: Pengelola Aktivitas
    • binder_driver: Driver Kernel Binder
    • dalvik: VM Dalvik
    • freq: Frekuensi CPU
    • gfx: Grafis
    • hal: Modul Hardware
    • idle: CPU Tidak Ada Aktivitas
    • sched: Penjadwalan CPU
    • sync: Sinkronisasi
    • view: Lihat Sistem
    • wm: Window Manager
  3. Aktifkan Rekam pelacakan.

  4. Muat game Anda.

  5. Lakukan interaksi dalam game Anda sesuai gameplay yang performa perangkatnya ingin diukur.

  6. Begitu menemukan perilaku yang tidak diinginkan dalam game Anda, nonaktifkan pelacakan sistem.

Anda telah merekam statistik performa yang diperlukan untuk menganalisis masalah itu lebih jauh.

Untuk menghemat ruang disk, pelacakan sistem di perangkat menyimpan file dalam format pelacakan terkompresi (*.ctrace). Untuk membatalkan kompresi file ini saat membuat laporan, gunakan program command line dan sertakan opsi --from-file:

python systrace.py --from-file=/data/local/traces/my_game_trace.ctrace \
  -o my_systrace_report.html

Meningkatkan area performa tertentu

Bagian ini menyoroti beberapa masalah performa yang umum terjadi dalam game seluler dan menjelaskan cara mengidentifikasi dan meningkatkan aspek tersebut dalam game Anda.

Kecepatan pemuatan

Pemain ingin mulai memainkan game Anda secepat mungkin, jadi penting untuk membuat waktu pemuatan game sesingkat mungkin. Tindakan berikut dapat membantu mengurangi waktu pemuatan:

  • Jalankan pemuatan lambat. Jika Anda menggunakan aset yang sama di sejumlah scene atau level yang berurutan dalam game, muat aset ini sekali saja.
  • Kurangi ukuran aset. Dengan begitu, Anda dapat memaketkan versi aset tersebut yang tidak dikompresi bersama APK game.
  • Gunakan metode kompresi yang hemat ruang disk. Contoh metode ini adalah zlib.
  • Menggunakan IL2CPP bukan mono. (Hanya berlaku jika Anda menggunakan Unity.) IL2CPP menghadirkan performa yang lebih baik untuk skrip C# Anda.
  • Jadikan game Anda multi-thread. Untuk detail selengkapnya, lihat bagian konsistensi kecepatan frame.

Konsistensi kecepatan frame

Salah satu elemen terpenting dari pengalaman gameplay adalah mencapai kecepatan frame yang konsisten. Untuk memudahkan pencapaian sasaran ini, ikuti teknik pengoptimalan yang dijelaskan di bagian ini.

Multithreading

Saat mengembangkan untuk banyak platform, sebaiknya tempatkan semua aktivitas dalam game Anda di satu thread. Meskipun mudah diterapkan di banyak game engine, metode eksekusi ini tidak optimal saat dijalankan di perangkat Android. Akibatnya, game thread tunggal sering kali lambat dimuat dan tidak memiliki kecepatan frame yang konsisten.

Systrace yang ditunjukkan pada Gambar 1 menampilkan perilaku standar game yang berjalan hanya pada satu CPU pada waktu yang sama:

Diagram thread
dalam pelacakan sistem

Gambar 1. Laporan Systrace untuk game thread tunggal

Untuk meningkatkan performa game, jadikan game Anda multi-thread. Biasanya, model terbaiknya adalah menggunakan 2 thread:

  • Satu thread game, yang berisi modul utama game dan mengirim perintah render.
  • Satu thread render, yang menerima perintah render dan mengubahnya menjadi perintah grafis yang dapat digunakan GPU perangkat untuk menampilkan scene.

Vulkan API dikembangkan berdasarkan model ini, mengingat kapabilitasnya untuk mengirim 2 buffering umum secara paralel. Dengan fitur ini, Anda dapat mendistribusikan beberapa thread render ke banyak CPU, yang akan semakin mempersingkat waktu render scene.

Anda juga dapat membuat beberapa perubahan khusus engine untuk meningkatkan performa multithreading game:

  • Jika Anda mengembangkan game menggunakan game engine Unity, aktifkan opsi Multithreaded Rendering dan GPU Skinning.
  • Jika Anda menggunakan engine rendering kustom, pastikan pipeline perintah render sudah tepat selaras dengan pipeline perintah grafis; jika tidak, penayangan scene game dapat tertunda.

Setelah menerapkan perubahan ini, game Anda akan menggunakan setidaknya 2 CPU secara bersamaan, sebagaimana ditunjukkan pada Gambar 2:

Diagram thread
dalam pelacakan sistem

Gambar 2. Laporan Systrace untuk game multi-thread

Pemuatan elemen UI

Diagram stack
  frame dalam pelacakan sistem
Gambar 3. Laporan Systrace untuk game yang merender belasan elemen UI secara bersamaan

Saat membuat game yang kaya fitur, Anda mungkin tergoda untuk menampilkan berbagai opsi dan aksi secara bersamaan kepada pemain. Namun, untuk mempertahankan kecepatan frame yang konsisten, penting untuk mempertimbangkan ukuran layar perangkat seluler yang relatif kecil dan membuat UI Anda sesederhana mungkin.

Laporan Systrace pada Gambar 3 menunjukkan contoh frame UI yang mencoba merender terlalu banyak elemen dibandingkan kapabilitas perangkat seluler.

Usahakan untuk mengurangi waktu update UI menjadi 2-3 milidetik. Untuk mencapai update secepat ini, jalankan pengoptimalan seperti berikut:

  • Update hanya elemen di layar yang telah bergerak.
  • Batasi jumlah tekstur dan lapisan UI. Pertimbangkan untuk menggabungkan panggilan grafis, seperti shader dan tekstur, yang menggunakan materi yang sama.
  • Alihkan operasi animasi elemen ke GPU.
  • Jalankan penghilangan frustum dan hambatan yang lebih agresif.
  • Jika memungkinkan, jalankan operasi gambar menggunakan Vulkan API. Overhead panggilan gambar lebih rendah di Vulkan.

Pemakaian daya

Meskipun sudah melakukan pengoptimalan yang dibahas di bagian sebelumnya, kecepatan frame game Anda mungkin menurun dalam 45-50 menit pertama gameplay. Selain itu, perangkat mungkin akan memanas dan pemakaian daya baterai meningkat seiring waktu.

Dalam banyak kasus, meningkatnya suhu dan pemakaian daya yang tidak diinginkan ini terkait dengan distribusi beban kerja game Anda ke seluruh CPU perangkat. Untuk meningkatkan efisiensi pemakaian daya game Anda, terapkan praktik terbaik yang ditunjukkan di bagian berikut.

Menyimpan thread yang menggunakan banyak memori di satu CPU

Di banyak perangkat seluler, cache L1 berada di CPU tertentu, dan cache L2 berada di kumpulan CPU yang membagikan jam. Untuk memaksimalkan penemuan cache L1, sebaiknya tetap jalankan thread utama game Anda, juga thread lain yang menggunakan banyak memori, di satu CPU.

Mengalihkan tugas berdurasi singkat ke CPU yang menggunakan lebih sedikit daya

Sebagian besar mesin game, termasuk Unity, tahu cara mengalihkan operasi thread pekerja ke CPU lain yang terkait dengan thread utama game. Namun, mesin ini tidak mengetahui arsitektur spesifik perangkat dan tidak dapat mengantisipasi beban kerja game sebaik Anda.

Sebagian besar perangkat sistem di chipm emiliki setidaknya 2 jam bersama, satu untuk CPU cepat dan satu untuk CPU lambat perangkat. Konsekuensi dari arsitektur ini adalah, jika satu CPU cepat perlu beroperasi pada kecepatan maksimum, semua CPU cepat lainnya juga akan beroperasi pada kecepatan maksimum.

Contoh laporan pada Gambar 4 menunjukkan game yang memanfaatkan CPU cepat. Namun, level aktivitas tinggi ini memakai banyak daya dan menimbulkan kenaikan suhu dengan cepat.

Diagram thread
dalam pelacakan sistem

Gambar 4. Laporan Systrace yang menunjukkan penetapan thread yang kurang optimal ke CPU perangkat

Untuk mengurangi penggunaan daya secara keseluruhan, sebaiknya atur penjadwal sehingga tugas berdurasi singkat—seperti memuat audio, menjalankan thread pekerja, dan mengeksekusi koreografer—dialihkan ke kumpulan CPU lambat di perangkat. Transfer sebanyak mungkin tugas ini ke CPU lambat sekaligus mempertahankan kecepatan frame yang diinginkan.

Sebagian besar perangkat mencantumkan CPU lambat sebelum CPU cepat, tetapi Anda tidak dapat mengasumsikan bahwa SOC perangkat Anda menggunakan urutan ini. Untuk memeriksa, jalankan perintah yang mirip dengan yang yang ditunjukkan dalam penemuan topologi CPU ini, kode di GitHub.

Setelah mengetahui CPU yang merupakan CPU lambat di perangkat, Anda dapat mendeklarasikan afinitas untuk thread berdurasi singkat, yang diikuti oleh penjadwal perangkat. Caranya, tambahkan kode berikut dalam setiap thread:

#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);

Stres termal

Jika menjadi terlalu panas, perangkat dapat men-throttle CPU dan/atau GPU, dan hal ini dapat memengaruhi aplikasi secara tidak terduga. Game yang menggabungkan grafik yang rumit, komputasi yang berat, atau aktivitas jaringan yang berkelanjutan cenderung akan mengalami masalah.

Gunakan Thermal API untuk memantau perubahan suhu pada perangkat dan melakukan tindakan untuk mempertahankan penggunaan daya yang lebih rendah dan suhu perangkat yang lebih dingin. Saat perangkat melaporkan tekanan termal, hentikan aktivitas yang sedang berlangsung untuk mengurangi penggunaan daya. Misalnya, kurangi kecepatan frame atau teselasi poligon.

Pertama, deklarasikan objek PowerManager dan lakukan inisialisasi dalam metode onCreate(). Tambahkan pemroses status termal ke objek.

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);
    }
}

Tentukan tindakan yang akan dilakukan saat pemroses mendeteksi perubahan status. Jika game Anda menggunakan C/C++, tambahkan kode ke level status termal di onThermalStatusChanged() untuk memanggil kode game native menggunakan JNI atau gunakan Thermal API native.

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;
        }
    }
};

Latensi sentuh-hingga-tampil

Game yang merender frame secepat mungkin membuat skenario terkait GPU, dengan buffering frame yang menjadi terlalu sesak. CPU harus menunggu GPU, dan hal ini menimbulkan jeda yang terlihat antara input pemain dan penerapan input tersebut di layar.

Untuk mengetahui apakah kecepatan frame game dapat ditingkatkan, selesaikan langkah-langkah berikut:

  1. Buat laporan Systrace yang menyertakan kategori gfx dan input. Kategori ini mencakup pengukuran yang sangat berguna untuk mengetahui latensi sentuh-hingga-tampil.
  2. Lihat bagian SurfaceView dalam laporan Systrace. Buffering yang terlalu sesak mengakibatkan jumlah gambar buffering yang tertunda berubah-ubah antara 1 dan 2, sebagaimana ditunjukkan pada Gambar 5:

    Diagram
antrean buffer dalam pelacakan sistem

    Gambar 5. Laporan Systrace menampilkan buffering terlalu sesak yang secara berkala terlalu penuh untuk menerima perintah gambar

Untuk mengurangi inkonsistensi dalam kecepatan frame ini, selesaikan tindakan yang dijelaskan di bagian berikut:

Integrasikan Android Frame Pacing API ke dalam game

Android Frame Pacing API membantu Anda menjalankan pertukaran frame dan menentukan interval pertukaran sehingga game Anda mempertahankan kecepatan frame yang lebih konsisten.

Kurangi resolusi aset non-UI dalam game

Layar perangkat seluler modern memiliki jumlah piksel yang jauh lebih banyak daripada yang dapat diproses pemain, sehingga tidak masalah jika Anda mengurangi sampel sehingga rangkaian 5 atau bahkan 10 piksel semuanya memuat satu warna. Mengingat struktur sebagian besar cache layar, sebaiknya kurangi resolusi untuk satu dimensi saja.

Namun, jangan kurangi resolusi elemen UI game. Penting untuk menjaga ketebalan garis pada elemen ini untuk mempertahankan ukuran target sentuh yang cukup besar bagi semua pemain.

Kelancaran proses rendering

Saat SurfaceFlinger mengait ke buffering tampilan untuk menampilkan scene dalam game, aktivitas CPU akan meningkat sesaat. Jika lonjakan aktivitas CPU ini tidak merata, kemungkinan game Anda akan tersendat. Diagram pada Gambar 6 menggambarkan alasan terjadinya hal ini:

Diagram frame
melewatkan jendela Vsync karena terlambat memulai menggambar

Gambar 6. Laporan Systrace yang menunjukkan cara sebuah frame melewatkan Vsync

Jika sebuah frame terlambat memulai operasi gambar, bahkan beberapa milidetik, jendela tampilan berikutnya akan terlewatkan. Kemudian frame tersebut harus menunggu hingga Vsync berikutnya ditampilkan (33 milidetik jika menjalankan game pada 30 FPS), sehingga menyebabkan jeda yang terlihat dari perspektif pemain.

Untuk mengatasi situasi ini, gunakan Android Frame Pacing API, yang selalu menayangkan frame baru di wavefront VSync.

Status memori

Jika menjalankan game dalam waktu yang lama, kemungkinan perangkat akan mengalami error kehabisan memori.

Dalam situasi ini, periksa aktivitas CPU dalam laporan Systrace dan lihat seberapa sering sistem melakukan panggilan ke daemon kswapd. Jika ada banyak panggilan selama eksekusi game, sebaiknya perhatikan cara game Anda mengelola dan membersihkan memori.

Untuk informasi selengkapnya, lihat Mengelola memori dalam game secara efektif.

Status thread

Saat membuka elemen standar dalam laporan Systrace, Anda dapat melihat banyaknya waktu yang dihabiskan thread tertentu dalam setiap kemungkinan status thread dengan memilih thread tersebut dalam laporan, sebagaimana ditunjukkan pada Gambar 7:

Diagram
laporan Systrace

Gambar 7. Laporan Systrace menunjukkan bahwa dengan memilih thread, laporan akan menampilkan ringkasan status untuk thread tersebut

Seperti ditunjukkan pada Gambar 7, Anda dapat melihat bahwa thread game Anda tidak berstatus "running" atau "runnable" sesering yang seharusnya. Daftar berikut menunjukkan alasan umum thread tertentu mungkin beralih secara berkala ke status yang tidak biasa:

  • Jika tidur dalam waktu lama, thread mungkin mengalami pertentangan kunci atau menunggu adanya aktivitas GPU.
  • Jika thread terus-menerus diblokir di I/O, Anda mungkin membaca terlalu banyak data dari disk pada satu waktu, atau game Anda mengalami thrashing.

Referensi tambahan

Untuk mempelajari lebih lanjut cara meningkatkan performa game Anda, lihat referensi tambahan berikut.

Video