Bộ nhớ đệm biên dịch

Từ Android 10, API mạng thần kinh (NNAPI) cung cấp các chức năng hỗ trợ bộ nhớ đệm của các cấu phần phần mềm biên dịch, giúp giảm thời gian sử dụng để biên dịch khi ứng dụng khởi động. Khi sử dụng chức năng bộ nhớ đệm này, trình điều khiển không cần quản lý hoặc dọn sạch các tệp được lưu trong bộ nhớ đệm. Đây là một tính năng tùy chọn có thể được triển khai với NN HAL 1.2. Để biết thêm thông tin về chức năng này, hãy xem ANeuralNetworksCompilation_setCaching .

Trình điều khiển cũng có thể triển khai bộ nhớ đệm biên dịch độc lập với NNAPI. Điều này có thể được triển khai cho dù các tính năng bộ nhớ đệm NNAPI NDK và HAL có được sử dụng hay không. AOSP cung cấp thư viện tiện ích cấp thấp (công cụ lưu trữ). Để biết thêm thông tin, hãy xem Triển khai công cụ bộ đệm .

Tổng quan về quy trình làm việc

Phần này mô tả quy trình làm việc chung với tính năng bộ nhớ đệm biên dịch được triển khai.

Thông tin bộ đệm được cung cấp và lần truy cập bộ đệm

  1. Ứng dụng sẽ chuyển một thư mục bộ đệm và tổng kiểm tra duy nhất cho mô hình.
  2. Thời gian chạy NNAPI tìm kiếm các tệp bộ đệm dựa trên tổng kiểm tra, tùy chọn thực thi cũng như kết quả phân vùng và tìm các tệp.
  3. NNAPI mở các tệp bộ đệm và chuyển các thẻ điều khiển cho trình điều khiển bằng prepareModelFromCache .
  4. Trình điều khiển chuẩn bị mô hình trực tiếp từ các tệp bộ đệm và trả về mô hình đã chuẩn bị.

Thông tin bộ nhớ đệm được cung cấp và lỗi bộ nhớ đệm

  1. Ứng dụng sẽ vượt qua tổng kiểm tra duy nhất cho mô hình và thư mục bộ nhớ đệm.
  2. Thời gian chạy NNAPI tìm kiếm các tệp bộ nhớ đệm dựa trên tổng kiểm tra, tùy chọn thực thi cũng như kết quả phân vùng và không tìm thấy các tệp bộ nhớ đệm.
  3. NNAPI tạo các tệp bộ đệm trống dựa trên tổng kiểm tra, tùy chọn thực thi và phân vùng, mở các tệp bộ đệm và chuyển các thẻ điều khiển cũng như mô hình cho trình điều khiển bằng prepareModel_1_2 .
  4. Trình điều khiển biên dịch mô hình, ghi thông tin bộ đệm vào các tệp bộ đệm và trả về mô hình đã chuẩn bị.

Thông tin bộ nhớ đệm không được cung cấp

  1. Ứng dụng gọi quá trình biên dịch mà không cung cấp bất kỳ thông tin bộ nhớ đệm nào.
  2. Ứng dụng không chuyển gì liên quan đến bộ nhớ đệm.
  3. Thời gian chạy NNAPI chuyển mô hình cho trình điều khiển bằng prepareModel_1_2 .
  4. Trình điều khiển biên dịch mô hình và trả về mô hình đã chuẩn bị.

Thông tin bộ đệm

Thông tin bộ đệm được cung cấp cho trình điều khiển bao gồm mã thông báo và các thẻ xử lý tệp bộ đệm.

Mã thông báo

Mã thông báo là mã thông báo bộ đệm có độ dài Constant::BYTE_SIZE_OF_CACHE_TOKEN xác định mô hình đã chuẩn bị. Mã thông báo tương tự được cung cấp khi lưu tệp bộ nhớ đệm bằng prepareModel_1_2 và truy xuất mô hình đã chuẩn bị bằng prepareModelFromCache . Khách hàng của người lái xe nên chọn mã thông báo có tỷ lệ va chạm thấp. Trình điều khiển không thể phát hiện xung đột mã thông báo. Xung đột dẫn đến việc thực thi không thành công hoặc thực thi thành công tạo ra các giá trị đầu ra không chính xác.

Xử lý tệp bộ đệm (hai loại tệp bộ đệm)

Hai loại tệp bộ đệm là bộ đệm dữ liệubộ đệm mô hình .

  • Bộ đệm dữ liệu: Sử dụng để lưu vào bộ đệm dữ liệu không đổi bao gồm bộ đệm tensor được xử lý trước và chuyển đổi. Việc sửa đổi bộ nhớ đệm dữ liệu sẽ không gây ra bất kỳ ảnh hưởng nào tệ hơn việc tạo ra các giá trị đầu ra không hợp lệ tại thời điểm thực thi.
  • Bộ nhớ đệm mẫu: Sử dụng để lưu vào bộ nhớ đệm dữ liệu nhạy cảm về bảo mật, chẳng hạn như mã máy thực thi được biên dịch ở định dạng nhị phân gốc của thiết bị. Việc sửa đổi bộ đệm mô hình có thể ảnh hưởng đến hành vi thực thi của trình điều khiển và máy khách độc hại có thể lợi dụng điều này để thực thi ngoài quyền được cấp. Vì vậy, trình điều khiển phải kiểm tra xem bộ đệm mô hình có bị hỏng hay không trước khi chuẩn bị mô hình từ bộ đệm. Để biết thêm thông tin, hãy xem Bảo mật .

