Neural Networks HAL 1.2 introduit le concept d'exécutions par rafales. Rafale les exécutions sont une séquence d'exécutions du même modèle préparé qui ont lieu dans succession rapide (par exemple, celles qui fonctionnent sur les images d'une capture d'appareil photo ou audio successifs échantillons. Un objet "burst" permet de contrôler un ensemble d'exécutions de "burst" et de préserver les ressources entre les exécutions, ce qui permet de réduire les coûts. Les objets "Burst" permettent trois optimisations :
- Un objet d'utilisation intensive est créé avant une séquence d'exécutions et libéré à la fin de la séquence. La durée de vie de l'utilisation intensive des indications d'objet indiquant au pilote combien de temps il doit rester dans un environnement de l'état.
- Un objet burst peut préserver des ressources entre les exécutions. Par exemple, un le pilote peut mapper un objet mémoire lors de la première exécution et mettre en cache le mappage dans l'objet d'utilisation intensive afin de le réutiliser lors d'exécutions ultérieures. Toute ressource mise en cache peut être libérée lorsque l'objet burst est détruit ou lorsque l'environnement d'exécution NNAPI informe l'objet burst que la ressource n'est plus requise.
- Un objet de rafale utilise des files d'attente de messages rapides (FMQ) pour communiquer entre les processus d'application et de pilote. Cela peut réduire la latence, car le FMQ contourne HIDL et transmet les données directement à un autre processus via un FIFO circulaire atomique en mémoire partagée. La le processus consommateur sait qu'il doit retirer un élément de la file d'attente et commencer le traitement en interrogeant le nombre d'éléments dans le FIFO ou en attendant l'événement FMQ qui est signalé par le producteur. Cet indicateur d'événement est mutex (futex) de l’espace utilisateur.
Une file de messages de faible niveau est une structure de données qui n'offre aucune garantie de durée de vie entre les processus et ne dispose d'aucun mécanisme intégré pour déterminer si le processus à l'autre extrémité de la file de messages fonctionne comme prévu. Par conséquent, si le producteur de la file de messages en file d'attente meurt, le consommateur peut être bloqué en attendant des données qui n'arrivent jamais. Un la solution à ce problème consiste à associer les FMQ aux un objet d'utilisation intensive de niveau supérieur pour détecter la fin de l'exécution en rafale.
Étant donné que les exécutions par rafales fonctionnent sur les mêmes arguments et renvoient les mêmes résultats que les autres chemins d'exécution, les FMQ sous-jacents doivent transmettre les mêmes données aux pilotes de service NNAPI et depuis eux. Toutefois, les files de messages de file d'attente ne peuvent transférer que des types de données simples. Le transfert de données complexes
est réalisé par la sérialisation
et désérialisation des tampons imbriqués (types de vecteurs) directement dans les FMQ, et à l'aide de
Objets de rappel HIDL pour transférer les poignées de pool de mémoire à la demande Le côté producteur de la file de messages de requêtes doit envoyer les messages de requête ou de résultat au consommateur de manière atomique à l'aide de MessageQueue::writeBlocking
si la file est bloquante, ou à l'aide de MessageQueue::write
si la file n'est pas bloquante.
Interfaces de rafale
Les interfaces de rafales pour le HAL des réseaux de neurones se trouvent dans hardware/interfaces/neuralnetworks/1.2/
et sont décrites ci-dessous. Pour en savoir plus sur les interfaces d'utilisation intensive dans le NDK,
, consultez
frameworks/ml/nn/runtime/include/NeuralNetworks.h
types.hal
types.hal
définit le type de données envoyées via le FMQ.
FmqRequestDatum
: Élément unique d'une représentation sérialisée d'une exécutionRequest
et une valeurMeasureTiming
, qui est envoyée via le service de messagerie rapide file d'attente.FmqResultDatum
: élément unique d'une représentation sérialisée des valeurs renvoyées à partir d'une exécution (ErrorStatus
,OutputShapes
etTiming
), qui est renvoyée via la file d'attente de messages rapide.
IBurstContext.hal
IBurstContext.hal
définit l'objet d'interface HIDL qui se trouve dans le service de réseaux de neurones.
IBurstContext
: Contexte permettant de gérer les ressources d'une utilisation intensive.
IBurstCallback.hal
IBurstCallback.hal
définit l'objet d'interface HIDL pour un rappel créé par l'environnement d'exécution des réseaux de neurones et est utilisé par le service de réseaux de neurones pour récupérer les objets hidl_memory
correspondant aux identifiants de fente.
- IBurstCallback: Objet de rappel utilisé par un service pour récupérer des objets mémoire.
IPreparedModel.hal
IPreparedModel.hal
est étendu dans HAL 1.2 avec une méthode permettant de créer un objet IBurstContext
à partir d'un
le modèle préparé.
configureExecutionBurst
: Configure un objet d'utilisation intensive utilisé pour exécuter plusieurs inférences sur un objet à la suite d'un modèle.
Prendre en charge les exécutions en rafale dans un pilote
Le moyen le plus simple de prendre en charge les objets en rafale dans un service HIDL NNAPI consiste à utiliser la classe
la fonction utilitaire d'utilisation intensive ::android::nn::ExecutionBurstServer::create
,
trouvé dans
ExecutionBurstServer.h
et empaquetées dans libneuralnetworks_common
et libneuralnetworks_util
bibliothèques statiques. Cette fonction d'usine comporte deux surcharges :
- Une surcharge accepte un pointeur vers un objet
IPreparedModel
. Cette fonction utilitaire utilise la méthodeexecuteSynchronously
dans un objetIPreparedModel
pour exécuter le modèle. - Une surcharge accepte un objet
IBurstExecutorWithCache
personnalisable, qui peut être utilisé pour mettre en cache des ressources (telles que des mappageshidl_memory
) qui persistent sur plusieurs exécutions.
Chaque surcharge renvoie un objet IBurstContext
(qui représente l'utilisation intensive).
) qui contient et gère son propre thread d'écoute dédié. Ce fil de discussion
reçoit les requêtes du FMQ requestChannel
, effectue l'inférence, puis
renvoie les résultats via le FMQ resultChannel
. Ce fil de discussion et tous les autres
les ressources contenues dans l'objet IBurstContext
sont automatiquement libérées
Lorsque le client de la rafale perd sa référence à IBurstContext
.
Vous pouvez également créer votre propre implémentation de IBurstContext
qui
comprenne comment envoyer et recevoir des messages via requestChannel
et
resultChannel
FMQ transmis à IPreparedModel::configureExecutionBurst
.
Les fonctions utilitaires de rafales se trouvent dans ExecutionBurstServer.h
.
/**
* Create automated context to manage FMQ-based executions.
*
* This function is intended to be used by a service to automatically:
* 1) Receive data from a provided FMQ
* 2) Execute a model with the given information
* 3) Send the result to the created FMQ
*
* @param callback Callback used to retrieve memories corresponding to
* unrecognized slots.
* @param requestChannel Input FMQ channel through which the client passes the
* request to the service.
* @param resultChannel Output FMQ channel from which the client can retrieve
* the result of the execution.
* @param executorWithCache Object which maintains a local cache of the
* memory pools and executes using the cached memory pools.
* @result IBurstContext Handle to the burst context.
*/
static sp<ExecutionBurstServer> create(
const sp<IBurstCallback>& callback, const FmqRequestDescriptor& requestChannel,
const FmqResultDescriptor& resultChannel,
std::shared_ptr<IBurstExecutorWithCache> executorWithCache);
/**
* Create automated context to manage FMQ-based executions.
*
* This function is intended to be used by a service to automatically:
* 1) Receive data from a provided FMQ
* 2) Execute a model with the given information
* 3) Send the result to the created FMQ
*
* @param callback Callback used to retrieve memories corresponding to
* unrecognized slots.
* @param requestChannel Input FMQ channel through which the client passes the
* request to the service.
* @param resultChannel Output FMQ channel from which the client can retrieve
* the result of the execution.
* @param preparedModel PreparedModel that the burst object was created from.
* IPreparedModel::executeSynchronously will be used to perform the
* execution.
* @result IBurstContext Handle to the burst context.
*/
static sp<ExecutionBurstServer> create(const sp<IBurstCallback>& callback,
const FmqRequestDescriptor& requestChannel,
const FmqResultDescriptor& resultChannel,
IPreparedModel* preparedModel);
Voici une implémentation de référence d'une interface d'utilisation intensive disponible dans
Exemple de pilote de réseaux de neurones :
frameworks/ml/nn/driver/sample/SampleDriver.cpp
Return<void> SamplePreparedModel::configureExecutionBurst(
const sp<V1_2::IBurstCallback>& callback,
const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
configureExecutionBurst_cb cb) {
NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
"SampleDriver::configureExecutionBurst");
// Alternatively, the burst could be configured via:
// const sp<V1_2::IBurstContext> burst =
// ExecutionBurstServer::create(callback, requestChannel,
// resultChannel, this);
//
// However, this alternative representation does not include a memory map
// caching optimization, and adds overhead.
const std::shared_ptr<BurstExecutorWithCache> executorWithCache =
std::make_shared<BurstExecutorWithCache>(mModel, mDriver, mPoolInfos);
const sp<V1_2::IBurstContext> burst = ExecutionBurstServer::create(
callback, requestChannel, resultChannel, executorWithCache);
if (burst == nullptr) {
cb(ErrorStatus::GENERAL_FAILURE, {});
} else {
cb(ErrorStatus::NONE, burst);
}
return Void();
}