Pule pamięci

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

Ta strona opisuje struktury danych i metody używane do efektywnej komunikacji buforów operandowych między sterownikiem a platformą.

W czasie kompilacji modelu struktura udostępnia sterownikowi wartości stałych operandów. W zależności od okresu istnienia operandu stałego jego wartości znajdują się w wektorze HIDL lub w puli pamięci współużytkowanej.

  • Jeśli okres istnienia to 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 to zwykle używane 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 okres istnienia to CONSTANT_REFERENCE , wartości znajdują się w polu pools struktury modelu. Podczas IPC duplikowane są tylko uchwyty pul pamięci współużytkowanej, a nie kopiowane wartości surowych. Dlatego bardziej wydajne jest przechowywanie dużej ilości danych (na przykład parametrów wagi w splotach) przy użyciu pul pamięci współużytkowanej 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 za pośrednictwem kolekcji pul pamięci.

Typ danych hidl_memory jest używany zarówno podczas kompilacji, jak i wykonywania do reprezentowania niezamapowanej puli pamięci współużytkowanej. Sterownik powinien odpowiednio mapować pamięć, aby była użyteczna na podstawie nazwy 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 przez mmap .
  • hardware_buffer_blob : Pamięć współdzielona wspierana przez 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 wspierana przez ogólny AHardwareBuffer , który nie używa formatu AHARDWARE_BUFFER_FORMAT_BLOB . Bufor sprzętowy w trybie innym niż BLOB jest obsługiwany tylko podczas wykonywania modelu. Dostępny od NN HAL 1.2. Aby uzyskać więcej informacji, zobacz AHardwareBuffer .

Począwszy od NN HAL 1.3, NNAPI obsługuje domeny pamięci, które zapewniają interfejsy alokatora 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ą również obsługiwać mapowanie hardware_buffer_blob . Obsługa ogólnych domen hardware_buffer i pamięci w trybie innym niż BLOB jest opcjonalna.

AHardwareBuffer

AHardwareBuffer to rodzaj pamięci współdzielonej, która otacza bufor Gralloc . W systemie Android 10 interfejs API sieci neuronowych (NNAPI) obsługuje korzystanie z AHardwareBuffer , umożliwiając sterownikowi wykonywanie wykonań bez kopiowania danych, co poprawia wydajność i zużycie energii przez aplikacje. Na przykład stos HAL aparatu może przekazywać obiekty AHardwareBuffer do NNAPI w celu obsługi obciążeń uczenia maszynowego przy użyciu uchwytów AHardwareBuffer generowanych przez interfejsy API NDK aparatu i nośnika NDK. Aby uzyskać więcej informacji, zobacz ANeuralNetworksMemory_createFromAHardwareBuffer .

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

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

Sterownik musi poprawnie zdekodować dostarczone 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 się nie powieść, jeśli pole hidl_handle używane dla stałego operandu nie jest obsługiwane. Wykonanie musi się nie powieść, jeśli pole hidl_handle używane dla operandu wejściowego lub wyjściowego 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 alokatora dla buforów zarządzanych przez sterowniki. Pozwala to na przekazywanie natywnych pamięci urządzenia między wykonaniami, eliminując niepotrzebne kopiowanie danych i transformację między kolejnymi wykonaniami na tym samym sterowniku. Ten przepływ przedstawiono na rysunku 1.

Buforowy przepływ danych z domenami pamięci i bez nich
Rysunek 1. Przepływ danych w buforze przy użyciu domen pamięci

Funkcja domeny pamięci jest przeznaczona dla tensorów, które w większości są wewnętrzne w sterowniku 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, preferowane jest używanie pul pamięci współdzielonej.

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

  • BufferDesc opisuje wymagane właściwości bufora.
  • BufferRole opisuje potencjalny wzorzec użycia bufora jako wejście lub wyjście przygotowanego modelu. Podczas alokacji buforu 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 sterownika. Kierowca 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ę przy użyciu zwróconego tokenu lub obiektu IBuffer .

Token z IDevice::allocate jest dostarczany podczas odwoływania się do bufora jako jednego z obiektów MemoryPool w strukturze Request wykonania. Aby zapobiec próbie uzyskania przez proces 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, 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ółużytkowanej lub skopiować bufor do puli pamięci współużytkowanej. Przykładowe przypadki użycia obejmują:

  • Inicjalizacja tensora stanu
  • Buforowanie wyników pośrednich
  • Wykonanie awaryjne 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ę domeny pamięci. Opcjonalne jest, aby sterownik obsługiwał tryb nie-BLOB hardware_buffer .

Podczas alokacji buforu wymiary bufora można wywnioskować z odpowiednich operandó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 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 wyjść w różnych wykonaniach, a sterownik musi prawidłowo obsługiwać zmianę rozmiaru bufora.

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

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

Możliwe jest równoczesne odczytywanie kilku różnych wątków z bufora zarządzanego przez sterownik. Jednoczesne uzyskiwanie dostępu do bufora w celu zapisu lub odczytu/zapisu jest niezdefiniowane, 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 nieokreślonym stanie.