Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Ejecuciones en ráfagas y colas de mensajes rápidos

Neural Networks HAL 1.2 introduce el concepto de ejecuciones en ráfagas. Las ejecuciones en ráfaga son una secuencia de ejecuciones del mismo modelo preparado que ocurren en rápida sucesión, como las que operan en cuadros de una captura de cámara o muestras de audio sucesivas. Un objeto de ráfaga se utiliza para controlar un conjunto de ejecuciones de ráfaga y para preservar los recursos entre ejecuciones, lo que permite que las ejecuciones tengan una sobrecarga más baja. Los objetos de ráfaga permiten tres optimizaciones:

  1. Un objeto de ráfaga se crea antes de una secuencia de ejecuciones y se libera cuando la secuencia ha finalizado. Debido a esto, la vida útil del objeto de ráfaga le indica al controlador cuánto tiempo debe permanecer en un estado de alto rendimiento.
  2. Un objeto de ráfaga puede conservar recursos entre ejecuciones. Por ejemplo, un controlador puede mapear un objeto de memoria en la primera ejecución y almacenar en caché el mapeo en el objeto de ráfaga para reutilizarlo en ejecuciones posteriores. Cualquier recurso almacenado en caché se puede liberar cuando el objeto de ráfaga se destruye o cuando el tiempo de ejecución de NNAPI notifica al objeto de ráfaga que el recurso ya no es necesario.
  3. Un objeto de ráfaga utiliza mensaje rápido colas (FMQs) para la comunicación entre procesos de aplicaciones y controladores. Esto puede reducir la latencia porque el FMQ omite HIDL y pasa datos directamente a otro proceso a través de un FIFO circular atómico en la memoria compartida. El proceso del consumidor sabe sacar un artículo de la cola y comenzar a procesar, ya sea sondeando el número de elementos en el FIFO o esperando en el indicador de evento del FMQ, que es señalado por el productor. Este indicador de evento es un mutex de espacio de usuario rápido (futex).

Un FMQ es una estructura de datos de bajo nivel que no ofrece garantías de por vida en todos los procesos y no tiene un mecanismo incorporado para determinar si el proceso en el otro extremo del FMQ se está ejecutando como se esperaba. En consecuencia, si el productor de FMQ muere, el consumidor puede quedarse atascado esperando datos que nunca llegan. Una solución a este problema es que el controlador asocie los FMQ con el objeto de ráfaga de nivel superior para detectar cuándo ha finalizado la ejecución de la ráfaga.

Debido a que las ejecuciones en ráfagas operan con los mismos argumentos y devuelven los mismos resultados que otras rutas de ejecución, los FMQ subyacentes deben pasar los mismos datos hacia y desde los controladores de servicio NNAPI. Sin embargo, los FMQ solo pueden transferir tipos de datos antiguos sin formato. La transferencia de datos complejos se logra serializando y deserializando búferes anidados (tipos de vector) directamente en los FMQ y utilizando objetos de devolución de llamada HIDL para transferir identificadores de agrupaciones de memoria a pedido. El lado del productor FMQ debe enviar los mensajes de petición o de resultados al consumidor de forma atómica utilizando MessageQueue::writeBlocking si la cola está bloqueando, o mediante el uso de MessageQueue::write si la cola está sin bloqueo.

Interfaces de ráfaga

Las interfaces de ráfaga para la Redes Neuronales HAL se encuentran en hardware/interfaces/neuralnetworks/1.2/ y se describen a continuación. Para más información sobre las interfaces de ráfaga en la capa de NDK, ver frameworks/ml/nn/runtime/include/NeuralNetworks.h .

tipos.hal

types.hal define el tipo de datos que se envían a través de la FMQ.

  • FmqRequestDatum : Un solo elemento de una representación serializada de una ejecución Request de objeto y una MeasureTiming valor, que se envía a través de la cola de mensajes rápido.
  • FmqResultDatum : Un solo elemento de una representación en serie de los valores de regresar de una ejecución ( ErrorStatus , OutputShapes , y Timing ), que se devuelve a través de la cola de mensajes rápido.

IBurstContext.hal

IBurstContext.hal define el objeto de interfaz HIDL que vive en el servicio de Redes Neuronales.

  • IBurstContext : Contexto objeto de gestionar los recursos de una ráfaga.

IBurstCallback.hal

IBurstCallback.hal define el objeto de interfaz de HIDL para una devolución de llamada creado por el tiempo de ejecución de redes neuronales y es utilizado por el servicio de redes neuronales para recuperar hidl_memory objetos correspondientes a los identificadores de ranura.

  • IBurstCallback : objeto de devolución de llamada utilizado por un servicio para recuperar objetos de memoria.

IPreparedModel.hal

IPreparedModel.hal se extiende en HAL 1,2 con un método para crear un IBurstContext objeto a partir de un modelo preparado.

  • configureExecutionBurst : configura un objeto ráfaga utiliza para ejecutar múltiples inferencias en un modelo preparado en rápida sucesión.

Apoyando ejecuciones en ráfaga en un controlador

La forma más sencilla para apoyar objetos estalló en un servicio HIDL NNAPI es utilizar la función de utilidad estallido ::android::nn::ExecutionBurstServer::create , que se encuentra en ExecutionBurstServer.h y envasado en la libneuralnetworks_common y libneuralnetworks_util bibliotecas estáticas. Esta función de fábrica tiene dos sobrecargas:

  • Una sobrecarga acepta un puntero a un IPreparedModel objeto. Esta función de utilidad utiliza el executeSynchronously método en un IPreparedModel objeto de ejecutar el modelo.
  • Una sobrecarga acepta una adaptable IBurstExecutorWithCache objeto, que puede ser utilizado a los recursos de memoria caché (como hidl_memory asignaciones) que persisten a través de múltiples ejecuciones.

Cada sobrecarga devuelve un IBurstContext objeto (que representa el objeto de ráfaga) que contiene y administra su propio hilo oyente dedicado. Este hilo recibe solicitudes de la requestChannel FMQ, realiza la inferencia, a continuación, devuelve los resultados a través de la resultChannel FMQ. Este hilo y todos los demás recursos contenidos en el IBurstContext objeto se liberan automáticamente cuando el cliente del estallido pierde su referencia a IBurstContext .

Como alternativa, puede crear su propia implementación de IBurstContext que entiende cómo enviar y recibir mensajes a través de la requestChannel y resultChannel FMQs pasó a IPreparedModel::configureExecutionBurst .

Las funciones de utilidad de ráfaga se encuentran en 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);

La siguiente es una implementación de referencia de una interfaz de ráfaga encuentra en el controlador de ejemplo redes neuronales en 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();
}