編譯快取

從 Android 10 開始,Neural Networks API (NNAPI) 提供多種 這會減少編譯用的時間 應用程式啟動時。使用這項快取功能時,驅動程式不會 因此不需管理或清除快取檔案這是可透過 NN HAL 1.2 實作的選用功能。如要進一步瞭解這個函式 看 ANeuralNetworksCompilation_setCaching

驅動程式也可以獨立於 NNAPI 之外實作編譯快取。這個 無論使用 NNAPI NDK 和 HAL 快取功能,都能實作或 而不是Android 開放原始碼計畫提供低階公用程式庫 (快取引擎)。詳情請參閱「實作快取引擎」。

工作流程總覽

本節將說明實作編譯快取功能的一般工作流程。

提供的快取資訊和快取命中

  1. 應用程式會傳送快取目錄和模型專屬的總和檢查碼。
  2. NNAPI 執行階段會根據總和檢查碼、執行偏好設定和區隔結果,尋找快取檔案。
  3. NNAPI 會開啟快取檔案,並透過 prepareModelFromCache 將句柄傳遞至驅動程式。
  4. 驅動程式會直接從快取檔案準備模型,並傳回已準備的模型。

提供的快取資訊和快取失敗

  1. 應用程式會傳遞模型和快取目錄的專屬總和檢查碼。
  2. NNAPI 執行階段會根據總和檢查碼尋找快取檔案 執行偏好設定,分區結果未找到 快取檔案。
  3. NNAPI 會根據總和檢查碼,建立空白的快取檔案 和分區功能,開啟快取檔案並傳遞 輸出方式和模型傳送給驅動程式 prepareModel_1_2
  4. 驅動程式會編譯模型,並將快取資訊寫入快取 檔案,並傳回準備好的模型

未提供快取資訊

  1. 應用程式會在未提供任何快取資訊的情況下,叫用編譯作業。
  2. 應用程式不會傳遞任何與快取相關的內容。
  3. NNAPI 執行階段會使用 prepareModel_1_2 將模型傳遞至驅動程式。
  4. 驅動程式會編譯模型,並傳回準備好的模型。

快取資訊

提供給驅動程式的快取資訊包含權杖和快取檔案句柄。

憑證

符記 為這個 BERT 模型 Constant::BYTE_SIZE_OF_CACHE_TOKEN 用於識別準備模型儲存 快取檔案以及使用 prepareModel_1_2 擷取準備的模型 prepareModelFromCache。驅動程式的用戶端應選擇含有 碰撞率低驅動程式無法偵測符記衝突。衝突會導致執行作業失敗,或執行作業成功但產生不正確的輸出值。

快取檔案句柄 (兩種快取檔案)

快取檔案分為兩種類型:「資料快取」和「模型快取」

  • 資料快取:用於快取常數資料,包括預先處理和 經過轉換的張量緩衝區修改資料快取時,除了在執行期間產生不良的輸出值,不應造成任何其他負面影響。
  • 模型快取:用於將安全性敏感資料 (例如已編譯的機器碼) 快取至裝置的原生二進位格式。A 罩杯 修改模型快取可能會影響驅動程式的執行作業 讓惡意用戶端利用這項功能執行 授予的權限因此,驅動程式必須先檢查模型快取是否已損毀,再從快取中準備模型。如需更多資訊 請參閱安全性

驅動程式必須決定快取資訊在兩者之間的分配方式 快取檔案類型,並回報每種類型需要多少快取檔案 同時 getNumberOfCacheFilesNeeded

NNAPI 執行階段一律會開啟快取檔案控制,同時提供讀取和寫入功能 權限。

安全性

在編譯快取中,模型快取可能包含安全性敏感資料,例如 視為已編譯的可執行機器碼,採用裝置的原生二進位格式。如果未妥善保護,修改模型快取可能會影響驅動程式的執行行為。因為快取內容是儲存在應用程式中 目錄後,用戶端就能修改快取檔案。錯誤用戶端可能會 不小心破壞快取,並且惡意用戶端可以刻意 以便在裝置上執行未經驗證的程式碼。根據用途 裝置特性,那麼這可能是安全性問題。因此, 驅動程式必須能夠偵測 從快取準備模型之前,可能會發生模型快取毀損的情況。

為達成此目的,其中一個方法是讓驅動程式從憑證到 模型快取的加密編譯雜湊值驅動程式可在將編譯內容儲存至快取時,儲存其模型快取的符記和雜湊。從快取中擷取編譯內容時,驅動程式會使用已記錄的符記和雜湊值配對,檢查模型快取的新雜湊值。這個對應關係必須具有 系統就會重新啟動。駕駛人可使用 Android KeyStore 服務是 Android 的公用程式庫 framework/ml/nn/driver/cache, 或任何其他合適的地圖管理員實作機制。駕駛時 必須重新初始化這個對應管理員,以免準備快取 檔案。

為避免時間檢查到使用時間 (TOCTOU) 攻擊,驅動程式必須在將記錄的雜湊值儲存至檔案前進行運算,並在將檔案內容複製到內部緩衝區後計算新的雜湊值。

以下程式碼範例示範如何實作此邏輯。

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;
    }
}

進階用途

在某些進階用途中,驅動程式需要在編譯呼叫後存取快取內容 (讀取或寫入)。用途範例包括:

  • 及時編譯:編譯會延遲到 首次執行的時間
  • 多階段編譯:一開始會執行快速編譯 稍後會執行選用的最佳化編譯 會視使用頻率而定

如要在編譯呼叫後存取快取內容 (讀取或寫入),請確定 駕駛人:

  • 在叫用 Google AI 時複製檔案控制代碼 prepareModel_1_2prepareModelFromCache,並讀取/更新快取 內容。
  • 在一般編譯呼叫之外實作檔案鎖定邏輯 防止寫入作業與讀取或其他寫入並行。

導入快取引擎

除了 NN HAL 1.2 編譯快取介面,您也可以在 frameworks/ml/nn/driver/cache 目錄中找到快取公用程式庫。nnCache 子目錄包含持續性儲存碼,可讓驅動程式實作編譯快取功能,而無需使用 NNAPI 快取功能。這種形式 編譯快取功能可以透過任何版本的 NN HAL 執行。如果 驅動程式選擇實作與 HAL 介面中斷連線的快取, 驅動程式是 不再需要快取構件