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

Almacenamiento en caché de compilación

Desde Android 10, la API de redes neuronales (NNAPI) proporciona funciones para admitir el almacenamiento en caché de los artefactos de compilación, lo que reduce el tiempo utilizado para la compilación cuando se inicia una aplicación. Con esta función de almacenamiento en caché, el controlador no necesita administrar ni limpiar los archivos almacenados en caché. Esta es una característica opcional que se puede implementar con NN HAL 1.2. Para obtener más información sobre esta función, consulte ANeuralNetworksCompilation_setCaching .

El controlador también puede implementar el almacenamiento en caché de compilación independientemente de la NNAPI. Esto se puede implementar tanto si se utilizan como si no las funciones de almacenamiento en caché NNAPI NDK y HAL. AOSP proporciona una biblioteca de utilidades de bajo nivel (un motor de almacenamiento en caché). Para obtener más información, consulte Implementación de una herramienta de almacenamiento en caché .

Descripción general del flujo de trabajo

Esta sección describe los flujos de trabajo generales con la función de almacenamiento en caché de compilación implementada.

Información de almacenamiento en caché proporcionada y acierto en caché

  1. La aplicación pasa un directorio de almacenamiento en caché y una suma de comprobación única para el modelo.
  2. El tiempo de ejecución de NNAPI busca los archivos de caché según la suma de comprobación, la preferencia de ejecución y el resultado de la partición y encuentra los archivos.
  3. El NNAPI abre los archivos de caché y pasa los identificadores al conductor con prepareModelFromCache .
  4. El controlador prepara el modelo directamente a partir de los archivos de caché y devuelve el modelo preparado.

Almacenamiento en caché de la información proporcionada y falta de caché

  1. La aplicación pasa una suma de comprobación única para el modelo y un directorio de almacenamiento en caché.
  2. El tiempo de ejecución de NNAPI busca los archivos de almacenamiento en caché según la suma de comprobación, la preferencia de ejecución y el resultado de la partición, y no encuentra los archivos de caché.
  3. El NNAPI crea archivos de caché vacías sobre la base de la suma de comprobación, la preferencia de ejecución, y la partición, abre los archivos de caché, y pasa las asas y el modelo para el conductor con prepareModel_1_2 .
  4. El controlador compila el modelo, escribe información de almacenamiento en caché en los archivos de caché y devuelve el modelo preparado.

Información de almacenamiento en caché no proporcionada

  1. La aplicación invoca la compilación sin proporcionar ninguna información de almacenamiento en caché.
  2. La aplicación no pasa nada relacionado con el almacenamiento en caché.
  3. El tiempo de ejecución NNAPI pasa el modelo para el conductor con prepareModel_1_2 .
  4. El controlador compila el modelo y devuelve el modelo preparado.

Almacenamiento de información en caché

La información de almacenamiento en caché que se proporciona a un controlador consta de un token y identificadores de archivos de caché.

Simbólico

El contador es un almacenamiento en caché de muestra de longitud Constant::BYTE_SIZE_OF_CACHE_TOKEN que identifica el modelo preparado. Del mismo modo se proporciona al guardar los archivos de caché con prepareModel_1_2 y recuperar el modelo preparado con prepareModelFromCache . El cliente del conductor debe elegir un token con una baja tasa de colisión. El conductor no puede detectar una colisión simbólica. Una colisión da como resultado una ejecución fallida o una ejecución exitosa que produce valores de salida incorrectos.

Manejadores de archivos de caché (dos tipos de archivos de caché)

Los dos tipos de archivos de caché son caché de datos y la memoria caché del modelo.

  • Caché de datos: Se utiliza para el almacenamiento en caché de datos constantes que incluyen tampones tensor previamente procesados y transformados. Una modificación a la caché de datos no debería tener un efecto peor que generar valores de salida incorrectos en el momento de la ejecución.
  • Caché del modelo: el almacenamiento en caché de datos sensibles a la seguridad como el código máquina ejecutable compilado en formato binario nativo del dispositivo. Una modificación en la caché del modelo podría afectar el comportamiento de ejecución del controlador y un cliente malintencionado podría hacer uso de esto para ejecutar más allá del permiso otorgado. Por lo tanto, el controlador debe verificar si la caché del modelo está dañada antes de preparar el modelo desde la caché. Para obtener más información, consulte Seguridad .

El conductor debe decidir cómo se distribuye la información de la caché entre los dos tipos de archivos de caché, e informar de la cantidad de archivos de caché que necesita para cada tipo con getNumberOfCacheFilesNeeded .

El tiempo de ejecución de NNAPI siempre abre identificadores de archivos de caché con permisos de lectura y escritura.

Seguridad

