Nhóm bộ nhớ

Trang này mô tả các cấu trúc dữ liệu và phương thức dùng để giao tiếp hiệu quả vùng đệm toán hạng giữa trình điều khiển và khung.

Tại thời điểm biên dịch mô hình, khung sẽ cung cấp các giá trị của toán hạng không đổi cho trình điều khiển. Tuỳ thuộc vào vòng đời của toán hạng hằng, các giá trị của toán hạng đó sẽ nằm trong vectơ HIDL hoặc nhóm bộ nhớ dùng chung.

  • Nếu vòng đời là CONSTANT_COPY, thì các giá trị sẽ nằm trong trường operandValues của cấu trúc mô hình. Vì các giá trị trong vectơ HiDL được sao chép trong quá trình giao tiếp liên quy trình (IPC), nên các giá trị này thường chỉ dùng để chứa một lượng nhỏ dữ liệu như toán hạng vô hướng (ví dụ: vô hướng kích hoạt trong ADD) và các tham số tensor nhỏ (ví dụ: tensor hình dạng trong RESHAPE).
  • Nếu vòng đời là CONSTANT_REFERENCE, thì các giá trị sẽ nằm trong trường pools của cấu trúc mô hình. Chỉ các xử lý của nhóm bộ nhớ dùng chung mới được sao chép trong IPC thay vì sao chép các giá trị thô. Do đó, việc lưu giữ một lượng lớn dữ liệu (ví dụ: tham số trọng số trong các phép tích chập) sẽ hiệu quả hơn bằng cách sử dụng nhóm bộ nhớ dùng chung so với vectơ HIDL.

Tại thời điểm thực thi mô hình, khung sẽ cung cấp vùng đệm của các toán hạng đầu vào và đầu ra cho trình điều khiển. Không giống như các hằng số thời gian biên dịch có thể được gửi trong vectơ HIDL, dữ liệu đầu vào và đầu ra của quá trình thực thi luôn được giao tiếp thông qua một tập hợp các nhóm bộ nhớ.

Loại dữ liệu HIDL hidl_memory được dùng trong cả biên dịch và thực thi để đại diện cho một nhóm bộ nhớ dùng chung chưa được ánh xạ. Trình điều khiển phải liên kết bộ nhớ sao cho phù hợp để có thể sử dụng bộ nhớ dựa trên tên của loại dữ liệu hidl_memory. Sau đây là các tên bộ nhớ được hỗ trợ:

  • ashmem: Kỷ niệm dùng chung trên Android. Để biết thêm chi tiết, hãy xem bài viết về bộ nhớ.
  • mmap_fd: Bộ nhớ dùng chung được hỗ trợ bởi chỉ số mô tả tệp thông qua mmap.
  • hardware_buffer_blob: Bộ nhớ dùng chung được AHardwareBuffer hỗ trợ với định dạng AHARDWARE_BUFFER_FORMAT_BLOB. Có sẵn từ Mạng nơron (NN) HAL 1.2. Để biết thêm thông tin, hãy xem bài viết AHardwareBuffer.
  • hardware_buffer: Bộ nhớ dùng chung do AHardwareBuffer hỗ trợ chung không sử dụng định dạng AHARDWARE_BUFFER_FORMAT_BLOB. Vùng đệm phần cứng của chế độ không phải BLOB chỉ được hỗ trợ trong quá trình thực thi mô hình.Có trong NN HAL 1.2. Để biết thêm thông tin, hãy xem bài viết AHardwareBuffer.

Từ NN HAL 1.3, NNAPI hỗ trợ các miền bộ nhớ cung cấp giao diện bộ phân bổ (allocator) cho các vùng đệm do trình điều khiển quản lý. Các vùng đệm do trình điều khiển quản lý cũng có thể được dùng làm đầu vào hoặc đầu ra của quá trình thực thi. Để biết thêm thông tin chi tiết, hãy xem phần Miền bộ nhớ.

Trình điều khiển NNAPI phải hỗ trợ ánh xạ tên bộ nhớ ashmemmmap_fd. Từ NN HAL 1.3, trình điều khiển cũng phải hỗ trợ hoạt động ánh xạ hardware_buffer_blob. Không bắt buộc phải hỗ trợ chế độ chung không phải BLOB hardware_buffer và miền bộ nhớ.

