O Google tem o compromisso de promover a igualdade racial para as comunidades negras. Saiba como.

Execuções burst e filas de mensagens rápidas

Redes Neurais HAL 1.2 introduz o conceito de execuções em rajadas. As execuções de burst são uma sequência de execuções do mesmo modelo preparado que ocorrem em rápida sucessão, como aquelas que operam em quadros de uma captura de câmera ou amostras de áudio sucessivas. Um objeto de burst é usado para controlar um conjunto de execuções de burst e para preservar recursos entre as execuções, permitindo que as execuções tenham sobrecarga mais baixa. Os objetos burst permitem três otimizações:

  1. Um objeto burst é criado antes de uma sequência de execuções e liberado quando a sequência termina. Por causa disso, a vida útil do objeto de burst sugere ao driver quanto tempo ele deve permanecer em um estado de alto desempenho.
  2. Um objeto burst pode preservar recursos entre as execuções. Por exemplo, um 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 intermitência é destruído ou quando o tempo de execução NNAPI notifica o objeto de intermitência de que o recurso não é mais necessário.
  3. Um objeto explodiu usa mensagem rápida filas (FMQs) para a comunicação entre processos de aplicação e driver. Isso pode reduzir a latência porque o FMQ ignora o HIDL e passa os dados diretamente para outro processo por meio de um FIFO circular atômico na memória compartilhada. O processo do consumidor sabe desenfileirar um item e começar o processamento por meio do polling do número de elementos no FIFO ou aguardando o sinalizador de evento do FMQ, que é sinalizado pelo produtor. Este sinalizador de evento é um mutex de espaço de usuário rápido (futex).

Um FMQ é uma estrutura de dados de baixo nível que não oferece garantias de tempo de vida entre os processos e não tem nenhum mecanismo integrado para determinar se o processo na outra extremidade do FMQ está sendo executado conforme o esperado. Conseqüentemente, se o produtor do FMQ morrer, o consumidor pode ficar preso à espera de dados que nunca chegam. Uma solução para esse problema é o driver associar FMQs ao objeto de burst de nível superior para detectar quando a execução de burst terminou.

Como as execuções intermitentes operam com os mesmos argumentos e retornam os mesmos resultados de outros caminhos de execução, os FMQs subjacentes devem passar os mesmos dados de e para os drivers de serviço NNAPI. No entanto, os FMQs só podem transferir tipos de dados simples e antigos. A transferência de dados complexos é realizada serializando e desserializando buffers aninhados (tipos de vetor) diretamente nos FMQs e usando objetos de retorno de chamada HIDL para transferir identificadores de pool de memória sob demanda. O lado produtor do FMQ deve enviar o pedido ou resultar mensagens para o consumidor atomicamente usando MessageQueue::writeBlocking se a fila está bloqueando, ou usando MessageQueue::write se a fila é não bloqueante.

Interfaces de burst

As interfaces de rajada para o Redes Neuronais HAL são encontrados em hardware/interfaces/neuralnetworks/1.2/ e são descritos abaixo. Para mais informações sobre as interfaces de ruptura na camada NDK, ver frameworks/ml/nn/runtime/include/NeuralNetworks.h .

types.hal

types.hal define o tipo de dados que são enviados através da FMQ.

  • FmqRequestDatum : Um único elemento de uma representação serializada de uma execução Request objeto e um MeasureTiming valor, que é enviado através da fila de mensagens rápido.
  • FmqResultDatum : Um único elemento de uma representação em série dos valores devolvidos por uma execução ( ErrorStatus , OutputShapes , e Timing ), que é devolvido através da fila de mensagens rápido.

IBurstContext.hal

IBurstContext.hal define o objeto de interface HIDL que vive no serviço de redes neurais.

  • IBurstContext : objeto de contexto para gerir os recursos de uma explosão.

IBurstCallback.hal

IBurstCallback.hal define o objeto de interface HIDL para um retorno de chamada criado pelo tempo de execução Redes Neurais e é usado pelo serviço de redes neurais para recuperar hidl_memory objetos correspondentes aos identificadores de caça-níqueis.

  • IBurstCallback : Callback objeto usado por um serviço para recuperar objetos de memória.

IPreparedModel.hal

IPreparedModel.hal é estendido em 1,2 HAL com um método para criar um IBurstContext objecto a partir de um modelo preparado.

  • configureExecutionBurst : configura um objecto de ruptura utilizado para executar várias inferências sobre um modelo preparado em sucessão rápida.

Suportando execuções em rajada em um driver

A maneira mais simples para apoiar objetos de estourar na um serviço HIDL NNAPI é usar a função de utilidade explosão ::android::nn::ExecutionBurstServer::create , que é encontrado em ExecutionBurstServer.h e embalados na libneuralnetworks_common e libneuralnetworks_util bibliotecas estáticas. Esta função de fábrica tem duas sobrecargas:

  • Uma sobrecarga aceita um ponteiro para uma IPreparedModel objecto. Esta função de utilidade utiliza o executeSynchronously método em um IPreparedModel objecto para executar o modelo.
  • Uma sobrecarga aceita um personalizável IBurstExecutorWithCache objeto, que pode ser usado para recursos de cache (como hidl_memory mapeamentos) que persistem em várias execuções.

Cada sobrecarga retorna um IBurstContext objeto (que representa o objeto burst) que contém e administra seu próprio segmento ouvinte dedicado. Esta discussão recebe pedidos do requestChannel FMQ, executa a inferência, em seguida, devolve os resultados através da resultChannel FMQ. Esta discussão e todos os outros recursos contidos no IBurstContext objeto são automaticamente liberada quando o cliente da explosão perde sua referência a IBurstContext .

Alternativamente, você pode criar sua própria implementação de IBurstContext que entende como enviar e receber mensagens sobre o requestChannel e resultChannel FMQs passado para IPreparedModel::configureExecutionBurst .

As funções utilitárias explosão são encontrados 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);

O seguinte é uma implementação de referência de uma interface de explosão encontrada no condutor amostra Redes Neurais em 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();
}