API Petunjuk Performa

Dirilis:

Android 12 (API Level 31) - Performance Hint API

Android 13 (API Level 33) - Pengelola Petunjuk Performa di NDK API

(Pratinjau) Android 15 (DP1) - reportActualWorkDuration()

Dengan petunjuk performa CPU, game dapat memengaruhi perilaku performa CPU dinamis agar lebih sesuai dengan kebutuhannya. Pada sebagian besar perangkat, Android secara dinamis menyesuaikan kecepatan clock dan jenis core CPU untuk beban kerja berdasarkan permintaan sebelumnya. Jika beban kerja menggunakan lebih banyak resource CPU, kecepatan clock akan ditingkatkan dan beban kerja pada akhirnya dipindahkan ke core yang lebih besar. Jika beban kerja menggunakan lebih sedikit resource, Android akan menurunkan alokasi resource. Dengan ADPF, aplikasi atau game dapat mengirim sinyal tambahan tentang performa dan batas waktunya. Hal ini membantu sistem melakukan peningkatan secara lebih agresif (meningkatkan performa) dan menurunkan waktu dengan cepat saat beban kerja selesai (menghemat penggunaan daya).

Kecepatan clock

Saat perangkat Android menyesuaikan kecepatan clock CPU secara dinamis, frekuensi tersebut dapat mengubah performa kode Anda. Mendesain kode yang menangani kecepatan clock dinamis penting untuk memaksimalkan performa, mempertahankan status termal yang aman, dan menggunakan daya secara efisien. Anda tidak dapat langsung menetapkan frekuensi CPU dalam kode aplikasi. Akibatnya, cara umum yang digunakan aplikasi untuk mencoba berjalan pada kecepatan clock CPU yang lebih tinggi adalah dengan menjalankan loop yang sibuk di thread latar belakang sehingga beban kerja tampak lebih berat. Ini adalah praktik yang buruk karena menyia-nyiakan daya dan meningkatkan beban termal pada perangkat saat aplikasi tidak benar-benar menggunakan resource tambahan. CPU PerformanceHint API dirancang untuk mengatasi masalah ini. Dengan memberi tahu sistem tentang durasi kerja sebenarnya dan durasi kerja target, Android akan bisa mendapatkan ringkasan kebutuhan CPU aplikasi dan mengalokasikan resource secara efisien. Hal ini akan menghasilkan performa optimal pada tingkat konsumsi daya yang efisien.

Jenis core

Jenis core CPU yang digunakan untuk menjalankan game Anda adalah faktor performa yang penting. Perangkat Android sering mengubah core CPU yang ditetapkan ke thread secara dinamis berdasarkan perilaku beban kerja terbaru. Penetapan core CPU bahkan lebih kompleks pada SoC dengan beberapa jenis core. Pada beberapa perangkat ini, core yang lebih besar hanya dapat digunakan sebentar tanpa beralih ke kondisi yang tidak berkelanjutan secara termal.

Game Anda tidak boleh mencoba menetapkan afinitas core CPU karena alasan berikut:

  • Jenis core terbaik untuk beban kerja bervariasi menurut model perangkat.
  • Keberlanjutan menjalankan core yang lebih besar bervariasi menurut SoC dan berbagai solusi termal yang disediakan oleh setiap model perangkat.
  • Dampak lingkungan pada status termal dapat semakin mempersulit pilihan core. Misalnya, cuaca atau casing ponsel dapat mengubah status termal perangkat.
  • Pemilihan core tidak dapat mengakomodasi perangkat baru dengan performa tambahan dan kemampuan termal. Akibatnya, perangkat sering kali mengabaikan afinitas prosesor game.

Contoh perilaku penjadwal Linux default

Perilaku Penjadwal Linux
Gambar 1. Gubernur dapat memerlukan waktu ~200 md untuk meningkatkan atau menurunkan frekuensi CPU. ADPF berfungsi dengan sistem Dynamic Voltage dan Frequency Scaling (DVFS) untuk memberikan performa terbaik per watt

PerformanceHint API mengabstraksikan lebih dari latensi DVFS

ADPF Mengabstraksi lebih dari Latensi DVFS
Gambar 2. ADPF tahu cara membuat keputusan terbaik atas nama Anda
  • Jika tugas perlu dijalankan pada CPU tertentu, PerformanceHint API tahu cara membuat keputusan tersebut untuk Anda.
  • Oleh karena itu, Anda tidak perlu menggunakan afinitas.
  • Perangkat dilengkapi dengan berbagai topologi; Karakteristik daya dan termal terlalu bervariasi untuk diekspos kepada developer aplikasi.
  • Anda tidak dapat membuat asumsi tentang sistem pokok yang Anda gunakan.

Solusi

ADPF menyediakan class PerformanceHintManager sehingga game dapat mengirimkan petunjuk performa ke Android untuk kecepatan clock CPU dan jenis core. OS kemudian dapat memutuskan cara terbaik untuk menggunakan petunjuk berdasarkan SoC dan solusi termal perangkat. Jika aplikasi Anda menggunakan API ini beserta pemantauan status termal, aplikasi dapat memberikan petunjuk yang lebih tepat ke OS, bukan menggunakan loop sibuk dan teknik coding lainnya yang dapat menyebabkan throttling.

