Penyimpanan dalam cache kompilasi

Mulai Android 10, Neural Networks API (NNAPI) menyediakan fungsi untuk mendukung caching artefak kompilasi, yang mengurangi waktu yang digunakan untuk kompilasi saat aplikasi dimulai. Dengan menggunakan fungsi penyimpanan dalam cache ini, driver tidak perlu mengelola atau membersihkan file yang di-cache. Ini adalah fitur opsional yang dapat diterapkan dengan NN HAL 1.2. Untuk informasi selengkapnya tentang fungsi ini, lihat ANeuralNetworksCompilation_setCaching.

Driver juga dapat menerapkan cache kompilasi secara independen dari NNAPI. Hal ini dapat diterapkan baik jika fitur caching HAL dan NNAPI NDK digunakan maupun tidak. AOSP menyediakan library utilitas level rendah (mesin penyimpanan dalam cache). Untuk informasi selengkapnya, lihat Menerapkan mesin penyimpanan ke dalam cache.

Ringkasan alur kerja

Bagian ini menjelaskan alur kerja umum dengan fitur cache kompilasi yang diterapkan.

Informasi cache yang diberikan dan cache ditemukan

  1. Aplikasi meneruskan direktori penyimpanan dalam cache dan checksum yang unik untuk model.
  2. Runtime NNAPI mencari file cache berdasarkan checksum, preferensi eksekusi, dan hasil partisi, lalu menemukan file tersebut.
  3. NNAPI membuka file cache dan meneruskan handle ke driver dengan prepareModelFromCache.
  4. Pengemudi menyiapkan model langsung dari file cache dan menampilkan model yang disiapkan.

Informasi cache yang diberikan dan cache tidak ditemukan

  1. Aplikasi meneruskan checksum yang unik untuk model dan direktori caching.
  2. Runtime NNAPI mencari file penyimpanan dalam cache berdasarkan checksum, preferensi eksekusi, dan hasil partisi, serta tidak menemukan file cache.
  3. NNAPI membuat file cache kosong berdasarkan checksum, preferensi eksekusi, dan partisi, membuka file cache, dan meneruskan handle dan model ke driver dengan prepareModel_1_2.
  4. Pengemudi mengompilasi model, menulis informasi penyimpanan dalam cache ke file cache, dan menampilkan model yang disiapkan.

Informasi cache tidak diberikan

  1. Aplikasi memanggil kompilasi tanpa memberikan informasi penyimpanan dalam cache apa pun.
  2. Aplikasi tidak meneruskan apa pun yang terkait dengan penyimpanan dalam cache.
  3. Runtime NNAPI meneruskan model ke driver dengan prepareModel_1_2.
  4. Pengemudi mengompilasi model dan menampilkan model yang disiapkan.

Informasi cache

Informasi penyimpanan dalam cache yang diberikan ke driver terdiri dari token dan handle file cache.

Token

Token adalah token penyimpanan dalam cache 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 disiapkan dengan prepareModelFromCache. Klien driver harus memilih token dengan tingkat tabrakan yang rendah. Pengemudi tidak dapat mendeteksi konflik token. Tabrakan menghasilkan eksekusi yang gagal atau eksekusi yang berhasil yang menghasilkan nilai output yang salah.

Nama file cache (dua jenis file cache)

Dua jenis file cache adalah cache data dan cache model.

  • Cache data: Gunakan untuk menyimpan data konstan dalam cache, termasuk buffer tensor yang diproses sebelumnya dan ditransformasikan. Modifikasi pada cache data tidak boleh menghasilkan efek yang lebih buruk daripada menghasilkan nilai output yang buruk pada waktu eksekusi.
  • Cache model: Gunakan untuk meng-cache data sensitif keamanan seperti kode mesin yang dapat dieksekusi yang dikompilasi dalam format biner native perangkat. Perubahan pada cache model dapat memengaruhi perilaku eksekusi driver, dan klien berbahaya dapat memanfaatkannya untuk dieksekusi di luar izin yang diberikan. Dengan demikian, driver harus memeriksa apakah cache model rusak sebelum menyiapkan model dari cache. Untuk informasi selengkapnya, lihat Keamanan.

Pengemudi harus memutuskan cara informasi cache didistribusikan 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 penyimpanan dalam cache kompilasi, cache model dapat berisi data sensitif 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 bermasalah dapat secara tidak sengaja merusak cache, dan klien berbahaya dapat sengaja menggunakannya untuk mengeksekusi kode yang tidak terverifikasi di perangkat. Bergantung pada karakteristik perangkat, hal ini mungkin merupakan masalah keamanan. Dengan demikian, driver harus dapat mendeteksi kemungkinan kerusakan cache model sebelum menyiapkan model dari cache.

Salah satu cara untuk melakukannya adalah dengan meminta driver untuk mempertahankan peta dari token ke hash kriptografis cache model. Pengemudi dapat menyimpan token dan hash cache modelnya saat menyimpan kompilasi ke cache. Pengemudi memeriksa hash baru cache model dengan token yang dicatat dan pasangan hash saat mengambil kompilasi dari cache. Pemetaan ini harus tetap ada di seluruh sistem yang dimulai ulang. Pengemudi dapat menggunakan layanan keystore Android, library utilitas di framework/ml/nn/driver/cache, atau mekanisme lain yang sesuai untuk menerapkan pengelola pemetaan. Setelah driver diupdate, 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 opsional yang dioptimalkan dilakukan di lain waktu bergantung pada frekuensi penggunaan.

Untuk mengakses konten cache (baca atau tulis) setelah panggilan kompilasi, pastikan driver:

  • Menduplikasi handle file selama pemanggilan prepareModel_1_2 atau prepareModelFromCache dan membaca/memperbarui konten cache di lain waktu.
  • Mengimplementasikan logika penguncian file di luar panggilan kompilasi biasa untuk mencegah penulisan yang terjadi secara serentak dengan pembacaan atau penulisan lainnya.

Mengimplementasikan mesin penyimpanan ke 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 bagi driver untuk menerapkan caching kompilasi tanpa menggunakan fitur caching NNAPI. Bentuk cache kompilasi ini dapat diterapkan dengan versi NN HAL apa pun. Jika driver memilih untuk menerapkan penyimpanan dalam cache yang terputus dari antarmuka HAL, driver bertanggung jawab untuk mengosongkan artefak yang di-cache jika tidak lagi diperlukan.