Pengoptimalan yang dipandu profil (Profile-guided optimization/PGO) adalah teknik pengoptimalan compiler yang sudah banyak dikenal. Dalam PGO, profil runtime dari eksekusi program digunakan oleh compiler untuk membuat pilihan yang optimal terkait inline dan tata letak kode. Hal ini dapat meningkatkan performa dan mengurangi ukuran kode.
PGO dapat di-deploy ke aplikasi atau library Anda dengan langkah-langkah berikut: 1. Mengidentifikasi beban kerja representatif. 2. Mengumpulkan profil. 3. Menggunakan profil di build Rilis.
Langkah 1: Mengidentifikasi Beban Kerja Representatif
Pertama, identifikasi beban kerja atau benchmark yang cukup mewakili aplikasi Anda. Langkah ini penting karena profil yang dikumpulkan dari beban kerja mengidentifikasi area yang panas dan dingin dalam kode. Saat menggunakan profil, compiler akan melakukan pengoptimalan agresif dan inline di area panas. Compiler juga dapat memilih untuk mengurangi ukuran kode area dingin saat menurunkan performa.
Mengidentifikasi beban kerja yang baik juga berguna untuk memantau performa secara umum.
Langkah 2: Mengumpulkan Profil
Pengumpulan profil melibatkan tiga langkah: - membuat kode native dengan instrumentasi, - menjalankan aplikasi berinstrumen di perangkat dan membuat profil, serta - menggabungkan/melanjutkan pemrosesan profil pada host.
Membuat Build Berinstrumen
Profil dikumpulkan dengan menjalankan beban kerja dari langkah 1 pada
build aplikasi yang berinstrumen. Untuk membuat build berinstrumen, tambahkan
-fprofile-generate
ke tanda compiler dan linker. Tanda ini harus
dikontrol oleh variabel build terpisah karena tanda ini tidak diperlukan selama
build default.
Membuat Profil
Berikutnya, jalankan aplikasi berinstrumen pada perangkat dan buat profil.
Profil dikumpulkan di memori saat biner berinstrumen dijalankan dan ditulis
ke file saat keluar. Namun, fungsi yang terdaftar dengan atexit
tidak akan
dipanggil di aplikasi Android dan aplikasi akan dihentikan.
Aplikasi/beban kerja harus melakukan pekerjaan tambahan untuk menetapkan jalur bagi file profil, lalu memicu penulisan profil secara eksplisit.
- Untuk menetapkan jalur file profil, panggil
__llvm_profile_set_filename(PROFILE_DIR "/default-%m.profraw
.%m
berguna saat ada beberapa library bersama.%m
diperluas ke tanda tangan modul unik untuk library tersebut, yang menghasilkan profil terpisah per library. Lihat di sini untuk penentu pola berguna lainnya.PROFILE_DIR
adalah direktori yang dapat ditulis dari aplikasi. Lihat demo untuk mendeteksi direktori ini saat runtime. - Untuk memicu penulisan profil secara eksplisit, panggil fungsi
__llvm_profile_write_file
.
extern "C" {
extern int __llvm_profile_set_filename(const char*);
extern int __llvm_profile_write_file(void);
}
#define PROFILE_DIR "<location-writable-from-app>"
void workload() {
// ...
// run workload
// ...
// set path and write profiles after workload execution
__llvm_profile_set_filename(PROFILE_DIR "/default-%m.profraw");
__llvm_profile_write_file();
return;
}
Catatan: Membuat file profil akan lebih mudah jika beban kerja adalah biner mandiri,
cukup setel variabel lingkungan LLVM_PROFILE_FILE
ke %t/default-%m.profraw
sebelum menjalankan biner.
Melanjutkan Pemrosesan Profil
File profil menggunakan format .profraw. File tersebut harus diambil terlebih dahulu dari
perangkat menggunakan adb pull
. Setelah pengambilan, gunakan utilitas llvm-profdata
di
NDK untuk mengonversi dari .profraw
menjadi .profdata
, yang kemudian dapat diteruskan ke
compiler.
$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-profdata \
merge --output=pgo_profile.profdata \
<list-of-profraw-files>
Gunakan llvm-profdata
dan clang
dari rilis NDK yang sama untuk menghindari ketidakcocokan
versi format file profil.
Langkah 3: Menggunakan Profil untuk Mem-build Aplikasi
Gunakan profil dari langkah sebelumnya selama merilis build
aplikasi Anda dengan meneruskan -fprofile-use=<>.profdata
ke compiler dan linker. Profil
dapat digunakan bahkan saat kode berkembang — compiler Clang dapat menoleransi
sedikit ketidakcocokan antara sumber dan profil.
Catatan: Secara umum, untuk sebagian besar library, profil bersifat umum di seluruh arsitektur. Misalnya, profil yang dihasilkan dari build arm64 library dapat digunakan untuk semua arsitektur. Masalahnya adalah jika ada jalur kode khusus arsitektur tertentu di library (arm vs x86 atau 32-bit vs 64-bit), profil terpisah harus digunakan untuk setiap konfigurasi tersebut.
Penutup
https://github.com/DanAlbert/ndk-samples/tree/pgo/pgo menunjukkan demo menyeluruh penggunaan PGO dari aplikasi. Demo ini memberikan detail tambahan yang dibahas sekilas dalam dokumen ini.
- Aturan build CMake menunjukkan cara menyiapkan variabel CMake yang membuat kode native dengan instrumentasi. Saat variabel build tidak disetel, kode native akan dioptimalkan menggunakan profil PGO yang dibuat sebelumnya.
- Pada build berinstrumen, pgodemo.cpp akan menulis profil sebagai eksekusi beban kerja.
- Lokasi yang dapat ditulis untuk profil diperoleh saat runtime di
MainActivity.kt
menggunakan
applicationContext.cacheDir.toString()
. - Untuk mengambil profil dari perangkat tanpa memerlukan
adb root
, gunakan urutan langkahadb
di sini.