Pule pamięci

Ta strona opisuje struktury danych i metody używane do wydajnej komunikacji buforów operandów między sterownikiem a strukturą.

W czasie kompilacji modelu struktura dostarcza sterownikowi wartości stałych operandów. W zależności od czasu życia stałego operandu, jego wartości są umieszczane albo w wektorze HIDL, albo w puli pamięci współużytkowanej.

  • Jeśli czas życia 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 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 tensorowe (na przykład tensor kształtu w RESHAPE ).
  • Jeśli czas życia to CONSTANT_REFERENCE , wartości znajdują się w polu pools struktury modelu. Tylko uchwyty pul pamięci współużytkowanej są duplikowane podczas IPC, zamiast kopiowania nieprzetworzonych wartości. W związku z tym 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 bufory operandów wejściowych i wyjściowych sterownikowi. 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 hidl_memory jest używany zarówno podczas kompilacji, jak i wykonywania, aby reprezentować niezamapowaną pulę pamięci współużytkowanej. Sterownik powinien odpowiednio mapować 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 przez mmap .
  • hardware_buffer_blob : Pamięć współdzielona obsługiwana przez AHardwareBuffer w formacie AHARDWARE_BUFFER_FORMAT_BLOB . Dostępne z sieci 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 podczas wykonywania modelu. Dostępne od NN HAL 1.2. Aby uzyskać więcej informacji, zobacz AHardwareBuffer .

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 NN HAL 1.3 sterowniki muszą również obsługiwać mapowanie hardware_buffer_blob . Obsługa ogólnego bufora hardware_buffer i domen pamięci w trybie innym niż BLOB jest opcjonalna.

Bufor sprzętowy

AHardwareBuffer to rodzaj współdzielonej pamięci, która opakowuje bufor Gralloc . W Androidzie 10 interfejs API sieci neuronowych (NNAPI) obsługuje użycie AHardwareBuffer , umożliwiając sterownikowi wykonywanie wykonań bez kopiowania danych, co poprawia wydajność i zużycie energii przez aplikacje. Na przykład stos HAL kamery 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 kamery NDK i multimediów NDK. Aby uzyskać więcej informacji, zobacz ANeuralNetworksMemory_createFromAHardwareBuffer .

Obiekty AHardwareBuffer używane w NNAPI są przekazywane do sterownika przez 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 platformę są zakodowane w polu hidl_handle struktury hidl_memory . Pole hidl_handle obejmuje native_handle , które 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 . Kiedy wywoływana jest metoda 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żywane jako stały operand nie jest obsługiwane. Wykonanie musi zakończyć się niepowodzeniem, jeśli pole hidl_handle używane dla operandu wejściowego lub wyjściowego wykonania nie jest obsługiwane. Zaleca się, aby sterownik zwracał 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. Przepływ ten przedstawiono na rysunku 1.

Buforuj 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 są w większości 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ółużytkowanej.

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

  • BufferDesc opisuje wymagane właściwości bufora.
  • BufferRole opisuje potencjalny wzorzec użycia 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 te określone role.

Przydzielony bufor jest wewnętrzny dla 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 interakcje przy użyciu zwróconego tokena 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 proces nie próbował uzyskać dostępu do bufora przydzielonego w innym procesie, sterownik musi stosować odpowiednią walidację przy każdym użyciu bufora. Sterownik musi zweryfikować, czy użycie bufora jest jedną z ról BufferRole dostarczonych 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 pewnych 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
  • Wykonywanie awaryjne na CPU

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. Opcjonalne jest, aby sterownik obsługiwał hardware_buffer w trybie innym niż obiekt BLOB .

Podczas alokacji bufora 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 danych wyjściowych 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 równoczesne 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ć dzwoniącego na czas nieokreślony. Sterownik może zwrócić błąd lub pozostawić zawartość bufora w nieokreślonym stanie.

,

Ta strona opisuje struktury danych i metody używane do wydajnej komunikacji buforów operandów między sterownikiem a strukturą.

W czasie kompilacji modelu struktura dostarcza sterownikowi wartości stałych operandów. W zależności od czasu życia stałego operandu, jego wartości są umieszczane albo w wektorze HIDL, albo w puli pamięci współużytkowanej.

  • Jeśli czas życia 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 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 tensorowe (na przykład tensor kształtu w RESHAPE ).
  • Jeśli czas życia to CONSTANT_REFERENCE , wartości znajdują się w polu pools struktury modelu. Tylko uchwyty pul pamięci współużytkowanej są duplikowane podczas IPC, zamiast kopiowania nieprzetworzonych wartości. W związku z tym 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 bufory operandów wejściowych i wyjściowych sterownikowi. 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 hidl_memory jest używany zarówno podczas kompilacji, jak i wykonywania, aby reprezentować niezamapowaną pulę pamięci współużytkowanej. Sterownik powinien odpowiednio mapować 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 przez mmap .
  • hardware_buffer_blob : Pamięć współdzielona obsługiwana przez AHardwareBuffer w formacie AHARDWARE_BUFFER_FORMAT_BLOB . Dostępne z sieci 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 podczas wykonywania modelu. Dostępne od NN HAL 1.2. Aby uzyskać więcej informacji, zobacz AHardwareBuffer .

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 NN HAL 1.3 sterowniki muszą również obsługiwać mapowanie hardware_buffer_blob . Obsługa ogólnego bufora hardware_buffer i domen pamięci w trybie innym niż BLOB jest opcjonalna.

Bufor sprzętowy

AHardwareBuffer to rodzaj współdzielonej pamięci, która opakowuje bufor Gralloc . W Androidzie 10 interfejs API sieci neuronowych (NNAPI) obsługuje użycie AHardwareBuffer , umożliwiając sterownikowi wykonywanie wykonań bez kopiowania danych, co poprawia wydajność i zużycie energii przez aplikacje. Na przykład stos HAL kamery 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 kamery NDK i multimediów NDK. Aby uzyskać więcej informacji, zobacz ANeuralNetworksMemory_createFromAHardwareBuffer .

Obiekty AHardwareBuffer używane w NNAPI są przekazywane do sterownika przez 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 platformę są zakodowane w polu hidl_handle struktury hidl_memory . Pole hidl_handle obejmuje native_handle , które 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 . Kiedy wywoływana jest metoda 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żywane jako stały operand nie jest obsługiwane. Wykonanie musi zakończyć się niepowodzeniem, jeśli pole hidl_handle używane dla operandu wejściowego lub wyjściowego wykonania nie jest obsługiwane. Zaleca się, aby sterownik zwracał 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. Przepływ ten przedstawiono na rysunku 1.

Buforuj 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 są w większości 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ółużytkowanej.

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

  • BufferDesc opisuje wymagane właściwości bufora.
  • BufferRole opisuje potencjalny wzorzec użycia 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 te określone role.

Przydzielony bufor jest wewnętrzny dla 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 interakcje przy użyciu zwróconego tokena 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 proces nie próbował uzyskać dostępu do bufora przydzielonego w innym procesie, sterownik musi stosować odpowiednią walidację przy każdym użyciu bufora. Sterownik musi zweryfikować, czy użycie bufora jest jedną z ról BufferRole dostarczonych 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 pewnych 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
  • Wykonywanie awaryjne na CPU

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. Opcjonalne jest, aby sterownik obsługiwał hardware_buffer w trybie innym niż obiekt BLOB .

Podczas alokacji bufora 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 danych wyjściowych 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 równoczesne 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ć dzwoniącego na czas nieokreślony. Sterownik może zwrócić błąd lub pozostawić zawartość bufora w nieokreślonym stanie.