Menguji performa UI

Pengujian performa antarmuka pengguna (UI) memastikan aplikasi Anda tidak hanya memenuhi persyaratan fungsionalnya, tetapi juga memastikan interaksi pengguna dengan aplikasi Anda berjalan lancar, dengan 60 frame per detik secara konsisten (mengapa 60 fps?), tanpa ada frame yang hilang atau tertunda, atau seperti yang kita sebut jank. Dokumen ini menjelaskan fitur yang tersedia untuk mengukur performa UI dan menyusun pendekatan untuk mengintegrasikan pengukuran performa UI ke dalam praktik pengujian Anda.

Mengukur performa UI

Untuk meningkatkan performa, pertama-tama Anda perlu kemampuan untuk mengukur performa sistem, kemudian mendiagnosis dan mengidentifikasi masalah yang mungkin muncul dari berbagai pipeline Anda.

dumpsys adalah fitur Android yang berjalan pada perangkat dan menyimpan informasi menarik mengenai status layanan sistem. Penerusan perintah gfxinfo ke dumpsys akan memberikan output dalam logcat berisi informasi performa terkait frame animasi yang terjadi selama fase perekaman.

    > adb shell dumpsys gfxinfo <PACKAGE_NAME>
    

Perintah ini dapat menghasilkan beragam varian data pengaturan waktu frame.

Menggabungkan statistik frame

Dengan Android 6.0 (API level 23) perintah mencetak analisis gabungan data frame ke logcat, yang dikumpulkan selama waktu proses. Contoh:

    Stats since: 752958278148ns
    Total frames rendered: 82189
    Janky frames: 35335 (42.99%)
    90th percentile: 34ms
    95th percentile: 42ms
    99th percentile: 69ms
    Number Missed Vsync: 4706
    Number High input latency: 142
    Number Slow UI thread: 17270
    Number Slow bitmap uploads: 1542
    Number Slow draw: 23342
    

Statistik tingkat tinggi ini menampilkan performa rendering tingkat tinggi aplikasi, serta stabilitasnya di seluruh frame.

Info pengaturan waktu frame yang akurat

Dengan Android 6.0, perintah baru tersedia untuk gfxinfo yang merupakan framestats yang memberikan informasi pengaturan waktu frame yang sangat detail dari frame terbaru, sehingga Anda dapat melacak dan men-debug masalah dengan lebih akurat.

    >adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats
    

Perintah ini mencetak informasi waktu frame, dengan stempel waktu nanodetik, dari 120 frame terakhir yang dihasilkan oleh aplikasi. Berikut adalah contoh output mentah dari framestats <PACKAGE_NAME> gfxinfo dumpsys adb:

    0,27965466202353,27965466202353,27965449758000,27965461202353,27965467153286,27965471442505,27965471925682,27965474025318,27965474588547,27965474860786,27965475078599,27965479796151,27965480589068,
    0,27965482993342,27965482993342,27965465835000,27965477993342,27965483807401,27965486875630,27965487288443,27965489520682,27965490184380,27965490568703,27965491408078,27965496119641,27965496619641,
    0,27965499784331,27965499784331,27965481404000,27965494784331,27965500785318,27965503736099,27965504201151,27965506776568,27965507298443,27965507515005,27965508405474,27965513495318,27965514061984,
    0,27965516575320,27965516575320,27965497155000,27965511575320,27965517697349,27965521276151,27965521734797,27965524350474,27965524884536,27965525160578,27965526020891,27965531371203,27965532114484,
    

Setiap baris output ini mewakili frame yang dihasilkan oleh aplikasi. Setiap baris memiliki jumlah kolom tetap yang mendeskripsikan waktu yang dihabiskan di setiap tahap pipeline penghasil frame. Bagian selanjutnya mendeskripsikan format tersebut secara detail, termasuk apa yang diwakili oleh setiap kolom.

Format data framestats

