Speicherpools

Auf dieser Seite werden die Datenstrukturen und Methoden beschrieben, die zur effizienten Kommunikation von Operandenpuffern zwischen dem Treiber und dem Framework verwendet werden.

Zur Modellkompilierungszeit stellt das Framework dem Treiber die Werte der konstanten Operanden bereit. Abhängig von der Lebensdauer des konstanten Operanden befinden sich seine Werte entweder in einem HIDL-Vektor oder einem Shared Memory Pool.

  • Wenn die Lebensdauer ist CONSTANT_COPY , werden die Werte in der Region operandValues Feld der Modellstruktur. Da die Werte in dem HIDL Vektor während der Interprozesskommunikation (IPC) kopiert werden, dies in der Regel nur verwendet, um eine kleine Menge von Daten , wie beispielsweise skalaren Operanden (beispielsweise die Aktivierung skalare in halte ADD ) und kleine tensor Parameter (zB die Form Tensor in RESHAPE ).
  • Wenn die Lebensdauer ist CONSTANT_REFERENCE , werden die Werte in der Region pools Feld der Modellstruktur. Beim IPC werden nur die Handles der Shared Memory Pools dupliziert, anstatt die Rohwerte zu kopieren. Daher ist es effizienter, eine große Datenmenge (z. B. die Gewichtungsparameter in Faltungen) unter Verwendung gemeinsamer Speicherpools als HIDL-Vektoren zu speichern.

Zur Modellausführungszeit stellt das Framework dem Treiber die Puffer der Eingabe- und Ausgabeoperanden zur Verfügung. Im Gegensatz zu den Kompilierzeitkonstanten, die in einem HIDL-Vektor gesendet werden können, werden die Eingabe- und Ausgabedaten einer Ausführung immer über eine Sammlung von Speicherpools kommuniziert.

Der HIDL Datentyp hidl_memory wird sowohl Kompilierung und Ausführung verwendet , um einen nicht zugeordneten gemeinsam genutzten Speicher - Pool zu repräsentieren. Der Fahrer sollte das Speicherabbild entsprechend auf dem Namen des Basis zu machen verwendbare hidl_memory Datentypen. Die unterstützten Speichernamen sind:

  • ashmem : Android Shared Memory. Weitere Einzelheiten finden Sie Speicher .
  • mmap_fd : Shared Memory unterstützt von einem Dateideskriptor durch mmap .
  • hardware_buffer_blob : Shared Memory unterstützt durch einen AHardwareBuffer mit dem Format AHARDWARE_BUFFER_FORMAT_BLOB . Erhältlich bei Neuronalen Netzen (NN) HAL 1.2. Weitere Einzelheiten finden Sie AHardwareBuffer .
  • hardware_buffer : Shared Memory gesichert durch eine allgemeine AHardwareBuffer , die nicht das Format nicht verwendet AHARDWARE_BUFFER_FORMAT_BLOB . Der Hardwarepuffer im Nicht-BLOB-Modus wird nur bei der Modellausführung unterstützt. Verfügbar ab NN HAL 1.2. Weitere Einzelheiten finden Sie AHardwareBuffer .

Ab NN HAL 1.3 unterstützt NNAPI Speicherdomänen, die Zuweisungsschnittstellen für treiberverwaltete Puffer bereitstellen. Die treiberverwalteten Puffer können auch als Ausführungseingänge oder -ausgänge verwendet werden. Weitere Einzelheiten finden Sie Speicherdomänen .

NNAPI Fahrer müssen Mapping von Unterstützung ashmem und mmap_fd Speicher Namen. Von NN HAL 1.3 - Treiber müssen auch Kartierung unterstützen hardware_buffer_blob . Unterstützung für die allgemeinen Nicht-BLOB - Modus hardware_buffer und Speicherdomänen ist optional.

AHardwarePuffer