Berikut ini cara game menggunakan petunjuk performa:

  1. Buat sesi petunjuk untuk thread utama yang berperilaku serupa. Contoh:
    • Thread rendering dan dependensinya mendapatkan satu sesi
      1. Di Cocos, thread mesin utama dan thread render mendapatkan satu sesi
      2. Di Unity, integrasikan plugin Penyedia Android Performa Adaptif
      3. Di Unreal, integrasikan plugin Unreal Adaptive Performance dan gunakan opsi Skalabilitas untuk mendukung beberapa tingkat kualitas
    • Thread IO mendapatkan sesi satunya
    • Thread audio mendapatkan sesi ketiga
  2. Game harus melakukannya lebih awal, setidaknya 2 md, dan sebaiknya lebih dari 4 md sebelum sebuah sesi memerlukan peningkatan resource sistem.
  3. Di setiap sesi petunjuk, prediksikan durasi yang diperlukan untuk menjalankan setiap sesi. Durasi biasanya setara dengan interval frame, tetapi aplikasi dapat menggunakan interval yang lebih pendek jika beban kerja tidak bervariasi secara signifikan di seluruh frame.

Berikut adalah cara mempraktikkan teori:

Menginisialisasi PerformanceHintManager dan createHintSession

Dapatkan pengelola menggunakan layanan sistem dan buat sesi petunjuk untuk thread atau grup thread yang bekerja pada beban kerja yang sama.

C++

int32_t tids[1];
tids[0] = gettid();
int64_t target_fps_nanos = getFpsNanos();
APerformanceHintManager* hint_manager = APerformanceHint_getManager();
APerformanceHintSession* hint_session =
  APerformanceHint_createSession(hint_manager, tids, 1, target_fps_nanos);

Java

int[] tids = {
  android.os.Process.myTid()
};
long targetFpsNanos = getFpsNanos();
PerformanceHintManager performanceHintManager =
  (PerformanceHintManager) this.getSystemService(Context.PERFORMANCE_HINT_SERVICE);
PerformanceHintManager.Session hintSession =
  performanceHintManager.createHintSession(tids, targetFpsNanos);

Menetapkan thread jika perlu

Dirilis:

Android 11 (Level API 34)

Gunakan fungsi setThreads dari PerformanceHintManager.Session jika Anda memiliki thread lain yang perlu ditambahkan nanti. Misalnya, jika Anda membuat thread fisika nanti dan perlu menambahkannya ke sesi, Anda dapat menggunakan setThreads API ini.

C++

auto tids = thread_ids.data();
std::size_t size = thread_ids_.size();
APerformanceHint_setThreads(hint_session, tids, size);

Java

int[] tids = new int[3];

// add all your thread IDs. Remember to use android.os.Process.myTid() as that
// is the linux native thread-id.
// Thread.currentThread().getId() will not work because it is jvm's thread-id.
hintSession.setThreads(tids);

Jika menargetkan API Level yang lebih rendah, Anda harus menghancurkan sesi dan membuat ulang sesi baru setiap kali Anda perlu mengubah ID thread.

Melaporkan Durasi Pekerjaan yang Sebenarnya

Lacak durasi sebenarnya yang diperlukan untuk menyelesaikan pekerjaan dalam nanodetik dan melaporkannya ke sistem setelah penyelesaian pekerjaan di setiap siklus. Misalnya, jika ini untuk thread rendering, panggil thread ini di setiap frame.

Untuk mendapatkan waktu aktual dengan andal, gunakan:

C++

clock_gettime(CLOCK_MONOTONIC, &clock); // if you prefer "C" way from <time.h>
// or
std::chrono::high_resolution_clock::now(); // if you prefer "C++" way from <chrono>

Java

System.nanoTime();

Contoh:

C++

// All timings should be from `std::chrono::steady_clock` or `clock_gettime(CLOCK_MONOTONIC, ...)`
auto start_time = std::chrono::high_resolution_clock::now();

// do work

auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count();
int64_t actual_duration = static_cast<int64_t>(duration);

APerformanceHint_reportActualWorkDuration(hint_session, actual_duration);

Java

long startTime = System.nanoTime();

// do work

long endTime = System.nanoTime();
long duration = endTime - startTime;

hintSession.reportActualWorkDuration(duration);

Memperbarui Durasi Pekerjaan Target jika diperlukan

Setiap kali durasi kerja target Anda berubah, misalnya jika pemain memilih target fps yang berbeda, panggil metode updateTargetWorkDuration untuk memberi tahu sistem sehingga OS dapat menyesuaikan resource sesuai dengan target baru. Anda tidak perlu memanggilnya di setiap frame dan hanya perlu memanggilnya saat target durasi berubah.

C++

APerformanceHint_updateTargetWorkDuration(hint_session, target_duration);

Java

hintSession.updateTargetWorkDuration(targetDuration);