Karena blok data merupakan output dalam format CSV, Anda dapat langsung menempelkannya ke fitur spreadsheet pilihan, atau mengumpulkan serta mengurainya dengan skrip. Tabel berikut menjelaskan format kolom data output. Semua stempel waktu ditampilkan dalam nanodetik.

  • FLAG
    • Baris berisi ‘0’ untuk kolom FLAG, total waktu frame-nya dapat dihitung dengan mengurangkan kolom INTENDED_VSYNC dari kolom FRAME_COMPLETED.
    • Jika tidak berisi nol, baris ini harus diabaikan, karena frame telah ditetapkan sebagai pencilan dari performa normal, ketika tata letak & penggambaran diperkirakan membutuhkan waktu lebih dari 16 md. Berikut beberapa alasan terjadinya:
      • Tata letak jendela berubah (misalnya frame pertama aplikasi atau setelah rotasi)
      • Ada kemungkinan frame tersebut terlewati sehingga beberapa nilai akan memiliki stempel waktu sampah. Frame dapat dilewatkan, misalnya jika melebihi 60 fps atau jika tidak ada sesuatu di layar yang berakhir secara kotor, ini bukan berarti pertanda adanya masalah dalam aplikasi.
  • INTENDED_VSYNC
    • Titik awal yang dimaksudkan untuk frame. Jika nilai ini berbeda dengan VSYNC, berarti ada pekerjaan yang terjadi pada UI thread yang mencegahnya merespons sinyal vsync tepat waktu.
  • VSYNC
    • Nilai waktu yang digunakan di semua pemroses vsync dan penggambaran untuk frame (callback frame Choreographer, animasi, View.getDrawingTime(), dll…)
    • Untuk memahami VSYNC dan caranya memengaruhi aplikasi lebih lanjut, lihat video Memahami VSYNC.
  • OLDEST_INPUT_EVENT
    • Stempel waktu peristiwa input terlama dalam antrean input, atau Long.MAX_VALUE jika tidak ada peristiwa input untuk frame.
    • Nilai ini utamanya dimaksudkan untuk pekerjaan platform dan memiliki kegunaan terbatas untuk developer aplikasi.
  • NEWEST_INPUT_EVENT
    • Stempel waktu peristiwa input terbaru dalam antrean input, atau 0 jika tidak ada peristiwa input untuk frame.
    • Nilai ini utamanya dimaksudkan untuk pekerjaan platform dan memiliki kegunaan terbatas untuk developer aplikasi.
    • Namun, ada kemungkinan untuk mendapatkan gambaran kasar mengenai besarnya latensi yang ditambahkan aplikasi dengan melihat (FRAME_COMPLETED - NEWEST_INPUT_EVENT).
  • HANDLE_INPUT_START
    • Stempel waktu saat peristiwa input dikirimkan ke aplikasi.
    • Dengan melihat waktu antara stempel waktu ini dan ANIMATION_START, Anda dapat mengukur berapa lama aplikasi menangani peristiwa input.
    • Jika angkanya tinggi (>2 md), ini menunjukkan aplikasi menghabiskan waktu yang sangat lama untuk memproses peristiwa input, seperti View.onTouchEvent(), yang mungkin menunjukkan pekerjaan ini perlu dioptimalkan, atau dialihkan ke thread lain. Perhatikan bahwa ada beberapa skenario, seperti peristiwa klik yang meluncurkan aktivitas baru atau serupa, yang diharapkan dan wajar jika angka ini besar.
  • ANIMATION_START
    • Stempel waktu saat animasi ini terdaftar pada Choreographer yang dijalankan.
    • Dengan melihat waktu antara stempel waktu ini dan PERFORM_TRANVERSALS_START, Anda dapat menentukan lama waktu yang diperlukan dalam mengevaluasi semua animator (ObjectAnimator, ViewPropertyAnimator, dan Transitions menjadi hal yang umum) yang sedang berjalan.
    • Jika angka ini tinggi (>2 md), periksa untuk mengetahui apakah aplikasi Anda telah menulis animator kustom atau kolom apa saja yang dianimasikan ObjectAnimators dan pastikan semuanya sesuai untuk animasi.
    • Untuk mempelajari Choreographer lebih lanjut, lihat video Untuk Lebih Baik atau Buruk.
  • PERFORM_TRAVERSALS_START
    • Jika mengurangkan DRAW_START dari nilai ini, Anda dapat mengetahui berapa lama fase tata letak & mengukur selesai. (Perhatikan, selama scroll, atau animasi, Anda akan berharap angka ini mendekati nol.)
    • Untuk mempelajari lebih lanjut fase mengukur & tata letak dari pipeline rendering, lihat video Invalidasi, Tata Letak, dan Performa.
  • DRAW_START
    • Waktu fase menggambar performTraversals dimulai. Inilah titik awal dari perekaman daftar tampilan dari tampilan apa pun yang tidak divalidasi.
    • Waktu antara stempel waktu ini dan SYNC_START adalah lama waktu yang diperlukan untuk memanggil View.draw() di semua tampilan yang tidak divalidasi dalam hierarki.
    • Untuk informasi model penggambaran selengkapnya, lihat video Akselerasi Hardware atau Invalidasi, Tata Letak, dan Performa.
  • SYNC_QUEUED
    • Waktu permintaan sinkronisasi dikirim ke RenderThread.
    • Ini menandai titik saat pesan yang memulai fase sinkronisasi dikirim ke RenderThread. Jika waktu antara stempel waktu ini dan SYNC_START diketahui substansial (>0,1 md atau lebih), berarti RenderThread sedang sibuk mengerjakan frame lain. Secara internal, ini digunakan untuk membedakan antara frame yang terlalu banyak melakukan pekerjaan dan melebihi batas 16 md dengan frame yang macet karena frame sebelumnya melebihi batas 16 md.
  • SYNC_START
    • Waktu fase sinkronisasi menggambar dimulai.
    • Jika waktu antara stempel waktu ini dan ISSUE_DRAW_COMMANDS_START diketahui substansial (>0,4 md atau lebih), hal ini biasanya menunjukkan bahwa banyak Bitmap baru yang digambar dan harus diupload ke GPU.
    • Untuk memahami fase sinkronisasi lebih lanjut, lihat video Membuat Profil Rendering GPU.
  • ISSUE_DRAW_COMMANDS_START
    • Waktu saat perender hardware mulai mengeluarkan perintah menggambar ke GPU.
    • Waktu antara stempel waktu ini dan FRAME_COMPLETED memberikan gambaran kasar mengenai besarnya pekerjaan GPU yang dihasilkan aplikasi. Masalah seperti overdraw yang terlalu banyak atau efek rendering yang tidak efisien ditampilkan di sini.
  • SWAP_BUFFERS
    • Waktu saat eglSwapBuffers dipanggil, relatif tidak menarik selain pekerjaan platform.
  • FRAME_COMPLETED
    • Selesai! Waktu total yang dihabiskan untuk mengerjakan frame ini dapat dihitung dengan melakukan FRAME_COMPLETED - INTENDED_VSYNC.

