美文网首页
MNN 构建后端

MNN 构建后端

作者: i_1312 | 来源:发表于2022-12-19 21:46 被阅读0次

添加一个后端需要去继承Backend抽象类,并实现所有纯虚函数。

//类函数执行流程:构造 -> onCreate -> onResizeBegin -> onResizeEnd -> onAcquire -> onCopyBuffer -> onExecuteBegin -> onExecuteEnd -> onCopyBuffer -> onClearBuffer
class NPUBackend : public Backend {
    public:
        //NPUBackend构造函数,NPURuntime用于存放cache参赛以及附加编译选项
        NPUBackend(const NPURuntime* runtime);
        virtual ~NPUBackend();
        //onCreate : 根据传入op的类型,创建对应npu算子
        virtual Execution* onCreate(const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs, const MNN::Op* op) override;
        //推理前准备函数
        virtual void onExecuteBegin() const override;
        //推理后处理函数
        virtual void onExecuteEnd() const override;
        //内存申请统一函数
        virtual Backend::MemObj* onAcquire(const Tensor* tensor, StorageType storageType) override;
        //清除缓存
        virtual bool onClearBuffer() override;
        //内存拷贝函数(通常用于数据格式转换)
        virtual void onCopyBuffer(const Tensor* srcTensor, const Tensor* dstTensor) const override;
        //维度计算/内存申请前准备
        virtual void onResizeBegin() override;
        //计算维度/内存申请后处理
        virtual void onResizeEnd() override;
}

onCreate函数
Backend需要通过onCreate为为每一个op创建出exection,一个exection通常代表一个算子实例:
它会去后端算子实现中查找算子,如果没有找到对应的算子,则使用CPU的后端

Execution* NPUBackend::onCreate(const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs, const MNN::Op* op) {
//获取已注册的npu算子map
    auto map = getCreatorMap();
    auto iter = map->find(op->type());
        
    if (iter == map->end()) {
        MNN_ERROR("map not find !!! \n");
        if(op != nullptr){
            if(op->name() != nullptr){
                MNN_PRINT("[NPU] Don't support type %d, %s\n", op->type(), op->name()->c_str());
            }
        }
        return nullptr;
    }
    //当查找到npu支持该算子,即创建exection
    auto exe = iter->second->onCreate(inputs, outputs, op, this);

    if (nullptr == exe) {
        MNN_ERROR("nullptr == exe !!! \n");
        if(op != nullptr){
            if(op->name() != nullptr){
                MNN_PRINT("[NPU] The Creator Don't support type %d, %s\n", op->type(), op->name()->c_str());
            }
        }
        return nullptr;
    }

    return exe;
}

onCopyBuffer

拷贝可能在backend内部,也可能在npu backend与CPU backend之间。拷贝需要处理Tensor间的布局转换,相同布局时,可以直接拷贝数据;不同布局,如NHWCNC4HW4,则一般需要做特殊转换。该部分工作需要在onCopyBuffer函数中实现。具体参考:https://github.com/alibaba/MNN/blob/master/source/backend/hiai/backend/NPUBackend.cpp

2.4 onResizeEnd
用于对构图后的模型,进行编译,生产npu可执行模型文件

void NPUBackend::onResizeEnd() {
    bulidIRModelAndLoad();
}

2.5 onExecuteEnd
模型推理代码,即npu sdk提供的推理api在此添加

  1. 新增算子
    3.1 实现
    每个新增算子都要继承Execution,并重写两个函数onResize,onExecute。onExecute用于mnn到npu参数转换,并重新构图。onExecute在npu没用到,只需返回NO_ERROR;
class NPUCommonExecution : public Execution {
    public:
    NPUCommonExecution(Backend *backend, const Op *op);
    virtual ~NPUCommonExecution() = default;
    virtual ErrorCode onResize(const std::vector<Tensor *> &inputs, const std::vector<Tensor *> &outputs) override;
    virtual ErrorCode onExecute(const std::vector<Tensor *> &inputs, const std::vector<Tensor *> &outputs) override;
};

ErrorCode NPUActivation::onResize(const std::vector<Tensor *> &inputs, const std::vector<Tensor *> &outputs) {
    
    auto opName = mOp->name()->str();
    
    auto xOp = mNpuBackend->getInputOps(mOp);

    shared_ptr<ge::op::Activation> relu(new ge::op::Activation(opName + "_relu"));
    (*relu)
        .set_input_x(*xOp.get())
        .set_attr_coef(.000000) 
        .set_attr_mode(mType);
    mNpuBackend->setOutputOps(mOp, {relu}, outputs);

    return NO_ERROR;
}

相关文章

网友评论

      本文标题:MNN 构建后端

      本文链接:https://www.haomeiwen.com/subject/sroeqdtx.html