AHardwareBuffer ist eine Art von gemeinsam genutzten Speicher, der einen Wraps Gralloc Puffer . In Android 10 unter Verwendung des Neural Networks API (NNAPI) Unterstützt AHardwareBuffer , so dass die Fahrer Ausführungen durchzuführen , ohne das Kopieren von Daten, die für Anwendungen Leistung und Stromverbrauch verbessert. Beispielsweise kann ein Kamera-HAL-Stack AHardwareBuffer-Objekte an die NNAPI für Machine-Learning-Workloads unter Verwendung von AHardwareBuffer-Handles, die von Kamera-NDK- und Medien-NDK-APIs generiert werden, übergeben. Weitere Informationen finden Sie ANeuralNetworksMemory_createFromAHardwareBuffer .

AHardwareBuffer Objekte in NNAPI verwendet werden , um den Fahrer über eine bestandene hidl_memory Struktur entweder namens hardware_buffer oder hardware_buffer_blob . Die hidl_memory struct hardware_buffer_blob stellt nur AHardwareBuffer Objekte mit dem AHARDWAREBUFFER_FORMAT_BLOB Format.

Die Informationen , die von Rahmen erforderlich sind , in dem codierten hidl_handle Feld der hidl_memory Struct. Die hidl_handle Feld wickelt native_handle , das alle der erforderlichen Metadaten über AHardwareBuffer oder Gralloc Puffer kodiert.

Der Fahrer muss die mitgelieferte richtig dekodieren hidl_handle Feld und den Zugang der Speicher durch beschrieben hidl_handle . Wenn die getSupportedOperations_1_2 , getSupportedOperations_1_1 oder getSupportedOperations Methode aufgerufen wird, soll der Fahrer erkennen , ob er die vorgesehen dekodieren kann hidl_handle und Zugriff auf den Speicher durch beschriebene hidl_handle . Die Modellvorbereitung muss fehlschlagen , wenn das hidl_handle Feld für einen konstanten Operanden verwendet wird , nicht unterstützt. Die Ausführung muß scheitern , wenn das hidl_handle Feld für eine Eingangs- oder Ausgangsoperanden der Ausführung verwendet wird , nicht unterstützt. Es ist für den Fahrer empfohlen , eine Rückkehr GENERAL_FAILURE fehlschlägt Fehlercode , wenn das Modell Vorbereitung oder Ausführung.

Speicherdomänen

Bei Geräten mit Android 11 oder höher unterstützt NNAPI Speicherdomänen, die Zuweisungsschnittstellen für treiberverwaltete Puffer bereitstellen. Dies ermöglicht die Weitergabe von geräteeigenen Speichern über Ausführungen hinweg, wodurch unnötiges Kopieren und Umwandeln von Daten zwischen aufeinanderfolgenden Ausführungen auf demselben Treiber verhindert wird. Dieser Ablauf ist in Abbildung 1 dargestellt.

Pufferdatenfluss mit und ohne Speicherdomänen
Abbildung 1. Pufferdaten - Speicherdomänen Fluss unter Verwendung von

Die Speicherdomänenfunktion ist für Tensoren gedacht, die größtenteils treiberintern sind und keinen häufigen Zugriff auf der Clientseite benötigen. Beispiele für solche Tensoren umfassen die Zustandstensoren in Sequenzmodellen. Für Tensoren, die auf der Clientseite häufigen CPU-Zugriff benötigen, ist es vorzuziehen, gemeinsam genutzte Speicherpools zu verwenden.

Um die Speicherdomäne Funktion zu unterstützen, implementieren IDevice::allocate den Rahmen Anforderung für Fahrer-Managed Pufferzuweisung zu ermöglichen. Bei der Zuweisung stellt das Framework die folgenden Eigenschaften und Nutzungsmuster für den Puffer bereit:

  • BufferDesc beschreibt die erforderlichen Eigenschaften des Puffers.
  • BufferRole beschreibt die möglichen Nutzungsmuster des Puffers als Eingang oder Ausgang eines vorbereiteten Modells. Während der Pufferzuweisung können mehrere Rollen angegeben werden, und der zugewiesene Puffer kann nur als diese angegebenen Rollen verwendet werden.