Anda dapat menggunakan data ini dengan berbagai cara. Satu visualisasi yang sederhana tetapi berguna adalah histogram yang menampilkan distribusi waktu frame (FRAME_COMPLETED - INTENDED_VSYNC) dalam bucket latensi yang berbeda, lihat gambar di bawah. Gambar ini memberi tahu kita secara sekilas bahwa sebagian besar frame sangat bagus - jauh di bawah batas 16 md (digambarkan dengan warna merah), tetapi beberapa frame terlihat jauh melebihi batasnya. Kita dapat melihat perubahan dalam histogram ini seiring waktu untuk mengetahui perpindahan besar-besaran atau pencilan baru yang sedang dibuat. Anda juga dapat membuat grafik latensi input, yaitu waktu yang dihabiskan dalam layout, atau metrik menarik serupa lain berdasarkan banyaknya stempel waktu dalam data.

Penimbunan pengaturan waktu frame sederhana

Jika Profil GPU Rendering ditetapkan ke Di adb shell dumpsys gfxinfo dalam Opsi Developer, perintah adb shell dumpsys gfxinfo akan mencetak informasi pengaturan waktu untuk 120 frame terbaru, yang dibagi menjadi beberapa macam kategori dengan nilai yang dipisahkan tab. Data ini dapat berguna untuk menunjukkan bagian mana dari pipeline penggambaran yang mungkin lambat pada tingkat tinggi.

