Pule pamięci

Na tej stronie opisano struktury danych i metody używane do efektywnej komunikacji 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 operandu stałego 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), jest on zwykle używany 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ć niezamapowany 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ółdzielone wspomnienie obsługiwane przez AHardwareBuffer w formacie AHARDWARE_BUFFER_FORMAT_BLOB. Dostępne 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 wykonywaniu 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 otacza 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 do zadań związanych z uczeniem maszynowym przy użyciu 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 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.

buforowanie przepływu danych z domenami pamięci z pamięcią podręczną i bez niej;

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 procesora po stronie klienta, zalecamy używanie 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żytkowania 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 zastosowania:

  • 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 danych wyjściowych w różnych wykonaniach, a sterownik musi odpowiednio zmienić rozmiar bufora.

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.