Trình điều khiển phải quyết định cách phân phối thông tin bộ đệm giữa hai loại tệp bộ đệm và báo cáo số lượng tệp bộ đệm cần thiết cho mỗi loại bằng getNumberOfCacheFilesNeeded .

Thời gian chạy NNAPI luôn mở các phần xử lý tệp bộ nhớ đệm với cả quyền đọc và ghi.

Bảo vệ

Trong bộ đệm ẩn biên dịch, bộ đệm mô hình có thể chứa dữ liệu nhạy cảm về bảo mật, chẳng hạn như mã máy thực thi được biên dịch ở định dạng nhị phân gốc của thiết bị. Nếu không được bảo vệ đúng cách, việc sửa đổi bộ đệm mô hình có thể ảnh hưởng đến hành vi thực thi của trình điều khiển. Vì nội dung bộ đệm được lưu trữ trong thư mục ứng dụng nên máy khách có thể sửa đổi các tệp bộ đệm. Máy khách bị lỗi có thể vô tình làm hỏng bộ đệm và máy khách độc hại có thể cố tình lợi dụng điều này để thực thi mã chưa được xác minh trên thiết bị. Tùy thuộc vào đặc điểm của thiết bị, đây có thể là vấn đề bảo mật. Do đó, trình điều khiển phải có khả năng phát hiện khả năng hỏng bộ nhớ đệm của mô hình trước khi chuẩn bị mô hình từ bộ nhớ đệm.

Một cách để thực hiện việc này là trình điều khiển duy trì bản đồ từ mã thông báo đến hàm băm mật mã của bộ nhớ đệm mô hình. Trình điều khiển có thể lưu trữ mã thông báo và hàm băm của bộ đệm mô hình khi lưu phần biên dịch vào bộ đệm. Trình điều khiển kiểm tra hàm băm mới của bộ đệm mô hình bằng cặp mã thông báo và hàm băm đã ghi khi truy xuất bản biên dịch từ bộ đệm. Ánh xạ này phải liên tục trong suốt quá trình khởi động lại hệ thống. Trình điều khiển có thể sử dụng dịch vụ kho khóa Android , thư viện tiện ích trong framework/ml/nn/driver/cache hoặc bất kỳ cơ chế phù hợp nào khác để triển khai trình quản lý ánh xạ. Sau khi cập nhật trình điều khiển, trình quản lý ánh xạ này phải được khởi tạo lại để ngăn việc chuẩn bị các tệp bộ đệm từ phiên bản cũ hơn.

Để ngăn chặn các cuộc tấn công theo thời gian kiểm tra theo thời gian sử dụng (TOCTOU), trình điều khiển phải tính toán hàm băm đã ghi trước khi lưu vào tệp và tính toán hàm băm mới sau khi sao chép nội dung tệp vào bộ đệm bên trong.

Mã mẫu này trình bày cách triển khai logic này.

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

Các trường hợp sử dụng nâng cao

Trong một số trường hợp sử dụng nâng cao nhất định, trình điều khiển yêu cầu quyền truy cập vào nội dung bộ đệm (đọc hoặc ghi) sau lệnh gọi biên dịch. Các trường hợp sử dụng ví dụ bao gồm:

  • Biên dịch đúng lúc: Quá trình biên dịch bị trì hoãn cho đến lần thực thi đầu tiên.
  • Quá trình biên dịch nhiều giai đoạn: Quá trình biên dịch nhanh được thực hiện ban đầu và quá trình biên dịch được tối ưu hóa tùy chọn sẽ được thực hiện sau đó tùy thuộc vào tần suất sử dụng.

Để truy cập nội dung bộ đệm (đọc hoặc ghi) sau lệnh gọi biên dịch, hãy đảm bảo rằng trình điều khiển:

  • Sao chép các phần xử lý tệp trong khi gọi prepareModel_1_2 hoặc prepareModelFromCache và đọc/cập nhật nội dung bộ đệm sau đó.
  • Triển khai logic khóa tệp bên ngoài lệnh gọi biên dịch thông thường để ngăn quá trình ghi xảy ra đồng thời với quá trình đọc hoặc ghi khác.

Triển khai công cụ bộ nhớ đệm

Ngoài giao diện bộ nhớ đệm biên dịch NN HAL 1.2, bạn cũng có thể tìm thấy thư viện tiện ích bộ nhớ đệm trong thư mục frameworks/ml/nn/driver/cache . Thư mục con nnCache chứa mã lưu trữ liên tục để trình điều khiển triển khai bộ nhớ đệm biên dịch mà không cần sử dụng các tính năng bộ nhớ đệm NNAPI. Hình thức bộ nhớ đệm biên dịch này có thể được triển khai với bất kỳ phiên bản nào của NN HAL. Nếu trình điều khiển chọn triển khai bộ nhớ đệm bị ngắt kết nối khỏi giao diện HAL, trình điều khiển có trách nhiệm giải phóng các thành phần lạ được lưu trong bộ nhớ đệm khi chúng không còn cần thiết nữa.