En el almacenamiento en caché de compilación, la caché del modelo puede contener datos sensibles a la seguridad, como código de máquina ejecutable compilado en el formato binario nativo del dispositivo. Si no se protege adecuadamente, una modificación de la caché del modelo puede afectar el comportamiento de ejecución del controlador. Debido a que el contenido de la caché se almacena en el directorio de la aplicación, el cliente puede modificar los archivos de la caché. Un cliente con errores puede dañar accidentalmente la caché y un cliente malintencionado podría hacer uso intencional de esto para ejecutar código no verificado en el dispositivo. Dependiendo de las características del dispositivo, esto puede ser un problema de seguridad. Por lo tanto, el controlador debe poder detectar posibles daños en la caché del modelo antes de preparar el modelo a partir de la caché.

Una forma de hacer esto es que el controlador mantenga un mapa del token a un hash criptográfico de la caché del modelo. El controlador puede almacenar el token y el hash de su modelo de caché al guardar la compilación en caché. El controlador verifica el nuevo hash de la caché del modelo con el token registrado y el par de hash cuando recupera la compilación de la caché. Esta asignación debe ser persistente en los reinicios del sistema. El conductor puede utilizar el servicio de Android almacén de claves , la biblioteca de utilidad en framework/ml/nn/driver/cache , o cualquier otro mecanismo adecuado para implementar un administrador de asignación. Tras la actualización del controlador, este administrador de mapeo debe reiniciarse para evitar la preparación de archivos de caché de una versión anterior.

Para evitar la hora del cheque para el tiempo de uso de ataques (TOCTOU), el conductor debe calcular el hash registrado antes de guardar en un archivo y calcular el hash de nuevo después de copiar el contenido de un archivo en un búfer interno.

Este código de muestra demuestra cómo implementar esta lógica.

bool saveToCache(const sp<V1_2::IPreparedModel> preparedModel,
                 const hidl_vec<hidl_handle>& modelFds, const hidl_vec<hidl_handle>& dataFds,
                 const HidlToken& token) {
    // Serialize the prepared model to internal buffers.
    auto buffers = serialize(preparedModel);

    // This implementation detail is important: the cache hash must be computed from internal
    // buffers instead of cache files to prevent time-of-check to time-of-use (TOCTOU) attacks.
    auto hash = computeHash(buffers);

    // Store the {token, hash} pair to a mapping manager that is persistent across reboots.
    CacheManager::get()->store(token, hash);

    // Write the cache contents from internal buffers to cache files.
    return writeToFds(buffers, modelFds, dataFds);
}

sp<V1_2::IPreparedModel> prepareFromCache(const hidl_vec<hidl_handle>& modelFds,
                                          const hidl_vec<hidl_handle>& dataFds,
                                          const HidlToken& token) {
    // Copy the cache contents from cache files to internal buffers.
    auto buffers = readFromFds(modelFds, dataFds);

    // This implementation detail is important: the cache hash must be computed from internal
    // buffers instead of cache files to prevent time-of-check to time-of-use (TOCTOU) attacks.
    auto hash = computeHash(buffers);

    // Validate the {token, hash} pair by a mapping manager that is persistent across reboots.
    if (CacheManager::get()->validate(token, hash)) {
        // Retrieve the prepared model from internal buffers.
        return deserialize<V1_2::IPreparedModel>(buffers);
    } else {
        return nullptr;
    }
}

Casos de uso avanzados

En ciertos casos de uso avanzado, un controlador requiere acceso al contenido de la caché (lectura o escritura) después de la llamada de compilación. Los casos de uso de ejemplo incluyen:

  • Compilación en tiempo de ejecución: La compilación se retrasa hasta la primera ejecución.
  • La compilación de varias etapas: una recopilación rápida se realiza al principio y una compilación optimizada opcional se lleva a cabo en un momento posterior en función de la frecuencia de uso.

Para acceder al contenido de la caché (lectura o escritura) después de la llamada de compilación, asegúrese de que el controlador:

  • Duplica los identificadores de archivo durante la invocación de prepareModel_1_2 o prepareModelFromCache y lee / actualiza el contenido de la caché en un momento posterior.
  • Implementa la lógica de bloqueo de archivos fuera de la llamada de compilación ordinaria para evitar que se produzca una escritura al mismo tiempo que una lectura u otra escritura.

Implementación de un motor de almacenamiento en caché

Además de la interfaz de almacenamiento en caché de compilación 1,2 NN HAL, también se puede encontrar una biblioteca de utilidad de caché en el frameworks/ml/nn/driver/cache directorio. El nnCache subdirectorio contiene código de almacenamiento persistente para el conductor para implementar la caché de compilación sin necesidad de utilizar las características de almacenamiento en caché NNAPI. Esta forma de almacenamiento en caché de compilación se puede implementar con cualquier versión de NN HAL. Si el controlador elige implementar el almacenamiento en caché desconectado de la interfaz HAL, el controlador es responsable de liberar los artefactos almacenados en caché cuando ya no sean necesarios.