Pool di memoria

Questa pagina descrive le strutture di dati ei metodi utilizzati per comunicare in modo efficiente i buffer degli operandi tra il driver e il framework.

Al momento della compilazione del modello, il framework fornisce i valori degli operandi costanti al driver. A seconda della durata dell'operando costante, i suoi valori si trovano in un vettore HIDL o in un pool di memoria condivisa.

  • Se la durata è CONSTANT_COPY , i valori si trovano nel operandValues campo della struttura del modello. Poiché i valori nel vettore HIDL vengono copiati durante la comunicazione interprocesso (IPC), questa è tipicamente utilizzata solo per contenere una piccola quantità di dati, come operandi scalari (ad esempio, lo scalare attivazione ADD ) e parametri tensore piccoli (per esempio, il tensore forma nella RESHAPE ).
  • Se la durata è CONSTANT_REFERENCE , i valori si trovano nel pools campo della struttura del modello. Durante l'IPC vengono duplicati solo gli handle dei pool di memoria condivisa anziché copiare i valori non elaborati. Pertanto, è più efficiente conservare una grande quantità di dati (ad esempio, i parametri di peso nelle convoluzioni) utilizzando i pool di memoria condivisi rispetto ai vettori HIDL.

Al momento dell'esecuzione del modello, il framework fornisce i buffer degli operandi di input e output al driver. A differenza delle costanti in fase di compilazione che potrebbero essere inviate in un vettore HIDL, i dati di input e output di un'esecuzione vengono sempre comunicati tramite una raccolta di pool di memoria.

Il tipo di dati HIDL hidl_memory viene usato sia compilazione e l'esecuzione di rappresentare una piscina non mappato memoria condivisa. Il conducente deve mappare la memoria di conseguenza per renderlo utilizzabile basato sul nome del hidl_memory tipo di dati. I nomi di memoria supportati sono:

  • ashmem : Android la memoria condivisa. Per maggiori dettagli, vedere la memoria .
  • mmap_fd : memoria condivisa supportata da un descrittore di file attraverso mmap .
  • hardware_buffer_blob : memoria condivisa sostenuta da un'AHardwareBuffer con il formato AHARDWARE_BUFFER_FORMAT_BLOB . Disponibile da Reti Neurali (NN) HAL 1.2. Per maggiori dettagli, vedere AHardwareBuffer .
  • hardware_buffer : memoria condivisa sostenuta da un'AHardwareBuffer generale, che non utilizza il formato AHARDWARE_BUFFER_FORMAT_BLOB . Il buffer hardware in modalità non BLOB è supportato solo nell'esecuzione del modello. Disponibile da NN HAL 1.2. Per maggiori dettagli, vedere AHardwareBuffer .

Da NN HAL 1.3, NNAPI supporta i domini di memoria che forniscono interfacce di allocazione per i buffer gestiti dal driver. I buffer gestiti dal driver possono essere utilizzati anche come input o output di esecuzione. Per maggiori dettagli, vedere i domini di memoria .

Driver NNAPI devono supportare la mappatura di ashmem e mmap_fd nomi memoria. Da NN HAL 1.3, i driver devono supportare la mappatura di hardware_buffer_blob . Il supporto per la modalità generali non BLOB hardware_buffer domini e la memoria è facoltativa.

AHardwareBuffer

AHardwareBuffer è un tipo di memoria condivisa che avvolge un tampone Gralloc . In Android 10, supporti API Neural Networks (NNAPI) usando AHardwareBuffer , consentendo al guidatore di effettuare esecuzioni senza copiare i dati, migliorando prestazioni e il consumo di potenza per applicazioni. Ad esempio, uno stack HAL della fotocamera può passare oggetti AHardwareBuffer a NNAPI per i carichi di lavoro di machine learning utilizzando gli handle AHardwareBuffer generati dalle API NDK della fotocamera e media NDK. Per ulteriori informazioni, vedere ANeuralNetworksMemory_createFromAHardwareBuffer .

AHardwareBuffer oggetti utilizzati nei NNAPI sono passati al conducente attraverso un hidl_memory struct nome sia hardware_buffer o hardware_buffer_blob . Il hidl_memory struct hardware_buffer_blob rappresenta solo oggetti AHardwareBuffer con AHARDWAREBUFFER_FORMAT_BLOB formato.

Le informazioni richieste dal quadro è codificato nel hidl_handle campo della hidl_memory struct. Il hidl_handle campo avvolge native_handle , che codifica tutti i metadati necessari circa AHardwareBuffer o tampone Gralloc.

