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 này sẽ cung cấp các giá trị của toán hạng hằng số 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 thời gian tồn tạ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 vectơ này thường chỉ được dùng để lưu trữ một lượng nhỏ dữ liệu, chẳng hạn 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 tay cầm 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 này sẽ cung cấp vùng đệm của 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ố tại thời điểm biên dịch có thể được gửi trong vectơ HIDL, dữ liệu đầu vào và đầu ra của một 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ớ 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: Bộ nhớ dùng chung của 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 mô tả bằng 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ó trong Neural Networks (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 được hỗ trợ bởi AHardwareBuffer chung không sử dụng định dạng AHARDWARE_BUFFER_FORMAT_BLOB. Vùng đệm phần cứng ở 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 phần 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ổ 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ợ việc liên kết tên bộ nhớ ashmemmmap_fd. Từ NN HAL 1.3, trình điều khiển cũng phải hỗ trợ việc liên kết hardware_buffer_blob. Bạn không bắt buộc phải hỗ trợ miền bộ nhớ và chế độ hardware_buffer không phải BLOB chung.

AHardwareBuffer

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ợ sử dụng AHardwareBuffer, cho phép trình điều khiển thực hiện các lệnh thực thi mà không cần sao chép dữ liệu, giúp cải thiện hiệu suất và mức tiêu thụ điện năng cho các ứng dụng. Ví dụ: ngăn xếp HAL 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 điều khiển AHardwareBuffer do NDK máy ảnh và API NDK đ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 bao bọc native_handle, mã hoá tất cả siêu dữ liệu bắt buộc về vùng đệm AHardwareBuffer hoặc Gralloc.

Trình điều khiển phải giải mã chính xác trường hidl_handle được cung cấp và truy cập vào bộ nhớ do 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 sẽ phát hiện xem phương thức đó có thể giải mã hidl_handle được cung cấp và truy cập vào bộ nhớ do hidl_handle mô tả hay không. Quá trình chuẩn bị mô hình sẽ không thành công nếu trường hidl_handle dùng cho toán hạng hằng số không được hỗ trợ. Quá trình thực thi phải không thành công nếu trường hidl_handle dùng cho toán hạng đầu vào hoặc đầu ra của quá trình thực thi không được hỗ trợ. Bạn nên yêu cầu trình điều khiển trả về mã lỗi GENERAL_FAILURE nếu quá trình chuẩn bị hoặc thực thi mô hình 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, ngăn chặn việc sao chép và chuyển đổi dữ liệu không cần thiết giữa các quá trình 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.

Luồng dữ liệu bộ đệ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ề các 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 các 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 này 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ột 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ể sử dụng vùng đệm được phân bổ theo các vai trò đã chỉ định đó.

Vùng đệm được phân bổ nằm trong trình điều khiển. Trình điều khiển 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 bộ đệ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 thích hợp mỗi khi 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 đó là bất hợp pháp.

Đối tượng IBuffer được dùng để sao chép bộ nhớ một cách rõ ràng. 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 đó vào một nhóm bộ nhớ dùng chung. Ví dụ về trường hợp sử dụng:

  • Khởi chạy tensor trạng thái
  • 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ề kích thước được kết hợp, vùng đệm có thể 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 ở trạng thái linh hoạt, trong đó các kích thước được cố định khi dùng làm đầu vào mô hình và ở trạng thái động khi dùng làm đầu ra 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 lần thực thi khác nhau và trình điều khiển phải xử lý việc đổ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 trình điều khiển 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ụ:

  • Bộ nhớ đệm được yêu cầu có kích thước động.
  • Trình điều khiển có các quy tắc ràng buộc về bộ nhớ khiến trình điều khiển không thể xử lý các vùng đệm lớn.

Có thể 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 vùng đệm đồng thời để ghi hoặc đọc/ghi là không xác định, nhưng không được làm hỏng 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.