編譯快取

從 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. 驅動程式會編譯模型,並傳回準備好的模型。

快取資訊

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

憑證

權杖是用來識別已準備好的模型,長度為 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;
    }
}

進階用途

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

  • 即時編譯:編譯作業會延後至第一次執行時才進行。
  • 多階段編譯:一開始會執行快速編譯 稍後會執行選用的最佳化編譯 會視使用頻率而定

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

  • 在叫用 prepareModel_1_2prepareModelFromCache 時複製檔案控制代碼,並稍後讀取/更新快取內容。
  • 在一般編譯呼叫之外實作檔案上鎖邏輯,以免寫入作業與讀取或其他寫入作業同時發生。

實作快取引擎

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