Bộ đệm phần cứng

AHardwareBuffer là một loại bộ nhớ dùng chung bao bọc vùng đệm Gralloc. Trong Android 10, Neural Networks API (NNAPI) hỗ trợ việc sử dụng AHardwareBuffer, cho phép trình điều khiển thực hiện quá trình thực thi mà không cần sao chép dữ liệu, nhờ đó cải thiện hiệu suất và mức tiêu thụ điện năng cho ứng dụng. Ví dụ: ngăn xếp HAL của máy ảnh có thể truyền các đối tượng AHardwareBuffer đến NNAPI cho khối lượng công việc học máy bằng cách sử dụng các tay cầm AHardwareBuffer do API NDK của máy ảnh và API NDK nội dung đa phương tiện tạo ra. Để biết thêm thông tin, hãy xem ANeuralNetworksMemory_createFromAHardwareBuffer.

Các đối tượng AHardwareBuffer dùng trong NNAPI được truyền tới trình điều khiển thông qua một cấu trúc hidl_memory có tên là hardware_buffer hoặc hardware_buffer_blob. Cấu trúc hidl_memory hardware_buffer_blob chỉ biểu thị các đối tượng AHardwareBuffer có định dạng AHARDWAREBUFFER_FORMAT_BLOB.

Thông tin mà khung yêu cầu được mã hoá trong trường hidl_handle của cấu trúc hidl_memory. Trường hidl_handle gói native_handle để mã hoá tất cả siêu dữ liệu bắt buộc về bộ đệm AHardwareBuffer hoặc Gralloc.

Trình điều khiển phải giải mã đúng cách trường hidl_handle được cung cấp và truy cập vào bộ nhớ mà hidl_handle mô tả. Khi phương thức getSupportedOperations_1_2, getSupportedOperations_1_1 hoặc getSupportedOperations được gọi, trình điều khiển phải phát hiện xem liệu có thể giải mã hidl_handle được cung cấp và truy cập vào bộ nhớ mà hidl_handle mô tả hay không. Quá trình chuẩn bị mô hình phải không thành công nếu trường hidl_handle dùng cho toán hạng hằng không được hỗ trợ. Phải không thực thi được nếu trường hidl_handle dùng cho một toán hạng đầu vào hoặc đầu ra của lượt thực thi đó không được hỗ trợ. Trình điều khiển nên trả về mã lỗi GENERAL_FAILURE nếu quá trình chuẩn bị mô hình hoặc thực thi không thành công.

Miền bộ nhớ

Đối với các thiết bị chạy Android 11 trở lên, NNAPI hỗ trợ các miền bộ nhớ cung cấp giao diện bộ phân bổ (allocator) cho các vùng đệm do trình điều khiển quản lý. Điều này cho phép truyền bộ nhớ gốc của thiết bị giữa các quá trình thực thi, hạn chế việc sao chép và chuyển đổi dữ liệu không cần thiết giữa các lần thực thi liên tiếp trên cùng một trình điều khiển. Quy trình này được minh hoạ trong Hình 1.

Lưu luồng dữ liệu vào vùng đệm có và không có miền bộ nhớ

Hình 1. Lưu luồng dữ liệu vào vùng đệm bằng miền bộ nhớ

Tính năng miền bộ nhớ dành cho các tensor sử dụng chủ yếu trong nội bộ cho trình điều khiển và không cần thường xuyên truy cập ở phía máy khách. Ví dụ về tensor đó bao gồm các tensor trạng thái trong mô hình chuỗi. Đối với các tensor cần truy cập CPU thường xuyên ở phía máy khách, bạn nên sử dụng nhóm bộ nhớ dùng chung.

