A HAL 1.2 de redes neurais introduz o conceito de execuções burst. Explosão execuções são uma sequência de execuções do mesmo modelo preparado que ocorrem rápida sucessão, como aqueles que operam em frames de uma captura de câmera ou áudio consecutivo de amostra. Um objeto de burst é usado para controlar um conjunto de execuções de burst e para preservar recursos entre execuções, permitindo que elas tenham menor sobrecarga. Os objetos de ruptura permitem três otimizações:
- Um objeto de burst é criado antes de uma sequência de execuções e liberado quando a sequência terminar. Por isso, o ciclo de vida do burst dicas de objetos para um motorista por quanto tempo ele deve permanecer em um ambiente estado.
- Um objeto de burst pode preservar recursos entre execuções. Por exemplo, o driver pode mapear um objeto de memória na primeira execução e armazenar em cache o mapeamento no objeto de burst para reutilização em execuções subsequentes. Qualquer recurso armazenado em cache pode ser liberado quando o objeto de burst é destruído ou quando a NNAPI O ambiente de execução notifica ao objeto de burst que o recurso não é mais necessário.
- Um objeto de burst usa filas rápidas de mensagens (FMQs, na sigla em inglês) para comunicação entre os processos do app e do driver. Isso pode reduzir a latência porque a FMQ ignora o HIDL e passa os dados diretamente ao outro processo com um FIFO circular atômico na memória compartilhada. A o processo do consumidor sabe remover um item da fila e começar o processamento Pesquisando o número de elementos no FIFO ou aguardando o evento do FMQ que é sinalizada pelo produtor. Essa sinalização de evento é um sinal rápido exclusão múltipla do espaço do usuário (futex).
Uma FMQ é uma estrutura de dados de baixo nível que não oferece garantias de ciclo de vida e não tem um mecanismo integrado para determinar se o processo da a outra extremidade do FMQ está sendo executada conforme o esperado. Consequentemente, se o produtor do os dados de FMQ morrem, o consumidor pode ficar preso à espera de dados que nunca chegam. Uma solução para esse problema é que o motorista associe FMQs com o objeto de burst de alto nível para detectar quando a execução do burst terminou.
Como as execuções de burst operam nos mesmos argumentos e retornam os mesmos
que outros caminhos de execução, os FMQs subjacentes devem passar os mesmos dados ao
e dos drivers de serviço da NNAPI. No entanto, os FMQs só podem transferir
dados simples e antigos. A transferência de dados complexos é realizada com a serialização
e desserialização de buffers aninhados (tipos de vetores) diretamente nos FMQs e usando
Objetos de callback HIDL para transferir identificadores de pool de memória sob demanda. O produtor
do FMQ deve enviar as mensagens de solicitação ou de resultado ao consumidor
atomicamente usando MessageQueue::writeBlocking
se a fila estiver bloqueando, ou
usando MessageQueue::write
se a fila não tiver bloqueios.
Interfaces de burst
As interfaces de burst para a HAL de redes neurais são encontradas em
hardware/interfaces/neuralnetworks/1.2/
e estão descritas abaixo. Para mais informações sobre interfaces de burst no NDK
, consulte
frameworks/ml/nn/runtime/include/NeuralNetworks.h
type.hal
types.hal
define o tipo de dados enviados no FMQ.
FmqRequestDatum
: Um único elemento de uma representação serializada de uma execuçãoRequest
objeto e um valorMeasureTiming
, que é enviado pela mensagem rápida fila.FmqResultDatum
: Um único elemento de uma representação serializada dos valores retornados do uma execução (ErrorStatus
,OutputShapes
eTiming
), que é retornados pela fila de mensagens rápidas.
IBurstContext.hal
IBurstContext.hal
define o objeto de interface HIDL que reside no serviço de redes neurais.
IBurstContext
: Objeto de contexto para gerenciar os recursos de um burst.
IBurstCallback.hal
IBurstCallback.hal
define o objeto de interface HIDL para um callback criado pelas redes neurais
ambiente de execução e é usado pelo serviço de redes neurais para recuperar hidl_memory
objetos que correspondem aos identificadores de slot.
- IBurstCallback: Objeto de callback usado por um serviço para recuperar objetos de memória.
IPreparedModel.hal
IPreparedModel.hal
foi estendida na HAL 1.2 com um método para criar um objeto IBurstContext
a partir de um
preparado.
configureExecutionBurst
: Configura um objeto de burst usado para executar várias inferências em uma do modelo em rápida sucessão.
Oferecer suporte a execuções de burst em um driver
A maneira mais simples de oferecer suporte a objetos de burst em um serviço NNAPI HIDL é usar o
função utilitária de burst ::android::nn::ExecutionBurstServer::create
, que é
encontrado em
ExecutionBurstServer.h
e empacotados em libneuralnetworks_common
e libneuralnetworks_util
bibliotecas estáticas. Essa função de fábrica tem duas sobrecargas:
- Uma sobrecarga aceita um ponteiro para um objeto
IPreparedModel
. Isso função utilitária usa o métodoexecuteSynchronously
em uma ObjetoIPreparedModel
para executar o modelo. - Uma sobrecarga aceita um objeto
IBurstExecutorWithCache
personalizável, que pode ser usado para armazenar recursos em cache (como mapeamentoshidl_memory
) que persistem em várias execuções.
Cada sobrecarga retorna um objeto IBurstContext
, que representa o burst
) que contém e gerencia a própria linha de execução de listener dedicada. Esta conversa
recebe solicitações do FMQ requestChannel
, realiza a inferência e
retorna os resultados por meio da FMQ resultChannel
. Esta conversa e todas as outras
os recursos no objeto IBurstContext
são liberados automaticamente
quando o cliente do burst perde a referência a IBurstContext
.
Como alternativa, você pode criar sua própria implementação de IBurstContext
que
entende como enviar e receber mensagens pela requestChannel
e
resultChannel
FMQs transmitidos para IPreparedModel::configureExecutionBurst
.
As funções utilitárias de burst são encontradas em
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);
Veja a seguir uma implementação de referência de uma interface de burst encontrada na
O exemplo do driver de redes neurais
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();
}