Serupa dengan framestats di atas, Anda dapat langsung menempelkannya ke fitur spreadsheet pilihan, atau mengumpulkan dan mengurainya dengan skrip. Gambar berikut menampilkan perincian tentang tempat frame yang dihasilkan aplikasi menghabiskan waktunya.

Hasil dari menjalankan gfxinfo, menyalin output, menempelkannya ke dalam aplikasi spreadsheet, dan membuat grafik data sebagai batang bertumpuk.

Setiap batang vertikal mewakili satu frame animasi; ketinggiannya mewakili jumlah milidetik yang dihabiskannya untuk menghitung frame animasi. Setiap segmen berwarna pada batang ini mewakili tahap berbeda dari pipeline rendering, sehingga Anda dapat melihat bagian mana dari aplikasi yang mungkin menimbulkan hambatan. Untuk informasi selengkapnya tentang memahami dan mengoptimalkan pipeline rendering, lihat video Invalidasi, Tata Letak, dan Performa.

Mengontrol jendela pengumpulan statistik

Framestat dan pengaturan waktu frame sederhana mengumpulkan data dalam periode yang sangat singkat - sekitar dua detik waktu rendering. Untuk mengontrol jangka waktu dengan akurat - misalnya, untuk membatasi data ke animasi tertentu - Anda dapat menyetel ulang semua penghitung, dan menyatukan statistik yang terkumpul.

    >adb shell dumpsys gfxinfo <PACKAGE_NAME> reset
    

Ini juga dapat digunakan bersamaan dengan perintah penimbunan itu sendiri untuk mengumpulkan dan menyetel ulang pada ritme yang teratur, yang mengambil jendela frame kurang-dari-dua-detik secara terus menerus.

Mendiagnosis regresi performa

Identifikasi regresi merupakan langkah pertama yang baik untuk melacak masalah, dan mempertahankan kesehatan aplikasi yang tinggi. Akan tetapi, dumpsys hanya mengidentifikasi keberadaan dan keparahan relatif masalahnya. Anda tetap harus mendiagnosis penyebab khusus dari masalah performa, dan menemukan cara yang sesuai untuk memperbaikinya. Untuk itu, sangat direkomendasikan menggunakan fitur systrace.

Referensi lainnya

Untuk mengetahui informasi selengkapnya tentang cara kerja pipeline rendering Android, masalah umum yang dapat Anda temukan di sana, dan cara memperbaikinya, referensi berikut mungkin berguna untuk Anda:

Mengotomatiskan pengujian performa UI

Satu pendekatan terhadap pengujian Performa UI adalah meminta seorang penguji melakukan serangkaian operasi pengguna pada aplikasi target, dan melihat dengan kasat mata setiap jank, atau penggunaan waktu yang sangat besar melalui pendekatan berbasis fitur untuk mengetahuinya. Namun, pendekatan manual ini penuh risiko - kemampuan manusia memahami perubahan frekuensi frame sangatlah berbeda-beda, dan pendekatan ini juga menyita waktu, melelahkan, serta rawan error.

Pendekatan yang lebih efisien adalah mencatat dan menganalisis metrik performa kunci dari pengujian UI otomatis. Android 6.0 mencakup kemampuan logging baru yang memudahkan untuk menentukan jumlah dan tingkat keparahan jank dalam animasi aplikasi Anda, serta dapat digunakan untuk membuat proses yang lebih terperinci dalam menentukan performa Anda saat ini dan melacak tujuan performa ke depannya.

Referensi lainnya

Untuk mempelajari topik ini lebih lanjut, lihat referensi berikut.

Codelab

  • Codelab Pengujian Performa Otomatis menunjukkan kepada Anda cara menulis dan menjalankan pengujian otomatis serta meninjau hasilnya untuk memahami cara meningkatkan performa aplikasi Anda.