在 AI 推理场景中,性能、延迟和能效是决定用户体验的关键指标。CANN(Compute Architecture for Neural Networks)提供了一套完整的异构计算软件栈,使开发者能在专用加速设备上高效运行深度学习模型。本文将带你从零开始,使用 CANN 的 C++ API 部署一个图像分类推理程序。

1. 环境准备

首先确保已安装 CANN 运行时及开发工具包(通常包含头文件 acl/acl.h 和动态库 libascendcl.so)。可通过以下命令验证:

bash

编辑

ls /usr/local/Ascend/ascend-toolkit/latest/include/acl/

注:路径可能因部署方式不同而变化,但核心头文件为 acl.h

2. 初始化运行时

所有 CANN 应用的第一步是初始化 ACL(Ascend Computing Language)运行时环境:

cpp

编辑

#include "acl/acl.h"

int main() {
    // 初始化 ACL
    aclError ret = aclInit(nullptr);
    if (ret != ACL_SUCCESS) {
        printf("ACL init failed, error code: %d\n", ret);
        return -1;
    }

    // 查询可用设备数量
    uint32_t deviceCount;
    aclrtGetDeviceCount(&deviceCount);
    printf("Found %u devices.\n", deviceCount);

    // 使用设备 0
    aclrtSetDevice(0);

    // 后续推理逻辑...

    // 释放资源
    aclFinalize();
    return 0;
}

3. 加载离线模型(OM 格式)

CANN 推荐使用离线模型(Offline Model, .om),它由原始模型(如 ONNX)经图编译器优化后生成。加载方式如下:

cpp

编辑

aclmdlDesc *modelDesc = nullptr;
aclmdlDataset *inputDataset = nullptr;
aclmdlDataset *outputDataset = nullptr;
uint32_t modelId;

// 从文件加载 OM 模型
const char* modelName = "resnet50.om";
ret = aclmdlLoadFromFile(modelName, &modelId);
if (ret != ACL_SUCCESS) {
    printf("Load model failed.\n");
    return -1;
}

modelDesc = aclmdlCreateDesc();
aclmdlGetDesc(modelDesc, modelId);

4. 准备输入数据

假设输入为 224x224 RGB 图像,需将其转换为 NCHW 格式并拷贝至设备内存:

cpp

编辑

size_t inputSize = 3 * 224 * 224 * sizeof(float);
void *hostInput = malloc(inputSize);
// 填充 hostInput(例如通过 OpenCV 读取图像并归一化)

void *deviceInput = nullptr;
aclrtMalloc(&deviceInput, inputSize, ACL_MEM_MALLOC_NORMAL_ONLY);
aclrtMemcpy(deviceInput, inputSize, hostInput, inputSize, ACL_MEMCPY_HOST_TO_DEVICE);

// 创建输入数据集
inputDataset = aclmdlCreateDataset();
aclDataBuffer* inputBuffer = aclCreateDataBuffer(deviceInput, inputSize);
aclmdlAddDatasetBuffer(inputDataset, inputBuffer);

5. 执行推理与获取结果

cpp

编辑

ret = aclmdlExecute(modelId, inputDataset, outputDataset);
if (ret != ACL_SUCCESS) {
    printf("Model execute failed.\n");
    return -1;
}

// 获取输出指针(假设输出为 1000 维分类 logits)
void* deviceOutput = aclGetDataBufferAddr(aclmdlGetDatasetBuffer(outputDataset, 0));
size_t outputSize = aclGetDataBufferSizeV2(aclmdlGetDatasetBuffer(outputDataset, 0));

float* hostOutput = new float[1000];
aclrtMemcpy(hostOutput, outputSize, deviceOutput, outputSize, ACL_MEMCPY_DEVICE_TO_HOST);

// 打印 top-1 类别
int maxIdx = 0;
for (int i = 1; i < 1000; ++i) {
    if (hostOutput[i] > hostOutput[maxIdx]) maxIdx = i;
}
printf("Predicted class: %d\n", maxIdx);

6. 资源清理

务必释放所有分配的内存和模型:

cpp

编辑

aclrtFree(deviceInput);
aclrtFree(deviceOutput);
aclmdlDestroyDataset(inputDataset);
aclmdlDestroyDataset(outputDataset);
aclmdlUnload(modelId);
delete[] hostOutput;
free(hostInput);

小结

通过以上步骤,我们完成了一个完整的 CANN 推理流程。虽然代码略显底层,但这种控制粒度为高性能部署提供了极大灵活性。在后续文章中,我们将介绍如何利用 Python 接口简化开发,以及如何进行性能调优。

cann组织链接:https://atomgit.com/cann

ops-nn仓库链接:https://atomgit.com/cann/ops-nn

Logo

昇腾计算产业是基于昇腾系列(HUAWEI Ascend)处理器和基础软件构建的全栈 AI计算基础设施、行业应用及服务,https://devpress.csdn.net/organization/setting/general/146749包括昇腾系列处理器、系列硬件、CANN、AI计算框架、应用使能、开发工具链、管理运维工具、行业应用及服务等全产业链

更多推荐