Pule pamięci

Ta strona opisuje struktury danych i metody używane do efektywnego komunikowania się buforów operandów między sterownikami a ramami.

Podczas kompilowania modelu platforma przekazuje wartości stałych operandów do sterownika. W zależności od czasu trwania stałej wartości operanda, jego wartości znajdują się w wektorze HIDL lub w puli pamięci współdzielonej.

  • Jeśli okres jest określony jako CONSTANT_COPY, wartości znajdują się w polu operandValues w strukturze modelu. Ponieważ wartości w wektorze HIDL są kopiowane podczas komunikacji między procesami (IPC), służą one zwykle tylko do przechowywania niewielkiej ilości danych, takich jak operandy skalarne (np. skalar aktywacji w funkcji ADD) i małe parametry tensora (np. tensor kształtu w funkcji RESHAPE).
  • Jeśli okres jest określony jako CONSTANT_REFERENCE, wartości znajdują się w polu pools w strukturze modelu. Podczas komunikacji między procesami duplikowane są tylko uchwyty puli pamięci współdzielonej, a nie wartości. Dlatego większą ilość danych (np. parametry wag w konwolucjach) lepiej jest przechowywać w pułach pamięci współdzielonej niż wektorach HIDL.

W momencie wykonywania modelu framework udostępnia sterownikowi bufory danych wejściowych i wyjściowych. W odróżnieniu od stałych wartości w czasie kompilacji, które mogą być wysyłane w wektorze HIDL, dane wejściowe i wyjściowe podczas wykonywania są zawsze przekazywane za pomocą zbioru puli pamięci.

Typ danych HIDL hidl_memory jest używany zarówno podczas kompilacji, jak i wykonania, aby reprezentować niemapowany wspólny obszar pamięci. Sterownik powinien mapować pamięć w odpowiednim miejscu, aby można było z niej korzystać na podstawie nazwy typu danych hidl_memory. Obsługiwane nazwy pamięci:

  • ashmem: współdzielone wspomnienie na Androidzie. Więcej informacji znajdziesz w sekcji pamięć.
  • mmap_fd: współdzielona pamięć obsługiwana przez deskryptor pliku za pomocą mmap.
  • hardware_buffer_blob: współdzielona pamięć obsługiwana przez AHardwareBuffer w formacie AHARDWARE_BUFFER_FORMAT_BLOB. Dostępny w ramach Neural Networks (NN) HAL 1.2. Więcej informacji znajdziesz w AHardwareBuffer.
  • hardware_buffer: współdzielona pamięć obsługiwana przez ogólny AHardwareBuffer, który nie używa formatu AHARDWARE_BUFFER_FORMAT_BLOB. Tryb niebędący BLOB bufora sprzętowego jest obsługiwany tylko w ramach wykonywania modelu.Dostępny od wersji NN HAL 1.2. Więcej informacji znajdziesz w AHardwareBuffer.

Od wersji NN HAL 1.3 interfejs NNAPI obsługuje domeny pamięci, które udostępniają interfejsy alokacji dla buforów zarządzanych przez sterownik. Bufory zarządzane przez sterownika mogą też służyć jako dane wejściowe lub wyjściowe. Więcej informacji znajdziesz w artykule MemoryDomains.

Sterowniki NNAPI muszą obsługiwać mapowanie nazw pamięci ashmemmmap_fd. Od wersji NN HAL 1.3 sterowniki muszą też obsługiwać mapowanie hardware_buffer_blob. Obsługa ogólnego trybu niebędącego BLOB hardware_buffer i domen pamięci jest opcjonalna.

AHardwareBuffer

AHardwareBuffer to typ współdzielonej pamięci, który zawiera bufor Gralloc. W Androidzie 10 interfejs Neural Networks API (NNAPI) obsługuje AHardwareBuffer, co pozwala sterownikowi wykonywać instrukcje bez kopiowania danych, co z kolei poprawia wydajność i zużycie energii przez aplikacje. Na przykład aparat HAL może przekazywać obiekty AHardwareBuffer do interfejsu NNAPI na potrzeby zadań związanych z uczeniem maszynowym, używając uchwytów AHardwareBuffer wygenerowanych przez NDK aparatu i NDK multimediów. Więcej informacji znajdziesz w sekcji ANeuralNetworksMemory_createFromAHardwareBuffer.

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

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

