Cache kompilasi

Dari 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 {i>caching<i} ini, {i>driver<i} tidak perlu mengelola atau membersihkan file {i>cache<i}. Ini adalah fitur opsional yang dapat diimplementasikan dengan NN HAL 1.2. Untuk informasi selengkapnya tentang fungsi ini, lihat ANeuralNetworksCompilation_setCaching

Driver juga dapat mengimplementasikan cache kompilasi secara terpisah dari NNAPI. Ini dapat diimplementasikan apakah fitur caching NNAPI NDK dan HAL digunakan atau tidak. AOSP menyediakan library utilitas tingkat rendah (mesin caching). Untuk selengkapnya informasi selengkapnya, lihat Mengimplementasikan mesin cache.

Ringkasan alur kerja

Bagian ini menjelaskan alur kerja umum dengan fitur cache kompilasi diimplementasikan.

Informasi cache yang diberikan dan cache ditemukan

  1. Aplikasi meneruskan direktori caching dan checksum unik ke model tersebut.
  2. Runtime NNAPI mencari file cache berdasarkan checksum, preferensi eksekusi, dan hasil partisi dan menemukan file.
  3. NNAPI membuka file cache dan meneruskan handle ke driver dengan prepareModelFromCache
  4. Driver menyiapkan model langsung dari file cache dan mengembalikan model yang sudah disiapkan.

Informasi cache yang diberikan dan cache tidak ditemukan

  1. Aplikasi meneruskan checksum unik ke model dan cache saat ini.
  2. Runtime NNAPI mencari file cache berdasarkan checksum, preferensi eksekusi, dan hasil partisi dan tidak menemukan file {i>cache<i}.
  3. NNAPI membuat file cache kosong berdasarkan checksum, eksekusi preferensi, dan partisi, membuka file {i>cache<i}, dan meneruskan dan model ke {i>driver<i} dengan prepareModel_1_2
  4. Driver mengompilasi model, menulis informasi caching ke cache file, dan menampilkan model yang disiapkan.

Informasi cache tidak diberikan

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

Informasi cache

Informasi {i>caching<i} yang diberikan ke {i>driver<i} terdiri dari token dan menangani file cache.

Token

Tujuan token adalah token caching panjang Constant::BYTE_SIZE_OF_CACHE_TOKEN yang mengidentifikasi model yang disiapkan. Token yang sama diberikan saat menyimpan menyimpan file cache dengan prepareModel_1_2, dan mengambil model yang telah disiapkan prepareModelFromCache. Klien {i>driver<i} harus memilih token dengan tingkat tumbukan yang rendah. Pengemudi tidak dapat mendeteksi tabrakan token. Tabrakan mengakibatkan gagalnya eksekusi, atau keberhasilan eksekusi yang menghasilkan nilai output yang salah.

Penanganan file cache (dua jenis file cache)

Dua jenis file cache adalah cache data dan cache model.

  • Cache data: Gunakan untuk menyimpan data konstanta dalam cache, termasuk data yang telah diproses sebelumnya dan buffer tensor yang ditransformasi. Modifikasi pada {i>cache<i} data tidak mengakibatkan efek yang lebih buruk daripada menghasilkan nilai output yang buruk saat eksekusi baik.
  • Cache model: Gunakan untuk meng-cache data yang sensitif terhadap keamanan seperti kompilasi kode mesin yang dapat dieksekusi dalam format biner native perangkat. J modifikasi pada cache model dapat memengaruhi eksekusi driver yang berbahaya, dan klien berbahaya dapat memanfaatkannya untuk mengeksekusi lebih dari izin yang diberikan. Dengan demikian, driver harus memeriksa apakah cache model rusak sebelum menyiapkan model dari cache. Untuk informasi selengkapnya, lihat Keamanan.

{i>Driver<i} harus memutuskan bagaimana informasi {i>cache<i} didistribusikan di antara keduanya jenis file {i>cache<i}, dan melaporkan berapa banyak file {i>cache<i} yang dibutuhkan untuk setiap jenis dengan getNumberOfCacheFilesNeeded

Runtime NNAPI selalu membuka penanganan file cache dengan operasi baca dan tulis izin akses.

Keamanan

Dalam cache kompilasi, cache model mungkin berisi data yang sensitif terhadap keamanan seperti sebagai kompilasi kode mesin yang dapat dieksekusi dalam format biner bawaan perangkat. Jika tidak dilindungi dengan benar, modifikasi cache model dapat memengaruhi perilaku eksekusi. Karena konten cache disimpan di aplikasi file {i>cache<i} dapat diubah oleh klien. Klien yang memiliki bug mungkin merusak {i>cache<i} secara tidak sengaja, dan klien yang jahat dapat dengan sengaja membuat menggunakannya untuk mengeksekusi kode yang belum diverifikasi pada perangkat. Bergantung pada karakteristik perangkat, hal ini mungkin menjadi masalah keamanan. Dengan demikian, pengemudi harus dapat mendeteksi potensi kerusakan cache model sebelum menyiapkan model dari cache.

Salah satu cara untuk melakukannya adalah pengemudi memelihara peta dari token ke yang merupakan hash kriptografis cache model. {i>Driver<i} dapat menyimpan token dan yang digunakan untuk membuat hash cache modelnya saat menyimpan kompilasi ke cache. Pengemudi memeriksa {i>hash <i}baru dari cache model dengan token dan pasangan hash yang dicatat ketika mengambil kompilasi dari cache. Pemetaan ini harus persisten di seluruh memulai ulang sistem. Pengemudi dapat menggunakan Layanan keystore Android, library utilitas di framework/ml/nn/driver/cache, atau mekanisme lain yang sesuai untuk menerapkan pengelola pemetaan. Saat pengemudi memperbarui, pengelola pemetaan ini harus diinisialisasi ulang untuk mencegah persiapan cache {i>file<i} dari versi sebelumnya.

Untuk mencegah waktu pemeriksaan hingga waktu penggunaan (TOCTOU), pengemudi harus menghitung hash yang dicatat sebelum menyimpan ke file dan hitung {i>hash<i} baru setelah menyalin isi file ke {i>buffer <i}(penyangga).

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 proses eksekusi pertama.
  • Kompilasi multi-tahap: Kompilasi cepat dilakukan terlebih dahulu dan kompilasi opsional yang dioptimalkan dilakukan di lain waktu tergantung pada frekuensi penggunaannya.

Untuk mengakses konten cache (baca atau tulis) setelah panggilan kompilasi, pastikan bahwa {i>driver<i}:

  • Menduplikasi nama sebutan channel selama pemanggilan prepareModel_1_2 atau prepareModelFromCache dan membaca/memperbarui cache konten di lain waktu.
  • Mengimplementasikan logika penguncian file di luar panggilan kompilasi biasa untuk mencegah penulisan terjadi bersamaan dengan operasi baca atau tulis lainnya.

Mengimplementasikan mesin cache

Selain antarmuka cache kompilasi NN HAL 1.2, Anda juga dapat menemukan {i>caching library<i} utilitas di frameworks/ml/nn/driver/cache saat ini. Tujuan nnCache berisi kode penyimpanan persisten yang harus diimplementasikan oleh driver pembuatan cache kompilasi tanpa menggunakan fitur caching NNAPI. Bentuk cache kompilasi dapat diimplementasikan dengan NN HAL versi apa pun. Jika {i>driver<i} memilih untuk mengimplementasikan {i>caching<i} yang terputus dari antarmuka HAL, {i>driver-<i}nya bertanggung jawab untuk membebaskan artefak yang di-{i>cache<i} saat tidak lagi dibutuhkan.