Pule pamięci

Na tej stronie opisano struktury danych i metody wykorzystywane do wydajnej komunikacji buforów argumentów pomiędzy sterownikiem a strukturą.

W czasie kompilacji modelu struktura udostępnia sterownikowi wartości stałych operandów. W zależności od czasu życia stałego operandu, jego wartości znajdują się albo w wektorze HIDL, albo w puli pamięci współdzielonej.

  • Jeśli okres istnienia wynosi CONSTANT_COPY , wartości znajdują się w polu operandValues ​​struktury modelu. Ponieważ wartości w wektorze HIDL są kopiowane podczas komunikacji międzyprocesowej (IPC), jest on zwykle używany tylko do przechowywania niewielkiej ilości danych, takich jak operandy skalarne (na przykład skalar aktywacji w ADD ) i małe parametry tensora (na przykład tensor kształtu w RESHAPE ).
  • Jeśli czas życia wynosi CONSTANT_REFERENCE , wartości znajdują się w polu pools struktury modelu. Podczas IPC duplikowane są tylko uchwyty pul pamięci współdzielonej, zamiast kopiować surowe wartości. Dlatego bardziej efektywne jest przechowywanie dużej ilości danych (na przykład parametrów wagi w splotach) przy użyciu pul pamięci współdzielonej niż wektorów HIDL.

W czasie wykonywania modelu struktura udostępnia sterownikowi bufory operandów wejściowych i wyjściowych. W przeciwieństwie do stałych czasu kompilacji, które mogą być wysyłane w wektorze HIDL, dane wejściowe i wyjściowe wykonania są zawsze przekazywane poprzez kolekcję pul pamięci.

Typ danych HIDL hidl_memory jest używany zarówno podczas kompilacji, jak i wykonywania, aby reprezentować niezamapowaną pulę pamięci współdzielonej. Sterownik powinien odpowiednio zmapować pamięć, aby była użyteczna w oparciu o nazwę typu danych hidl_memory . Obsługiwane nazwy pamięci to:

  • ashmem : pamięć współdzielona Androida. Aby uzyskać więcej informacji, zobacz pamięć .
  • mmap_fd : Pamięć współdzielona wspierana przez deskryptor pliku poprzez mmap .
  • hardware_buffer_blob : Pamięć współdzielona obsługiwana przez bufor AHardwareBuffer w formacie AHARDWARE_BUFFER_FORMAT_BLOB . Dostępne w sieciach neuronowych (NN) HAL 1.2. Aby uzyskać więcej informacji, zobacz AHardwareBuffer .
  • hardware_buffer : Pamięć współdzielona obsługiwana przez ogólny bufor AHardwareBuffer, który nie używa formatu AHARDWARE_BUFFER_FORMAT_BLOB . Bufor sprzętowy w trybie innym niż BLOB jest obsługiwany tylko w wykonaniu modelu. Dostępny w NN HAL 1.2. Aby uzyskać więcej informacji, zobacz AHardwareBuffer .

Od wersji NN HAL 1.3 NNAPI obsługuje domeny pamięci, które zapewniają interfejsy alokatorów dla buforów zarządzanych przez sterowniki. Bufory zarządzane przez sterownik mogą być również używane jako wejścia lub wyjścia wykonania. Aby uzyskać więcej informacji, zobacz Domeny pamięci .

Sterowniki NNAPI muszą obsługiwać mapowanie nazw pamięci ashmem i mmap_fd . Od wersji NN HAL 1.3 sterowniki muszą także obsługiwać mapowanie hardware_buffer_blob . Obsługa ogólnych domen hardware_buffer i pamięci w trybie innym niż BLOB jest opcjonalna.

Bufor sprzętowy

AHardwareBuffer to typ pamięci współdzielonej, która otacza bufor Gralloc . W systemie Android 10 interfejs Neural Networks API (NNAPI) obsługuje użycie AHardwareBuffer , umożliwiając sterownikowi wykonywanie operacji bez kopiowania danych, co poprawia wydajność i zużycie energii przez aplikacje. Na przykład stos HAL kamery może przekazywać obiekty AHardwareBuffer do interfejsu NNAPI w celu obsługi obciążeń związanych z uczeniem maszynowym przy użyciu uchwytów AHardwareBuffer generowanych przez interfejsy API NDK kamery i multimediów NDK. Aby uzyskać więcej informacji, zobacz ANeuralNetworksMemory_createFromAHardwareBuffer .

Obiekty AHardwareBuffer używane w NNAPI są przekazywane do sterownika poprzez strukturę hidl_memory o nazwie hardware_buffer lub hardware_buffer_blob . Struktura hidl_memory hardware_buffer_blob reprezentuje tylko obiekty AHardwareBuffer w formacie AHARDWAREBUFFER_FORMAT_BLOB .

Informacje wymagane przez framework są zakodowane w polu hidl_handle struktury hidl_memory . Pole hidl_handle otacza native_handle , które koduje wszystkie wymagane metadane dotyczące bufora AHardwareBuffer lub Gralloc.