Sterownik musi prawidłowo zdekodować podane pole hidl_handle i uzyskać dostęp do pamięci opisanej przez hidl_handle. Gdy wywoływana jest metoda getSupportedOperations_1_2, getSupportedOperations_1_1 lub getSupportedOperations, sterownik powinien wykryć, czy może zdekodować podany obiekt hidl_handle i czy ma dostęp do pamięci opisanej przez hidl_handle. Przygotowanie modelu musi zakończyć się niepowodzeniem, jeśli pole hidl_handle używane dla stałej wartości operanda nie jest obsługiwane. Jeśli pole hidl_handle używane do wejścia lub wyjścia danych w ramach wykonania nie jest obsługiwane, wykonanie nie może się udać. W przypadku niepowodzenia przygotowania lub wykonania modelu zalecamy zwrócenie przez sterownik kodu błędu GENERAL_FAILURE.

Domeny wspomnień

W przypadku urządzeń z Androidem 11 lub nowszym interfejs NNAPI obsługuje domeny pamięci, które udostępniają interfejsy alokacji dla buforów zarządzanych przez sterownik. Umożliwia to przekazywanie danych z pamięci natywnej urządzenia w trakcie kolejnych wywołań, co eliminuje niepotrzebne kopiowanie i przekształcanie danych między kolejnymi wywołaniami tego samego sterownika. Ten proces ilustruje rysunek 1.

Przepływ danych w buforze z domenami pamięci i bez nich

Rysunek 1. buforowanie przepływu danych za pomocą domen pamięci;

Funkcja domeny pamięci jest przeznaczona do tensorów, które są głównie wewnętrzne dla sterownika 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ć wspólnych puli pamięci.

Aby obsługiwać funkcję domeny pamięci, zaimplementuj IDevice::allocate, aby umożliwić frameworkowi wysyłanie żądań dotyczącego przydzielenia bufora zarządzanego przez sterownik. Podczas alokacji framework udostępnia te właściwości i wzorce użycia bufora:

  • BufferDescopisuje wymagane właściwości bufora.
  • BufferRole opisuje potencjalny wzorzec wykorzystania bufora jako danych wejściowych lub wyjściowych przygotowanego modelu. Podczas przydzielania bufora można określić wiele ról, a przydzielony bufor może być używany tylko zgodnie z określonymi rolami.

Przydzielone miejsce na bufor jest wewnętrzne dla sterownika. Sterownik może wybrać dowolną lokalizację bufora lub układ danych. Gdy bufor zostanie przydzielony, klient sterownika może się do niego odwoływać lub wchodzić z nim w interakcję za pomocą zwróconego tokena lub obiektu IBuffer.

Podczas odwoływania się do bufora jako jednego z MemoryPoolobiektów w Requeststrukturze wykonania token z IDevice::allocate jest podawany. Aby zapobiec próbom uzyskania dostępu do bufora przydzielonego w innym procesie, sterownik musi stosować odpowiednią weryfikację przy każdym użyciu bufora. Sterownik musi sprawdzić, czy użycie bufora jest jedną z rol BufferRole podanych podczas alokacji, i natychmiast przerwać wykonywanie kodu, jeśli użycie jest niezgodne z przepisami.

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

  • Inicjowanie tensora stanu
  • Buforowanie wyników pośrednich
  • Fallbackowe wykonywanie na procesorze

Aby obsługiwać te przypadki użycia, sterownik musi implementować IBuffer::copyTo i IBuffer::copyFrom z ashmem, mmap_fd i hardware_buffer_blob, jeśli obsługuje przydzielanie pamięci domeny. Obsługa trybu innego niż BLOB jest opcjonalna dla sterownika.hardware_buffer

Podczas przydzielania bufora wymiary bufora można wywnioskować z odpowiednich operandów modelu wszystkich ról określonych przez BufferRole oraz wymiarów podanych w BufferDesc. Po połączeniu wszystkich informacji o wymiarach bufor może zawierać nieznane wymiary lub nieznane miejsce w rankingu. W takim przypadku bufor jest 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. Tego samego bufora można używać w różnych kształtach w różnych wykonaniach, a sterownik musi odpowiednio zmienić jego rozmiar.

Domena pamięci jest opcjonalną funkcją. Kierowca może zdecydować, że nie może obsługiwać danego żądania alokacji z różnych powodów. Przykład:

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

Kilka wątków może jednocześnie odczytywać dane z bufora zarządzanego przez sterownik. Dostęp do bufora jednocześnie do zapisu lub odczytu/zapisu jest nieokreślony, ale nie może spowodować awarii usługi sterownika ani nie może blokować wywołującego na czas nieokreślony. Sterownik może zwrócić błąd lub pozostawić zawartość bufora w nieokreślonym stanie.