Cache kompilasi

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

Pengemudi juga dapat mengimplementasikan cache kompilasi yang independen dari NNAPI. Hal ini dapat diterapkan baik fitur caching NNAPI NDK dan HAL digunakan atau tidak. AOSP menyediakan perpustakaan utilitas tingkat rendah (mesin caching). Untuk informasi lebih lanjut, lihat Menerapkan mesin caching .

Ikhtisar alur kerja

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

Informasi cache disediakan dan cache tercapai

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

Informasi cache disediakan dan cache hilang

  1. Aplikasi meneruskan checksum unik untuk model dan direktori cache.
  2. Runtime NNAPI mencari file cache berdasarkan checksum, preferensi eksekusi, dan hasil partisi dan tidak menemukan file cache.
  3. NNAPI membuat file cache kosong berdasarkan checksum, preferensi eksekusi, dan partisi, membuka file cache, dan meneruskan pegangan dan model ke driver dengan prepareModel_1_2 .
  4. Pengemudi mengkompilasi model, menulis informasi cache ke file cache, dan mengembalikan model yang telah disiapkan.

Informasi cache tidak disediakan

  1. Aplikasi ini memanggil kompilasi tanpa memberikan informasi cache apa pun.
  2. Aplikasi ini tidak meneruskan apa pun yang terkait dengan cache.
  3. Runtime NNAPI meneruskan model ke driver dengan prepareModel_1_2 .
  4. Pengemudi mengkompilasi model dan mengembalikan model yang telah disiapkan.

Informasi cache

Informasi cache yang diberikan kepada driver terdiri dari token dan pegangan file cache.

Token

Token tersebut adalah token 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 telah disiapkan dengan prepareModelFromCache . Klien pengemudi harus memilih token dengan tingkat tabrakan yang rendah. Pengemudi tidak dapat mendeteksi tabrakan token. Tabrakan mengakibatkan eksekusi gagal atau eksekusi berhasil yang menghasilkan nilai output yang salah.

Pegangan file cache (dua jenis file cache)

Dua jenis file cache adalah cache data dan cache model .

  • Cache data: Digunakan untuk menyimpan data konstan termasuk buffer tensor yang telah diproses dan diubah. Modifikasi pada cache data tidak akan menghasilkan efek yang lebih buruk daripada menghasilkan nilai keluaran yang buruk pada waktu eksekusi.
  • Cache model: Digunakan untuk menyimpan data sensitif keamanan seperti kompilasi kode mesin yang dapat dieksekusi dalam format biner asli perangkat. Modifikasi pada cache model mungkin memengaruhi perilaku eksekusi driver, dan klien jahat dapat memanfaatkannya untuk mengeksekusi di luar izin yang diberikan. Oleh karena itu, pengemudi harus memeriksa apakah cache model rusak sebelum menyiapkan model dari cache. Untuk informasi lebih lanjut, lihat Keamanan .

Pengemudi harus memutuskan bagaimana informasi cache didistribusikan antara dua jenis file cache, dan melaporkan berapa banyak file cache yang diperlukan untuk setiap jenis dengan getNumberOfCacheFilesNeeded .

Runtime NNAPI selalu membuka pegangan file cache dengan izin baca dan tulis.

Keamanan

Dalam cache kompilasi, cache model mungkin berisi data sensitif keamanan seperti kompilasi kode mesin yang dapat dijalankan dalam format biner asli perangkat. Jika tidak dilindungi dengan benar, modifikasi pada cache model dapat mempengaruhi perilaku eksekusi driver. Karena konten cache disimpan di direktori aplikasi, file cache dapat dimodifikasi oleh klien. Klien yang bermasalah mungkin secara tidak sengaja merusak cache, dan klien jahat dapat dengan sengaja memanfaatkan ini untuk mengeksekusi kode yang belum diverifikasi pada perangkat. Tergantung pada karakteristik perangkat, ini mungkin merupakan masalah keamanan. Oleh karena itu, driver harus dapat mendeteksi potensi kerusakan cache model sebelum menyiapkan model dari cache.

Salah satu cara untuk melakukan ini adalah agar pengemudi mempertahankan peta dari token ke hash kriptografi dari cache model. Pengemudi dapat menyimpan token dan hash dari cache modelnya saat menyimpan kompilasi ke cache. Pengemudi memeriksa hash baru dari cache model dengan token dan pasangan hash yang direkam saat mengambil kompilasi dari cache. Pemetaan ini harus tetap ada saat sistem di-boot ulang. Pengemudi dapat menggunakan layanan keystore Android , pustaka utilitas dalam framework/ml/nn/driver/cache , atau mekanisme lain yang sesuai untuk mengimplementasikan manajer pemetaan. Setelah driver diperbarui, manajer 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 tingkat lanjut

Dalam kasus penggunaan tingkat lanjut tertentu, driver memerlukan akses ke konten cache (baca atau tulis) setelah panggilan kompilasi. Contoh kasus penggunaan meliputi:

  • Kompilasi just-in-time: Kompilasi ditunda hingga eksekusi pertama.
  • Kompilasi multi-tahap: Kompilasi cepat dilakukan pada awalnya dan kompilasi opsional yang dioptimalkan dilakukan di lain waktu tergantung pada frekuensi penggunaan.

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

  • Menggandakan pegangan 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 terjadi bersamaan dengan pembacaan atau penulisan lainnya.

Menerapkan mesin caching

Selain antarmuka caching kompilasi NN HAL 1.2, Anda juga dapat menemukan pustaka utilitas caching di direktori frameworks/ml/nn/driver/cache . Subdirektori nnCache berisi kode penyimpanan persisten bagi driver untuk mengimplementasikan cache kompilasi tanpa menggunakan fitur cache NNAPI. Bentuk cache kompilasi ini dapat diimplementasikan 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 ketika tidak lagi diperlukan.