Na tej stronie opisujemy struktury danych i metody używane do wydajnej komunikacji buforów operandów między sterownikiem a platformą.
Podczas kompilacji modelu platforma udostępnia sterownikowi wartości operandów stałych. W zależności od czasu życia operandu stałego jego wartości znajdują się w wektorze HIDL lub w puli pamięci współdzielonej.
- Jeśli czas życia to
CONSTANT_COPY, wartości znajdują się w poluoperandValuesstruktury 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 (np. skalar aktywacji wADD) i małe parametry tensorowe (np. tensor kształtu wRESHAPE). - Jeśli czas życia to
CONSTANT_REFERENCE, wartości znajdują się w polupoolsstruktury modelu. Podczas IPC duplikowane są tylko uchwyty puli pamięci współdzielonej, a nie surowe wartości. Dlatego bardziej wydajne jest przechowywanie dużej ilości danych (np. parametrów wagi w konwolucjach) w pulach pamięci współdzielonej niż w wektorach HIDL.
Podczas wykonywania modelu platforma 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 za pomocą kolekcji pul pamięci.
Typ danych HIDL hidl_memory jest używany zarówno podczas kompilacji, jak i wykonywania do reprezentowania niezmapowanej puli pamięci współdzielonej. Sterownik powinien odpowiednio zmapować pamięć, aby można było jej używać na podstawie nazwy typu danych hidl_memory.
Obsługiwane nazwy pamięci:
ashmem: pamięć współdzielona Androida. Więcej informacji znajdziesz w sekcji dotyczącej pamięci.mmap_fd: pamięć współdzielona obsługiwana przez deskryptor pliku za pomocąmmap.hardware_buffer_blob: pamięć współdzielona obsługiwana przez AHardwareBuffer w formacieAHARDWARE_BUFFER_FORMAT_BLOB. Dostępne w HAL sieci neuronowych (NN) 1.2. Więcej informacji znajdziesz w sekcji AHardwareBuffer.hardware_buffer: pamięć współdzielona obsługiwana przez ogólny AHardwareBuffer, który nie używa formatuAHARDWARE_BUFFER_FORMAT_BLOB. Bufor sprzętowy w trybie innym niż BLOB jest obsługiwany tylko podczas wykonywania modelu.Dostępne w NN HAL 1.2. Więcej informacji znajdziesz w sekcji AHardwareBuffer.
Od NN HAL 1.3 NNAPI obsługuje domeny pamięci, które udostępniają interfejsy alokatora dla buforów zarządzanych przez sterownik. Bufory zarządzane przez sterownik mogą być też używane jako dane wejściowe lub wyjściowe wykonania. Więcej informacji znajdziesz w sekcji Domeny pamięci.
Sterowniki NNAPI muszą obsługiwać mapowanie nazw pamięci ashmem i mmap_fd. Od NN HAL 1.3 sterowniki muszą też obsługiwać mapowanie hardware_buffer_blob. Obsługa ogólnego hardware_buffer w trybie innym niż BLOB i domen pamięci jest opcjonalna.
AHardwareBuffer
AHardwareBuffer to typ pamięci współdzielonej, która opakowuje bufor
Gralloc. W Androidzie
10 interfejs Neural Networks API (NNAPI) obsługuje używanie
AHardwareBuffer,
co pozwala sterownikowi wykonywać operacje bez kopiowania danych, co zwiększa
wydajność i zmniejsza zużycie energii w aplikacjach. Na przykład stos HAL aparatu może przekazywać obiekty AHardwareBuffer do NNAPI na potrzeby zadań uczenia maszynowego za pomocą uchwytów AHardwareBuffer generowanych przez interfejsy 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ą
hidl_memory struktury 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 platformę są zakodowane w polu hidl_handle struktury hidl_memory. Pole hidl_handle opakowuje native_handle, które koduje wszystkie wymagane metadane dotyczące AHardwareBuffer lub bufora 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
hidl_handle i uzyskać dostęp do pamięci opisanej przez hidl_handle. Przygotowanie modelu musi się nie udać, jeśli pole hidl_handle używane w przypadku operandu stałego nie jest obsługiwane. Wykonanie musi się nie udać, jeśli pole hidl_handle używane w przypadku operandu wejściowego lub wyjściowego wykonania nie jest obsługiwane. Jeśli przygotowanie lub wykonanie modelu się nie powiedzie, sterownik powinien zwrócić kod błędu GENERAL_FAILURE.
Domeny pamięci
W przypadku urządzeń z Androidem 11 lub nowszym NNAPI obsługuje domeny pamięci, które udostępniają interfejsy alokatora dla buforów zarządzanych przez sterownik. Umożliwia to przekazywanie natywnych pamięci urządzenia między wykonaniami, co eliminuje niepotrzebne kopiowanie i przekształcanie danych między kolejnymi wykonaniami na tym samym sterowniku. Ten proces jest przedstawiony na ilustracji 1.
Ilustracja 1. Przepływ danych bufora za pomocą domen pamięci
Funkcja domen pamięci jest przeznaczona dla 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ć pul pamięci współdzielonej.
Aby obsługiwać funkcję domen pamięci, zaimplementuj
IDevice::allocate
aby umożliwić platformie żądanie alokacji bufora zarządzanego przez sterownik. Podczas alokacji platforma udostępnia te właściwości i wzorce użycia bufora:
BufferDescopisuje wymagane właściwości bufora.BufferRoleopisuje potencjalny wzorzec użycia bufora jako danych wejściowych lub wyjściowych przygotowanego modelu. Podczas alokacji bufora można określić wiele ról, a przydzielony bufor może być używany tylko w tych rolach.
Przydzielony bufor jest wewnętrzny dla sterownika. Sterownik może wybrać dowolną lokalizację bufora lub układ danych. Gdy bufor zostanie pomyślnie przydzielony, klient sterownika może odwoływać się do bufora lub wchodzić z nim w interakcję za pomocą zwróconego tokena lub obiektu IBuffer.
Token z IDevice::allocate jest udostępniany podczas odwoływania się do bufora jako
jednego z
MemoryPool
obiektów w strukturze
Request
wykonania. Aby uniemożliwić procesowi próbę 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 ról BufferRole podanych podczas alokacji, i natychmiast przerwać wykonanie, jeśli użycie jest nieprawidłowe.
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łady przypadków użycia:
- Inicjowanie tensora stanu
- Buforowanie wyników pośrednich
- Wykonanie rezerwowe na procesorze
Aby obsługiwać 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ę domen
pamięci. Obsługa hardware_buffer w trybie innym niż BLOB jest opcjonalna.
Podczas alokacji 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 mieć nieznane wymiary lub rangę. 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. Ten sam bufor może być używany z różnymi kształtami danych wyjściowych w różnych wykonaniach, a sterownik musi prawidłowo obsługiwać zmianę rozmiaru bufora.
Domena pamięci jest funkcją opcjonalną. Sterownik może stwierdzić, że nie może obsługiwać danego żądania alokacji z kilku powodów. Na przykład:
- Żądany bufor ma dynamiczny rozmiar.
- Sterownik ma ograniczenia pamięci, które uniemożliwiają mu obsługę dużych buforów.
Kilka różnych wątków może jednocześnie odczytywać dane 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ć wywołującego na czas nieokreślony. Sterownik może zwrócić błąd lub pozostawić zawartość bufora w stanie nieokreślonym.