Il conducente deve decodificare correttamente il fornito hidl_handle campo e l'accesso alla memoria descritta da hidl_handle . Quando il getSupportedOperations_1_2 , getSupportedOperations_1_1 o getSupportedOperations metodo viene chiamato, il conducente deve rilevare se può decodificare il fornito hidl_handle e accedere alla memoria descritta da hidl_handle . La preparazione modello deve riuscire se la hidl_handle campo utilizzato per una costante operando non è supportato. L'esecuzione deve riuscire se il hidl_handle campo utilizzato come operando ingresso o uscita dell'esecuzione non è supportato. E 'consigliato per il guidatore per restituire un GENERAL_FAILURE codice di errore se la preparazione del modello o esecuzione non riesce.

Domini di memoria

Per i dispositivi che eseguono Android 11 o versioni successive, NNAPI supporta i domini di memoria che forniscono interfacce di allocazione per i buffer gestiti dal driver. Ciò consente di passare le memorie native del dispositivo tra le esecuzioni, sopprimendo la copia e la trasformazione dei dati non necessari tra esecuzioni consecutive sullo stesso driver. Questo flusso è illustrato nella Figura 1.

Flusso di dati buffer con e senza domini di memoria
Dati Figura 1. Buffer flusso utilizzando domini memoria

La funzionalità del dominio di memoria è destinata ai tensori che sono per lo più interni al driver e non richiedono un accesso frequente sul lato client. Esempi di tali tensori includono i tensori di stato nei modelli di sequenza. Per i tensori che richiedono un accesso frequente alla CPU sul lato client, è preferibile utilizzare i pool di memoria condivisi.

Per supportare la funzionalità dominio memoria, implementare IDevice::allocate per consentire il quadro di richiesta di allocazione del buffer conducente gestiti. Durante l'allocazione, il framework fornisce le seguenti proprietà e modelli di utilizzo per il buffer:

  • BufferDesc descrive le proprietà richieste del buffer.
  • BufferRole descrive il potenziale modello di utilizzo del buffer come ingresso o uscita di un modello preparato. È possibile specificare più ruoli durante l'allocazione del buffer e il buffer allocato può essere utilizzato solo come quei ruoli specificati.

Il buffer allocato è interno al driver. Un conducente può scegliere qualsiasi posizione del buffer o layout dei dati. Quando il buffer è allocata con successo, il client del conducente può fare riferimento o interagiscono con il tampone con i token restituito o IBuffer oggetto.

Il token da IDevice::allocate è previsto per fare riferimento al buffer come uno dei MemoryPool oggetti nella Request struttura di un esecuzione. Per evitare che un processo tenti di accedere al buffer allocato in un altro processo, il driver deve applicare la convalida appropriata a ogni utilizzo del buffer. Il conducente deve convalidare che l'utilizzo del buffer è uno dei BufferRole ruoli forniti durante l'assegnazione e deve sicuro l'esecuzione immediatamente se l'utilizzo è illegale.

IBuffer oggetto viene utilizzato per la copia della memoria esplicita. In determinate situazioni, il client del driver deve inizializzare il buffer gestito dal driver da un pool di memoria condivisa o copiare il buffer in un pool di memoria condivisa. I casi d'uso di esempio includono:

  • Inizializzazione del tensore di stato
  • Memorizzazione nella cache dei risultati intermedi
  • Esecuzione di fallback sulla CPU

A sostegno di questi casi d'uso, il driver deve implementare IBuffer::copyTo e IBuffer::copyFrom con ashmem , mmap_fd e hardware_buffer_blob se supporta l'allocazione della memoria dominio. E 'facoltativo per il driver per supportare la modalità non-BLOB hardware_buffer .

Durante tampone allocazione, le dimensioni del buffer possono essere dedotte dai corrispondenti operandi modello di tutti i ruoli specificati da BufferRole , e le dimensioni fornite in BufferDesc . Con tutte le informazioni dimensionali combinate, il buffer potrebbe avere dimensioni o rango sconosciuti. In tal caso, il buffer si trova in uno stato flessibile in cui le dimensioni sono fisse quando viene utilizzato come input del modello e in uno stato dinamico quando viene utilizzato come output del modello. Lo stesso buffer può essere utilizzato con diverse forme di output in diverse esecuzioni e il driver deve gestire correttamente il ridimensionamento del buffer.

Il dominio di memoria è una funzionalità opzionale. Un driver può determinare che non può supportare una determinata richiesta di allocazione per una serie di motivi. Per esempio:

  • Il buffer richiesto ha una dimensione dinamica.
  • Il driver ha vincoli di memoria che gli impediscono di gestire buffer di grandi dimensioni.

È possibile che diversi thread leggano contemporaneamente dal buffer gestito dal driver. L'accesso simultaneo al buffer per la scrittura o la lettura/scrittura non è definito, ma non deve arrestare il servizio driver o bloccare il chiamante a tempo indeterminato. Il driver può restituire un errore o lasciare il contenuto del buffer in uno stato indeterminato.