# 图编译和图执行

单算子模型执行是指基于图IR执行算子，先编译算子（例如，使用OMG工具将Ascend IR定义的单算子描述文件编译成算子omc模型文件），再调用模型加载推理接口执行单算子网络。下文仅提供单算子模型执行的样例和基础内容讲解，详细内容请参考[端侧部署](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/cannkit-whole-deployment-process)章节。
> 说明
>
> HarmonyOS Next暂不支持图编译与图执行，仅支持通过[AI框架算子适配](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/cannkit-overview-of-ai-framework-operator)方式集成自定义算子，以下步骤仅供参考。

## 环境要求

* 已参考[环境准备](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/cannkit-environment-preparation)，完成DDK驱动和软件的安装，配置DDK软件所需基本环境变量。

  安装DDK软件后，使用DDK运行开发者进行编译、运行时，需要以DDK运行开发者登录环境，执行**source ${install_path}/ddk/tools/tools_ascendc/set_ascendc_env.sh**命令设置环境变量。其中${install_path}为DDK软件的安装目录，例如：/home。
* 已参考完成算子的开发和部署。

## 准备验证代码工程

在执行NPU调试命令时，例如：

```shell
ascendebug kernel --backend npu  --chip-version kirin9020 --repo-type customize --json-file ./temp.json --core-type AiCore
```

命令行参数详见[NPU调测参数](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/cannkit-cli-parameters#npu调测参数)，会在${debug_workspace} /${op_type} /npu/src下生成验证样例工程，代码工程目录结构如下。

```text
src
├── BuildIRGraph.cpp // 生成单算子图源文件
├── BuildIRGraph.h // 单算子图入口：CreateCustomIRGraph
├── build.sh // omc 模型编译脚本
├── CMakeLists.txt
├── custom_graph.omc // 编译后得到的omc模型
└── op_proto.h // 算子编译输出的算子原型定义头文件
```

## 生成单算子离线模型文件

执行验证样例工程下的build.sh。

```shell
./build.sh
```

以上命令执行后，会在当前路径下生成custom_graph.omc的离线模型文件。

## 生成输入数据

在样例工程目录下，执行如下命令：

```shell
python3 scripts/gen_data.py
```

会在input目录下生成两个shape为(8,2048)，数据类型为float16的数据文件input_0.bin与input_1.bin，用于进行AddCustom算子的验证。

代码样例如下。

```python
import numpy as np
a = np.random.randint(100, size=(8, 2048,)).astype(np.float16)
b = np.random.randint(100, size=(8, 2048,)).astype(np.float16)
a.tofile('input_0.bin')
b.tofile('input_1.bin')
```

## APP集成代码

开发者可以参考如下样例编写模型加载、执行的代码逻辑。完整集成流程请参考[应用开发](https://developer.huawei.com/consumer/cn/doc/hiai-Guides/app-dev-0000001052605540)。

以下是关键步骤的代码示例，不可以直接拷贝编译运行，仅供参考，调用接口后，需增加异常处理的分支，并记录报错日志、提示日志，此处不一一列举。

```cpp
int RunModel(void* modelData, size_t modelSize)
{
    // 1.加载omc模型
    auto modelDescV1 = std::make_shared<hiai::AiModelDescription>("model", 3, 0, 0, 0);
    modelDescV1->SetModelBuffer(modelData, modelSize);

    std::vector<std::shared_ptr<hiai::AiModelDescription>> modelDescVec;
    modelDescVec.push_back(modelDescV1);
    auto clientV1 = std::make_shared<hiai::AiModelMngerClient>();
    if (clientV1->Init(nullptr) != hiai::SUCCESS) {
        LOGE("client Init() failed.");
        return FAILURE;
    }

    if (clientV1->Load(modelDescVec) != hiai::SUCCESS) {
        LOGE("client Load() failed.");
        return FAILURE;
    }
    LOGI("load model success");
    std::vector<hiai::TensorDimension> inDimVecV1;
    std::vector<hiai::TensorDimension> outDimVecV1;
    std::vector<std::shared_ptr<hiai::AiTensor>> inTensorVecV1;
    std::vector<std::shared_ptr<hiai::AiTensor>> outTensorVecV1;

    if (clientV1->GetModelIOTensorDim(modelDescV1->GetName(), inDimVecV1, outDimVecV1) != hiai::SUCCESS) {
        LOGE("client GetModelIOTensorDim() failed.");
        clientV1->UnLoadModel();
        return FAILURE;
    }
    // 2、设置模型输入、输出
    for (size_t i = 0; i < INPUT_NUM &&  i < inDimVecV1.size(); ++i) {
        void *data = nullptr;
        size_t size = 0;
        LOGI("open input file  inputFile: %s ", INPUT_LIST[i].c_str());
        if (!ReadData(INPUT_LIST[i], &data, size)) {
            LOGE("open input file failed! inputFile: %s ", INPUT_LIST[i].c_str());
            clientV1->UnLoadModel();
            return FAILURE;
        }

        auto inputV1 = std::make_shared<hiai::AiTensor>();

        inputV1->Init(&inDimVecV1[i], INPUT_TYPE);
        if (inputV1->GetSize() != size) {
            LOGE("inputSize: %d != fileSize: %zu ", inputV1->GetSize(), size);
            clientV1->UnLoadModel();
            free(data);
            data = nullptr;
            return FAILURE;
        }
        memcpy(inputV1->GetBuffer(), data, size);
        inTensorVecV1.push_back(inputV1);
        free(data);
        data = nullptr;
    }
    LOGI("load input success");

    for (size_t i = 0; i < outDimVecV1.size(); i++) {
        auto outputV1 = std::make_shared<hiai::AiTensor>();
        outputV1->Init(&outDimVecV1[i], OUTPUT_TYPE);
        outTensorVecV1.push_back(outputV1);
    }
    LOGI("init output success");

    // 3、进行模型推理
    hiai::AiContext context;
    context.AddPara("model_name", "model");
    int32_t istamp = 0;

    auto retCode = clientV1->Process(context, inTensorVecV1, outTensorVecV1, 1000, istamp);
    if (retCode != hiai::SUCCESS) {
        LOGE("process failed.");
        clientV1->UnLoadModel();
        return retCode;
    }
    LOGI("process success");

    for (size_t i = 0; i < outTensorVecV1.size(); i++) {
        char* data = reinterpret_cast<char*>(outTensorVecV1[i]->GetBuffer());
        DumpBufferToFile(data, outTensorVecV1[i]->GetSize(), OUTPUT_PATH);
    }
    LOGI("dump output success");

    // 4、模型卸载
    clientV1->UnLoadModel();
    return retCode;
}
```