Sterownik musi poprawnie zdekodować podane pole hidl_handle i uzyskać dostęp do pamięci opisanej przez hidl_handle . Po wywołaniu metody getSupportedOperations_1_2 , getSupportedOperations_1_1 lub getSupportedOperations sterownik powinien wykryć, czy może zdekodować dostarczony hidl_handle i uzyskać dostęp do pamięci opisanej przez hidl_handle . Przygotowanie modelu musi zakończyć się niepowodzeniem, jeśli pole hidl_handle użyte jako stały operand nie jest obsługiwane. Wykonanie musi zakończyć się niepowodzeniem, jeśli pole hidl_handle użyte jako operand wejściowy lub wyjściowy wykonania nie jest obsługiwane. Zaleca się, aby sterownik zwrócił kod błędu GENERAL_FAILURE , jeśli przygotowanie lub wykonanie modelu nie powiedzie się.

Domeny pamięci

W przypadku urządzeń z systemem Android 11 lub nowszym NNAPI obsługuje domeny pamięci, które zapewniają interfejsy alokatorów dla buforów zarządzanych przez sterowniki. Pozwala to na przekazywanie natywnych pamięci urządzenia pomiędzy wykonaniami, eliminując niepotrzebne kopiowanie i transformację danych pomiędzy kolejnymi wykonaniami na tym samym sterowniku. Przepływ ten przedstawiono na rysunku 1.

Buforuj przepływ danych z domenami pamięci i bez nich

Rysunek 1. Buforowanie przepływu danych przy użyciu domen pamięci

Funkcja domeny pamięci jest przeznaczona dla tensorów, które są w większości wbudowane w sterownik i nie wymagają częstego dostępu po stronie klienta. Przykładami takich tensorów są tensory stanu w modelach sekwencji. W przypadku tensorów, które wymagają częstego dostępu do procesora po stronie klienta, lepiej jest używać pul pamięci współdzielonej.

Aby obsługiwać funkcję domeny pamięci, zaimplementuj IDevice::allocate , aby umożliwić platformie żądanie alokacji buforu zarządzanego przez sterownik. Podczas alokacji struktura zapewnia następujące właściwości i wzorce użycia bufora:

  • BufferDesc opisuje wymagane właściwości bufora.
  • BufferRole opisuje potencjalny wzorzec wykorzystania bufora jako wejścia lub wyjścia przygotowanego modelu. Podczas alokacji bufora można określić wiele ról, a przydzielony bufor może być używany tylko jako określone role.

Przydzielony bufor jest wewnętrzny dla sterownika. Kierowca może wybrać dowolną lokalizację bufora lub układ danych. Po pomyślnej alokacji bufora klient sterownika może odwoływać się do bufora lub wchodzić z nim w interakcję przy użyciu zwróconego tokenu lub obiektu IBuffer .

Token z IDevice::allocate jest udostępniany podczas odwoływania się do bufora jako jednego z obiektów MemoryPool w strukturze Request wykonania. Aby uniemożliwić procesowi próbę uzyskania dostępu do bufora przydzielonego w innym procesie, sterownik musi zastosować odpowiednią walidację przy każdym użyciu bufora. Sterownik musi sprawdzić, czy użycie bufora jest jedną z ról BufferRole podanych podczas alokacji i musi natychmiast zakończyć się niepowodzeniem wykonania, jeśli użycie jest nielegalne.

Obiekt IBuffer służy do jawnego kopiowania pamięci. W niektórych sytuacjach klient sterownika musi zainicjować bufor zarządzany przez sterownik z puli pamięci współdzielonej lub skopiować bufor do puli pamięci współdzielonej. Przykładowe przypadki użycia obejmują:

  • Inicjalizacja tensora stanu
  • Buforowanie wyników pośrednich
  • Wykonanie awaryjne na procesorze

Aby obsłużyć te przypadki użycia, sterownik musi zaimplementować IBuffer::copyTo i IBuffer::copyFrom z ashmem , mmap_fd i hardware_buffer_blob , jeśli obsługuje alokację domeny pamięci. Opcjonalnie sterownik obsługuje tryb inny niż BLOB hardware_buffer .

Podczas alokacji bufora wymiary bufora można wywnioskować z odpowiednich argumentów modelu wszystkich ról określonych przez BufferRole i wymiarów podanych w BufferDesc . Po połączeniu wszystkich informacji o wymiarach bufor może mieć nieznane wymiary lub rangę. W takim przypadku bufor znajduje się w stanie elastycznym, w którym wymiary są stałe, gdy jest używany jako dane wejściowe modelu, oraz w stanie dynamicznym, gdy jest używany jako dane wyjściowe modelu. Ten sam bufor może być używany z różnymi kształtami wyjść w różnych wykonaniach, a sterownik musi odpowiednio obsługiwać zmianę rozmiaru bufora.

Domena pamięci jest funkcją opcjonalną. Sterownik może stwierdzić, że nie może obsłużyć danego żądania alokacji z wielu powodów. Na przykład:

  • Żądany bufor ma rozmiar dynamiczny.
  • Sterownik ma ograniczenia pamięci, które uniemożliwiają mu obsługę dużych buforów.

Możliwe jest jednoczesne odczytywanie kilku różnych wątków z bufora zarządzanego przez sterownik. Jednoczesny dostęp do bufora w celu zapisu lub odczytu/zapisu jest niezdefiniowany, ale nie może powodować awarii usługi sterownika ani blokować obiektu wywołującego na czas nieokreślony. Driver może zwrócić błąd lub pozostawić zawartość bufora w nieokreślonym stanie.