Mulai Android 10, Neural Networks API (NNAPI)
menyediakan fungsi untuk mendukung
penyimpanan artefak kompilasi dalam cache, yang mengurangi waktu yang digunakan untuk kompilasi
saat aplikasi dimulai. Dengan menggunakan fungsi caching ini, driver tidak
perlu mengelola atau membersihkan file yang di-cache. Ini adalah fitur opsional yang
dapat diimplementasikan dengan NN HAL 1.2. Untuk mengetahui informasi selengkapnya tentang fungsi ini, lihat
ANeuralNetworksCompilation_setCaching
.
Driver juga dapat menerapkan caching kompilasi secara terpisah dari NNAPI. Hal ini dapat diterapkan baik saat fitur caching NNAPI NDK dan HAL digunakan maupun tidak. AOSP menyediakan library utilitas tingkat rendah (mesin caching). Untuk mengetahui informasi selengkapnya, lihat Menerapkan mesin penyimpanan ke dalam cache.
Ringkasan alur kerja
Bagian ini menjelaskan alur kerja umum dengan fitur caching kompilasi yang diterapkan.
Informasi cache diberikan dan cache ditemukan
- Aplikasi meneruskan direktori penyimpanan dalam cache dan checksum yang unik untuk model.
- Runtime NNAPI mencari file cache berdasarkan checksum, preferensi eksekusi, dan hasil partisi, lalu menemukan file tersebut.
- NNAPI membuka file cache dan meneruskan handle ke driver dengan
prepareModelFromCache
. - Driver menyiapkan model langsung dari file cache dan menampilkan model yang telah disiapkan.
Informasi cache diberikan dan cache tidak ditemukan
- Aplikasi meneruskan checksum unik untuk model dan direktori penyimpanan dalam cache.
- Runtime NNAPI mencari file caching berdasarkan checksum, preferensi eksekusi, dan hasil partisi, tetapi tidak menemukan file cache.
- NNAPI membuat file cache kosong berdasarkan checksum, preferensi
eksekusi, dan partisi, membuka file cache, serta meneruskan
handle dan model ke driver dengan
prepareModel_1_2
. - Driver mengompilasi model, menulis informasi caching ke file cache, dan menampilkan model yang telah disiapkan.
Informasi cache tidak diberikan
- Aplikasi memanggil kompilasi tanpa memberikan informasi caching apa pun.
- Aplikasi tidak meneruskan apa pun yang terkait dengan caching.
- Runtime NNAPI meneruskan model ke driver dengan
prepareModel_1_2
. - Driver mengompilasi model dan menampilkan model yang telah disiapkan.
Informasi cache
Informasi caching yang diberikan kepada driver terdiri dari token dan handle file cache.
Token
Token
adalah token caching dengan panjang
Constant::BYTE_SIZE_OF_CACHE_TOKEN
yang mengidentifikasi model yang disiapkan. Token yang sama diberikan saat menyimpan file cache dengan prepareModel_1_2
dan mengambil model yang telah disiapkan dengan prepareModelFromCache
. Klien pengemudi harus memilih token dengan tingkat tabrakan yang rendah. Driver tidak dapat mendeteksi tabrakan token. Tabrakan
menyebabkan eksekusi gagal atau eksekusi berhasil yang menghasilkan
nilai output yang salah.
Menangani file cache (dua jenis file cache)
Dua jenis file cache adalah cache data dan cache model.
- Cache data: Digunakan untuk menyimpan data konstan dalam cache, termasuk buffer tensor yang telah diproses dan ditransformasikan. Modifikasi pada cache data tidak boleh menghasilkan efek yang lebih buruk daripada menghasilkan nilai output yang buruk pada waktu eksekusi.
- Cache model: Digunakan untuk menyimpan data sensitif keamanan dalam cache seperti kode mesin yang dapat dieksekusi yang dikompilasi dalam format biner native perangkat. Modifikasi pada cache model dapat memengaruhi perilaku eksekusi driver, dan klien berbahaya dapat memanfaatkan hal ini untuk melakukan eksekusi di luar izin yang diberikan. Oleh karena itu, driver harus memeriksa apakah cache model rusak sebelum menyiapkan model dari cache. Untuk mengetahui informasi selengkapnya, lihat Keamanan.
Driver harus memutuskan cara mendistribusikan informasi cache di antara dua
jenis file cache, dan melaporkan jumlah file cache yang diperlukan untuk setiap jenis
dengan
getNumberOfCacheFilesNeeded
.
Runtime NNAPI selalu membuka handle file cache dengan izin baca dan tulis.
Keamanan
Dalam caching kompilasi, cache model dapat berisi data yang sensitif terhadap keamanan, seperti kode mesin yang dapat dieksekusi yang dikompilasi dalam format biner native perangkat. Jika tidak dilindungi dengan benar, modifikasi pada cache model dapat memengaruhi perilaku eksekusi driver. Karena konten cache disimpan di direktori aplikasi, file cache dapat diubah oleh klien. Klien yang penuh bug dapat merusak cache secara tidak sengaja, dan klien berbahaya dapat sengaja menggunakan hal ini untuk mengeksekusi kode yang tidak terverifikasi di perangkat. Bergantung pada karakteristik perangkat, hal ini dapat menjadi masalah keamanan. Oleh karena itu, driver harus dapat mendeteksi potensi kerusakan cache model sebelum menyiapkan model dari cache.
Salah satu cara untuk melakukannya adalah dengan meminta driver mempertahankan peta dari token ke hash kriptografi cache model. Driver dapat menyimpan token dan hash cache modelnya saat menyimpan kompilasi ke cache. Driver memeriksa hash baru cache model dengan pasangan hash dan token yang tercatat saat mengambil kompilasi dari cache. Pemetaan ini harus tetap ada saat sistem dimulai ulang. Driver dapat menggunakan
layanan keystore Android, library utilitas di
framework/ml/nn/driver/cache
,
atau mekanisme lain yang sesuai untuk menerapkan pengelola pemetaan. Setelah update driver, pengelola pemetaan ini harus diinisialisasi ulang untuk mencegah penyiapan file cache dari versi sebelumnya.
Untuk mencegah serangan time-of-check to time-of-use (TOCTOU), driver harus menghitung hash yang direkam sebelum menyimpan ke file dan menghitung hash baru setelah menyalin konten file ke buffer internal.
Kode contoh ini menunjukkan cara menerapkan logika ini.
bool saveToCache(const sp<V1_2::IPreparedModel> preparedModel,
const hidl_vec<hidl_handle>& modelFds, const hidl_vec<hidl_handle>& dataFds,
const HidlToken& token) {
// Serialize the prepared model to internal buffers.
auto buffers = serialize(preparedModel);
// This implementation detail is important: the cache hash must be computed from internal
// buffers instead of cache files to prevent time-of-check to time-of-use (TOCTOU) attacks.
auto hash = computeHash(buffers);
// Store the {token, hash} pair to a mapping manager that is persistent across reboots.
CacheManager::get()->store(token, hash);
// Write the cache contents from internal buffers to cache files.
return writeToFds(buffers, modelFds, dataFds);
}
sp<V1_2::IPreparedModel> prepareFromCache(const hidl_vec<hidl_handle>& modelFds,
const hidl_vec<hidl_handle>& dataFds,
const HidlToken& token) {
// Copy the cache contents from cache files to internal buffers.
auto buffers = readFromFds(modelFds, dataFds);
// This implementation detail is important: the cache hash must be computed from internal
// buffers instead of cache files to prevent time-of-check to time-of-use (TOCTOU) attacks.
auto hash = computeHash(buffers);
// Validate the {token, hash} pair by a mapping manager that is persistent across reboots.
if (CacheManager::get()->validate(token, hash)) {
// Retrieve the prepared model from internal buffers.
return deserialize<V1_2::IPreparedModel>(buffers);
} else {
return nullptr;
}
}
Kasus penggunaan lanjutan
Dalam kasus penggunaan lanjutan tertentu, driver memerlukan akses ke konten cache (baca atau tulis) setelah panggilan kompilasi. Contoh kasus penggunaan mencakup:
- Kompilasi tepat waktu: Kompilasi ditunda hingga eksekusi pertama.
- Kompilasi multi-tahap: Kompilasi cepat dilakukan pada awalnya dan kompilasi yang dioptimalkan opsional dilakukan di lain waktu, bergantung pada frekuensi penggunaan.
Untuk mengakses konten cache (baca atau tulis) setelah panggilan kompilasi, pastikan bahwa driver:
- Menduplikasi handle file selama pemanggilan
prepareModel_1_2
atauprepareModelFromCache
dan membaca/memperbarui konten cache di lain waktu. - Menerapkan logika penguncian file di luar panggilan kompilasi biasa untuk mencegah penulisan terjadi secara bersamaan dengan pembacaan atau penulisan lain.
Menerapkan mesin penyimpanan dalam cache
Selain antarmuka caching kompilasi NN HAL 1.2, Anda juga dapat menemukan library utilitas caching di direktori
frameworks/ml/nn/driver/cache
. Subdirektori
nnCache
berisi kode penyimpanan persisten agar driver dapat menerapkan
penyimpanan cache kompilasi tanpa menggunakan fitur penyimpanan cache NNAPI. Caching kompilasi
ini dapat diterapkan dengan versi NN HAL apa pun. Jika
driver memilih untuk menerapkan caching yang terputus dari antarmuka HAL,
driver bertanggung jawab
untuk membebaskan artefak yang di-cache saat tidak lagi diperlukan.