Để hỗ trợ tính năng miền bộ nhớ, hãy triển khai IDevice::allocate để cho phép khung yêu cầu phân bổ vùng đệm do người lái quản lý. Trong quá trình phân bổ, khung sẽ cung cấp các thuộc tính và mẫu sử dụng sau đây cho vùng đệm:

  • BufferDesc mô tả các thuộc tính bắt buộc của vùng đệm.
  • BufferRole mô tả mẫu sử dụng tiềm năng của vùng đệm dưới dạng đầu vào hoặc đầu ra của mô hình đã chuẩn bị. Bạn có thể chỉ định nhiều vai trò trong quá trình phân bổ vùng đệm và chỉ có thể dùng vùng đệm được phân bổ làm các vai trò đã chỉ định đó.

Vùng đệm được phân bổ là nội bộ của trình điều khiển. Người lái xe có thể chọn bất kỳ vị trí vùng đệm hoặc bố cục dữ liệu nào. Khi vùng đệm được phân bổ thành công, ứng dụng của trình điều khiển có thể tham chiếu hoặc tương tác với vùng đệm bằng mã thông báo được trả về hoặc đối tượng IBuffer.

Mã thông báo từ IDevice::allocate được cung cấp khi tham chiếu vùng đệm dưới dạng một trong các đối tượng MemoryPool trong cấu trúc Request của quá trình thực thi. Để ngăn một quy trình cố gắng truy cập vào vùng đệm được phân bổ trong một quy trình khác, trình điều khiển phải áp dụng quy trình xác thực phù hợp cho mỗi lần sử dụng bộ đệm. Trình điều khiển phải xác thực rằng mức sử dụng vùng đệm là một trong các vai trò BufferRole được cung cấp trong quá trình phân bổ và phải không thực thi được ngay lập tức nếu việc sử dụng đó không hợp pháp.

Đối tượng IBuffer được dùng để sao chép bộ nhớ tường minh. Trong một số trường hợp, ứng dụng của trình điều khiển phải khởi chạy vùng đệm do trình điều khiển quản lý từ một nhóm bộ nhớ dùng chung hoặc sao chép vùng đệm đó sang một nhóm bộ nhớ dùng chung. Sau đây là một số ví dụ về trường hợp sử dụng:

  • Khởi động tensor trạng thái
  • Đang lưu kết quả trung gian vào bộ nhớ đệm
  • Thực thi dự phòng trên CPU

Để hỗ trợ các trường hợp sử dụng này, trình điều khiển phải triển khai IBuffer::copyToIBuffer::copyFrom với ashmem, mmap_fdhardware_buffer_blob nếu hỗ trợ tính năng phân bổ miền bộ nhớ. Trình điều khiển không bắt buộc phải hỗ trợ chế độ không phải BLOB hardware_buffer.

Trong quá trình phân bổ vùng đệm, kích thước của vùng đệm có thể được suy ra từ toán hạng mô hình tương ứng của tất cả các vai trò do BufferRole chỉ định và các chiều được cung cấp trong BufferDesc. Khi tất cả thông tin về phương diện được kết hợp, vùng đệm có thể có các kích thước hoặc thứ hạng không xác định. Trong trường hợp như vậy, vùng đệm sẽ ở trạng thái linh hoạt, trong đó kích thước được cố định khi dùng làm dữ liệu đầu vào của mô hình và ở trạng thái động khi được dùng làm đầu ra của mô hình. Bạn có thể sử dụng cùng một vùng đệm với nhiều hình dạng đầu ra trong các quá trình thực thi khác nhau và trình điều khiển phải xử lý việc thay đổi kích thước vùng đệm đúng cách.

Miền bộ nhớ là một tính năng không bắt buộc. Trình điều khiển có thể xác định rằng không thể hỗ trợ một yêu cầu phân bổ nhất định vì một số lý do. Ví dụ:

  • Vùng đệm được yêu cầu có kích thước động.
  • Trình điều khiển có các hạn chế về bộ nhớ ngăn không cho xử lý các vùng đệm lớn.

Có thể có nhiều luồng khác nhau đọc đồng thời từ vùng đệm do trình điều khiển quản lý. Việc truy cập vào bộ đệm cùng lúc để ghi hoặc đọc/ghi là không xác định, nhưng không được gây ra sự cố cho dịch vụ trình điều khiển hoặc chặn phương thức gọi vô thời hạn. Trình điều khiển có thể trả về lỗi hoặc để nội dung của vùng đệm ở trạng thái không xác định.