供應商擴展

Android 10 中引入的神經網絡 API (NNAPI) 供應商擴展是供應商定義的操作和數據類型的集合。在運行 NN HAL 1.2 或更高版本的設備上,驅動程序可以通過支持相應的供應商擴展來提供自定義硬件加速操作。供應商擴展不會修改現有操作的行為。

供應商擴展為 OEM 操作和數據類型提供了更結構化的替代方案,這些在 Android 10 中已棄用。有關更多信息,請參閱OEM 操作和數據類型

擴展程序使用許可名單

供應商擴展只能由明確指定的 Android 應用程序和/product/vendor/odm/data分區上的本機二進製文件使用。位於/system分區的應用程序和本機二進製文件不能使用供應商擴展。

允許使用 NNAPI 供應商擴展的 Android 應用程序和二進製文件列表存儲在/vendor/etc/nnapi_extensions_app_allowlist中。文件的每一行都包含一個新條目。條目可以是帶有斜杠 (/) 前綴的本機二進制路徑,例如/data/foo ,也可以是 Android 應用程序包的名稱,例如com.foo.bar

允許列表是從 NNAPI 運行時共享庫強制執行的。此庫可防止意外使用,但不能防止應用程序直接使用 NNAPI 驅動程序 HAL 接口故意規避。

供應商擴展定義

供應商創建並維護一個帶有擴展定義的頭文件。擴展定義的完整示例可以在example/fibonacci/FibonacciExtension.h中找到。

每個擴展都必須有一個唯一的名稱,該名稱以供應商的反向域名開頭。

const char EXAMPLE_EXTENSION_NAME[] = "com.example.my_extension";

該名稱充當操作和數據類型的命名空間。 NNAPI 使用此名稱來區分供應商擴展。

操作和數據類型的聲明方式與runtime/include/NeuralNetworks.h中的類似。

enum {
    /**
     * A custom scalar type.
     */
    EXAMPLE_SCALAR = 0,

    /**
     * A custom tensor type.
     *
     * Attached to this tensor is {@link ExampleTensorParams}.
     */
    EXAMPLE_TENSOR = 1,
};

enum {
    /**
     * Computes example function.
     *
     * Inputs:
     * * 0: A scalar of {@link EXAMPLE_SCALAR}.
     *
     * Outputs:
     * * 0: A tensor of {@link EXAMPLE_TENSOR}.
     */
    EXAMPLE_FUNCTION = 0,
};

擴展操作可以使用任何操作數類型,包括非擴展操作數類型和來自其他擴展的操作數類型。當使用來自另一個擴展的操作數類型時,驅動程序必須支持另一個擴展。

擴展還可以聲明自定義結構以伴隨擴展操作數。

/**
 * Quantization parameters for {@link EXAMPLE_TENSOR}.
 */
typedef struct ExampleTensorParams {
    double scale;
    int64_t zeroPoint;
} ExampleTensorParams;

在 NNAPI 客戶端中使用擴展

runtime/include/NeuralNetworksExtensions.h (C API) 文件提供運行時擴展支持。本節概述了 C API。

要檢查設備是否支持擴展,請使用ANeuralNetworksDevice_getExtensionSupport

bool isExtensionSupported;
CHECK_EQ(ANeuralNetworksDevice_getExtensionSupport(device, EXAMPLE_EXTENSION_NAME,
                                                   &isExtensionSupported),
         ANEURALNETWORKS_NO_ERROR);
if (isExtensionSupported) {
    // The device supports the extension.
    ...
}

要使用擴展操作數構建模型,請使用ANeuralNetworksModel_getExtensionOperandType獲取操作數類型並調用ANeuralNetworksModel_addOperand

int32_t type;
CHECK_EQ(ANeuralNetworksModel_getExtensionOperandType(model, EXAMPLE_EXTENSION_NAME, EXAMPLE_TENSOR, &type),
         ANEURALNETWORKS_NO_ERROR);
ANeuralNetworksOperandType operandType{
        .type = type,
        .dimensionCount = dimensionCount,
        .dimensions = dimensions,
};
CHECK_EQ(ANeuralNetworksModel_addOperand(model, &operandType), ANEURALNETWORKS_NO_ERROR);

或者,使用ANeuralNetworksModel_setOperandExtensionData將附加數據與擴展操作數相關聯。

ExampleTensorParams params{
        .scale = 0.5,
        .zeroPoint = 128,
};
CHECK_EQ(ANeuralNetworksModel_setOperandExtensionData(model, operandIndex, &params, sizeof(params)),
         ANEURALNETWORKS_NO_ERROR);

要使用擴展操作構建模型,請使用ANeuralNetworksModel_getExtensionOperationType獲取操作類型並調用ANeuralNetworksModel_addOperation

ANeuralNetworksOperationType type;
CHECK_EQ(ANeuralNetworksModel_getExtensionOperationType(model, EXAMPLE_EXTENSION_NAME, EXAMPLE_FUNCTION,
                                                        &type),
         ANEURALNETWORKS_NO_ERROR);
CHECK_EQ(ANeuralNetworksModel_addOperation(model, type, inputCount, inputs, outputCount, outputs),
         ANEURALNETWORKS_NO_ERROR);

向 NNAPI 驅動程序添加擴展支持

驅動程序通過IDevice::getSupportedExtensions方法報告支持的擴展。返回的列表必須包含一個描述每個支持的擴展的條目。

Extension {
    .name = EXAMPLE_EXTENSION_NAME,
    .operandTypes = {
        {
            .type = EXAMPLE_SCALAR,
            .isTensor = false,
            .byteSize = 8,
        },
        {
            .type = EXAMPLE_TENSOR,
            .isTensor = true,
            .byteSize = 8,
        },
    },
}

在用於識別類型和操作的 32 位中,高Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX位是擴展前綴,低Model::ExtensionTypeEncoding::LOW_BITS_TYPE位表示擴展的類型或操作。

處理操作或操作數類型時,驅動程序必須檢查擴展前綴。如果擴展前綴具有非零值,則操作或操作數類型是擴展類型。如果值為0 ,則操作或操作數類型不是擴展類型。

要將前綴映射到擴展名,請在model.extensionNameToPrefix中查找。對於給定模型,從前綴到擴展名的映射是一一對應(雙射)。不同的前綴值可能對應於不同模型中的相同擴展名。

驅動程序必須驗證擴展操作和數據類型,因為 NNAPI 運行時無法驗證特定的擴展操作和數據類型。

擴展操作數可以在operand.extraParams.extension中有關聯的數據,運行時將其視為任意大小的原始數據塊。

OEM 操作和數據類型

NNAPI 具有 OEM 操作和 OEM 數據類型,以允許設備製造商提供自定義的、特定於驅動程序的功能。這些操作和數據類型僅供 OEM 應用程序使用。 OEM 操作和數據類型的語義是 OEM 特定的,並且可以隨時更改。 OEM 操作和數據類型使用OperationType::OEM_OPERATIONOperandType::OEMOperandType::TENSOR_OEM_BYTE進行編碼。