Der zugewiesene Puffer ist treiberintern. Ein Treiber kann einen beliebigen Pufferspeicherort oder ein beliebiges Datenlayout wählen. Wenn der Puffer erfolgreich zugeordnet wird, kann der Client des Fahrers Referenz oder interagieren mit dem Puffer , um den zurück Token oder die Verwendung von IBuffer - Objekt.

Der Token von IDevice::allocate bereitgestellt wird , wenn der Puffer als eine der Referenzierung MemoryPool - Objekte in der Request Struktur einer Ausführung. Um zu verhindern, dass ein Prozess versucht, auf den in einem anderen Prozess zugewiesenen Puffer zuzugreifen, muss der Treiber bei jeder Verwendung des Puffers eine ordnungsgemäße Validierung anwenden. Der Fahrer muss bestätigen , dass die Puffernutzung eine der ist BufferRole Rollen bei der Zuordnung zur Verfügung gestellt und muss die Ausführung sofort fehlschlagen , wenn die Nutzung illegal ist.

Das IBuffer - Objekt wird für explizites Gedächtnis Kopieren verwendet. In bestimmten Situationen muss der Client des Treibers den vom Treiber verwalteten Puffer aus einem gemeinsam genutzten Speicherpool initialisieren oder den Puffer in einen gemeinsam genutzten Speicherpool kopieren. Beispiele für Anwendungsfälle sind:

  • Initialisierung des Zustandstensors
  • Zwischenergebnisse zwischenspeichern
  • Fallback-Ausführung auf CPU

Um diese Anwendungsfälle zu unterstützen, muss der Treiber implementieren IBuffer::copyTo und IBuffer::copyFrom mit ashmem , mmap_fd und hardware_buffer_blob wenn es Speicherdomainvergabe unterstützt. Es ist optional für den Fahrer nicht-BLOB - Modus zu unterstützen hardware_buffer .

Während Pufferzuweisung können die Abmessungen des Puffers aus den entsprechenden Modell - Operanden aller Rollen durch Angabe abgeleitet werden BufferRole , und die in der vorgesehenen Abmessungen BufferDesc . Wenn alle Dimensionsinformationen kombiniert werden, kann der Puffer unbekannte Dimensionen oder einen unbekannten Rang haben. In einem solchen Fall befindet sich der Puffer in einem flexiblen Zustand, in dem die Abmessungen bei der Verwendung als Modelleingabe festgelegt sind, und in einem dynamischen Zustand, wenn sie als Modellausgabe verwendet wird. Derselbe Puffer kann mit unterschiedlichen Ausgabeformen in verschiedenen Ausführungen verwendet werden und der Treiber muss die Puffergröße richtig handhaben.

Die Speicherdomäne ist eine optionale Funktion. Ein Treiber kann aus verschiedenen Gründen feststellen, dass er eine bestimmte Zuweisungsanforderung nicht unterstützen kann. Zum Beispiel:

  • Der angeforderte Puffer hat eine dynamische Größe.
  • Der Treiber hat Speicherbeschränkungen, die ihn daran hindern, große Puffer zu verarbeiten.

Es ist möglich, dass mehrere verschiedene Threads gleichzeitig aus dem vom Treiber verwalteten Puffer lesen. Der gleichzeitige Zugriff auf den Puffer zum Schreiben oder Lesen/Schreiben ist undefiniert, aber er darf den Treiberdienst nicht zum Absturz bringen oder den Aufrufer auf unbestimmte Zeit blockieren. Der Treiber kann einen Fehler zurückgeben oder den Inhalt des Puffers in einem unbestimmten Zustand belassen.