• OpenHarmony AI 业务子系统


    整体代码信息

    • Version : code-v3.1-Beta
    目录名描述
    applications应用程序样例,包括camera等
    base基础软件服务子系统集&硬件服务子系统集
    build组件化编译、构建和配置脚本
    docs说明文档
    domains增强软件服务子系统集
    drivers驱动子系统
    foundation系统基础能力子系统集
    kernel内核子系统
    prebuilts编译器及工具链子系统
    test测试子系统
    third_party开源第三方组件
    utils常用的工具集
    vendor厂商提供的软件
    build.py编译脚本文件

    在这里插入图片描述

    关于目前的状态

    实际模型推理的逻辑,以及支持新的推理引擎

    • 例子中使用的模型: wk
      在这里插入图片描述

      • 如何将训练得到的模型,导出成该格式?–> 该格式是华为硬件平台对应的模型格式。将模型转为NNIE框架支持的wk模型——以tensorflow框架为例 - 知乎 (zhihu.com)
      • 每个算法sdk 封装成一个插件 plugin 的方式,实现i_plugin.h 的接口
        在这里插入图片描述
    • Plugin 需要实现的接口
    
    class IPlugin {
    public:
        virtual ~IPlugin() = default;
    
        virtual const long long GetVersion() const = 0;
    
        virtual const char *GetName() const = 0;
    
        /**
         * Get plugin inference mode.
         *
         * @return Inference mode, synchronous or asynchronous.
         */
        virtual const char *GetInferMode() const = 0;
    
        /**
         * Algorithmic inference interface for synchronous tasks.
         *
         * @param [in] request Request task which contains the specific information of the task.
         * @param [out] response Results of encapsulated algorithmic inference.
         * @return Returns 0 if the operation is successful, returns a non-zero value otherwise.
         */
        virtual int SyncProcess(IRequest *request, IResponse *&response) = 0;
    
        /**
         * Algorithmic inference interface for asynchronous tasks.
         *
         * @param [in] request Request task which contains the specific information of the task.
         * @param [in] callback Callback which is used to return the result of asynchronous inference.
         * @return Returns 0 if the operation is successful, returns a non-zero value otherwise.
         */
        virtual int AsyncProcess(IRequest *request, IPluginCallback *callback) = 0;
    
        /**
         * Initialize plugin.
         *
         * @param [in] transactionId Transaction ID.
         * @param [in] inputInfo Data information needed to initialize plugin.
         * @param [out] outputInfo The returned data information of initializing plugin.
         * @return Returns 0 if the operation is successful, returns a non-zero value otherwise.
         */
        virtual int Prepare(long long transactionId, const DataInfo &inputInfo, DataInfo &outputInfo) = 0;
    
        /**
         * Unload model and plugin.
         *
         * @param [in] isFullUnload Whether to unload completely.
         * @param [in] transactionId Transaction ID.
         * @param [in] inputInfo Data information needed to unload model and plugin.
         * @return Returns 0 if the operation is successful, returns a non-zero value otherwise.
         */
        virtual int Release(bool isFullUnload, long long transactionId, const DataInfo &inputInfo) = 0;
    
        /**
         * Set the configuration parameters of the plugin.
         *
         * @param [in] optionType The type of setting option.
         * @param [in] inputInfo Configuration parameter needed to set up the plugin.
         * @return Returns 0 if the operation is successful, returns a non-zero value otherwise.
         */
        virtual int SetOption(int optionType, const DataInfo &inputInfo) = 0;
    
        /**
         * Get the configuration parameters of plugin.
         *
         * @param [in] optionType The type of getting option.
         * @param [in] inputInfo Parameter information for getting options.
         * @param [out] outputInfo The configuration information of plugin.
         * @return Returns 0 if the operation is successful, returns a non-zero value otherwise.
         */
        virtual int GetOption(int optionType, const DataInfo &inputInfo, DataInfo &outputInfo) = 0;
    };
    
    typedef IPlugin *(*IPLUGIN_INTERFACE)();
    } // namespace AI
    } // namespace OHOS
    
    #endif // I_PLUGIN_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 关于支持新的推理引擎(无论是硬件厂商的还是第三方软件厂商),可通过继承EngineAdapter该类,实现对应的接口,可以参考华为提供的NNIE的方式
      • EngineAdapter
    foundation/ai/engine/services/common/platform/os_wrapper/engine_hal/interfaces/engine_adapter.h
    
    • 1
    class EngineAdapter {
    public:
        virtual ~EngineAdapter() = default;
    
        /* Initializes the algorithm and get the algorithm execution handle */
        virtual int32_t Init(const char *modelPath, intptr_t &handle) = 0;
    
        /* De-Initializes all the algorithms.  */
        virtual int32_t Deinit() = 0;
    
        /* Makes the model based on the given handle Inference once. */
        virtual int32_t Invoke(intptr_t handle) = 0;
    
        /* Gets the inputBuffer and inputSize after the handle related model is initialized. */
        virtual int32_t GetInputAddr(intptr_t handle, uint16_t nodeId,
                                     uintptr_t &inputBuffer, size_t &inputSize) = 0;
    
        /* Gets the outputBuffer and outputSize after the handle related model is initialized. */
        virtual int32_t GetOutputAddr(intptr_t handle, uint16_t nodeId,
                                      uintptr_t &outputBuffer, size_t &outputSize) = 0;
    
        /* Release the algorithm based on the given handle. */
        virtual int32_t ReleaseHandle(intptr_t handle) = 0;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • NNIE实现的方式
    device/hisilicon/hardware/ai/hal/hispark_taurus/include/nnie_adapter.h
    
    • 1
    - 存档的头文件,以及对应的静态库和动态库
    
    • 1

    在这里插入图片描述

    • 通过宏来选择不同的引擎
      在这里插入图片描述

    现状

    • 基于目前的OpenHarmony 版本来看,虽然在子系统的配置文件中,存在定义,但是并没有定义相关的ohos.build文件。
      在这里插入图片描述
      在这里插入图片描述
    • 关于使用./build.sh脚本编译时,编译rk3568没有涉及到这部分功能。

    Exameple

    将AI engine 注册成为一个server

    • 首先定义一个新的服务,并实现服务的生命周期函数
      在这里插入图片描述
      • 宏INHERIT_SERVICE 定义一些函数指针
        在这里插入图片描述
      • 宏INHERIT_IUNKNOWNENTRY类似一个模板,定义了一些变量,通过调用INHERIT_IUNKNOWNENTRY(AiInterface),在该结构体中定义一些一些属性。
        在这里插入图片描述
      • 查看AiInterface结构体,可以知道其中定义了如下函数指针,作为一些方法。
        在这里插入图片描述
      • 通过上面几部分,完成了一个struct 或 class的定义。
    • 其次,创建一个服务对象并同时定义默认功能,并向SAMGR注册服务即接口,定义服务的初始化入口。
      在这里插入图片描述
      • 在创建对象的时候,将struct中的函数指针完成赋值。注意其中的使用到的宏SERVER_IPROXY_IMPL_BEGIN和IPROXY_END 是为了对应之前使用到的INHERIT_IUNKNOWNENTRY(AiInterface),可以查看这两个宏的定义。
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
      • 在文件foundation/ai/engine/services/server/communication_adapter/source/sa_server.c中,定了变量g_aiEngine中赋值号右边的函数。此时完成最终的定义。
    • 在client端调用跨进程服务的对外接口,并调用IPC消息接口
      在这里插入图片描述
    • 在server查看Invoke函数的处理IPC消息
      在这里插入图片描述
    More

    该部分内容,更过可以查看foundation/distributedschedule/samgr_lite/README_zh.md文件,该部分是软总线部分的外部接口的介绍。

    通过ID_LOAD_ALGORITHM来看这个调用栈

    • 从一个app端开始,
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    • 最后,APP端调用的接口,开始到AI Engine中的cline
      • 通过不同的FUNID来调用对应的响应函数,以ID_LOAD_ALGORITHM为例来看。大概是三部分,一部分在client端,一部分在通信部分,最后一部分在server端。
        • client
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
        • 中间
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
          • 关于跨client 和server必然存在一个双方共同使用部分,现在使用IPC传递一个IpcIo 一个双向链表, client和server双方对同一个IpcIo进行读写。
            • 首先可以查看IpcIo的定义, 可以通过查看IpcIoInit函数对于其中的各个内容起到的作用有更深刻的理解
              在这里插入图片描述
              在这里插入图片描述
            • 以LoadAlgorithmProxy为例,关于其中的ParcelClientInfo, ParcelAlgorithmInfo 和 ParcelDataInfo 最终操作的均是将数据push到IpcIo中。
              在这里插入图片描述
            • 对比看server端,
              在这里插入图片描述
            • 跳入UnParcelInfo函数,可以发现是对于client中ParcelClientInfo, ParcelAlgorithmInfo 和 ParcelDataInfo 的逆过程,读出client中写入的数据
              在这里插入图片描述
            • 关于在server端结果写入到另外一个IpcIo 对象 reply, 关于该对象的初始化,应该是samgr_lite组件来完成(推理),关于client如何获取这些server写入的数据,查看关于IClientProxy接口的定义,可以发现,最后的参数CallbackBuff是一个用来处理响应数据的回调函数。
              在这里插入图片描述
          • 查看回调函数CallbackBuff定义,可以发现从另外一个IpcIo对象中读取server吸入的数据。这样子,基于已有的samgr_lite接口,完成了clinet和server端数据的通信。
            在这里插入图片描述
        • server端
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
          • 在engine->GetPlugin()调用中获取一个std::shared_ptr, 查看关于class Plugin的定义,可以发现其中包含IPlugin *pluginAlgorithm_, 通过该指针,调用IPlugin相关的方法, 自定义的算法插件,继承IPlugin并完成对应的接口定义。
            在这里插入图片描述
            在这里插入图片描述
            在这里插入图片描述

    同步执行 Sync

    • 我们还是从client 端来看整个调用栈,目的理解对于整个然间作拓展的缘由,进而学习整个软件设计,像整个软件开发生态链,向上挺进。
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    • 其实到现在的话,与之前设置算法ID其实并没有太多的区别。
      在这里插入图片描述
      在这里插入图片描述
    • 查看其中的msgHandler_,类型IHandler, 关于同步运行类型SyncMsgHandler也继承自该类型,同样异步运行类型也继承自该类型AsyncMsgHandler
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    • 查看模板类SimpleEventNotifier, 存在一个关于ISemaphore的智能指针producer_, 并相关的方法AddToBack, 将数据保存到属性T *value_, 并让信号量自增,GetFromFront, 等待一段时间,查看信号量是否成功自减,如果成功,将对应的数据返回。
      在这里插入图片描述
      在这里插入图片描述
    • 关于ISemaphore信号量的定义,可以具体查看下面的内容,若了解,可以直接跳过。 其中关于宏定义的使用还是挺有意思的。
      在这里插入图片描述
    • 接着SyncMsgHandler继续往下走,查看SendRequest方法具体做什么,将request和notifier封装成一个Task,压入queue_,查看Task的定义,发现也没做什么,只是封装下数据,然后关于quque_ 其实是从engine中直接传递下去的。
      在这里插入图片描述
      在这里插入图片描述
      • 关于查看queue_ 数据传递。
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
    • 关于目前为止,只是发现将request及相关的数据压入了queue_,并且在IHandler 与Engine层面使用的是同一分queue_, 那么这个数据到底是怎么处理的?
      • 这个需要回到,算法加载,创建Engine部分,并开始初始化该Engine,在调用Engine的构造函数中,将queue 引用同时传递给EngineWorker,实例化对象, EngineWorker继承于IWorker。
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
      • 查看Engine::Initialize(), 可以发现SyncMsgHandler(*queue_, plugin_->GetPluginAlgorithm()),并在线程中,开始执行thread_->StartThread(&worker_), 之后调用EngineWorker::OneAction() , 先取出一个任务queue_.PopFront(task);,然后处理任务task.handler->Process(task); 实际调用SyncMsgHandler::Process(const Task &task), 开始调用pluginAlgorithm_->SyncProcess(request, response);完成实际处理,(task.notifier)->AddToBack(response); 通知信号量,自增。 最终,完成SyncMsgHandler::ReceiveResponse(int timeOut, SimpleEventNotifier ¬ifier, IResponse *&response)
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述

    异步执行 Async

    • 关于异步执行主要有两部分,即注册回调函数,以及完成执行。
    注册回调函数
    • 目前提供的两个demo ,图片分类以及语音识别,均使用的是同步执行,在文件foundation/ai/engine/services/client/client_executor/include/i_aie_client.inl 没有发现 直接的接口,往下一层foundation/ai/engine/services/client/communication_adapter/source/sa_client_proxy.cpp中,可以发现存在预留的接口RegisterCallbackProxy
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    • 先查看注册saAsyncHandler->RegisterAsyncHandler(clientInfo->clientId);中做的工作
      在这里插入图片描述
      在这里插入图片描述
    • 接着查看启动中做的工作saAsyncHandler->StartAsyncProcess(clientInfo->clientId, adapter);
      在这里插入图片描述
      在这里插入图片描述
    • 接着查看下classAsyncProcessWorker 构造函数所作工作。初始化属性工作
      在这里插入图片描述
      在这里插入图片描述
    • 关于线程的启动,跳过,直接查看OneAction工作,关于线程具体如何工作,可以查看前面的同步执行,过程是一样。
      在这里插入图片描述
    • 先跳过如果获取response,查看IpcIoResponse 工作。
      在这里插入图片描述
    • 关于SvcIdentity *svcIdentity = adapter_->GetEngineListener(); 就是将之前保存好的SvcIdentity 取出来。
    • 关于Transact(nullptr, *svcIdentity, ON_ASYNC_PROCESS_CODE, &io, &reply, LITEIPC_FLAG_ONEWAY, nullptr); 工作内容,
      在这里插入图片描述
      在这里插入图片描述
    • 我们接着查看下 如果获取 response IResponse *response = handler_->FetchCallbackRecord(); 首先,前面实例化了一个ClientListenerHandler 对象。查看下构造函数。实例化了一个对象Event。
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    • 那么,接着IResponse *response = handler_->FetchCallbackRecord(); 从类ClientListenerHandler 中属性responses_ 取出 响应, 该属性十一两个list 专门保存 响应, 那么可以知道,异步执行时,必然是将属性保存到responses_, 通过上面的Event 来控制这个写入,读出。
      在这里插入图片描述
    异步执行
    • 通过上面注册回调函数过程,响应必然会保存到ClientListenerHandler 的responses_ 中。
    • 首先,与同步执行相似,先初始化Engine。
      在这里插入图片描述
      在这里插入图片描述
    • 查看下Handler 具体定义
      在这里插入图片描述
    • bool isStarted = thread_->StartThread(&worker_); 开始执行任务队列,现在任务队列还是为空。 等待
    • 直接从foundation/ai/engine/services/client/communication_adapter/source/sa_client_adapter.cpp 开始这个过程,前面的部分,与之前相似。
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    • 查看实例化future 工作内容。并且在Future 中, 包含请求和响应。
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    • 随着Task 压入队列, 那么开始执行工作,也就是开始执行请求。 之前的队列是复制到EngineWorker 中,
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    • 到现在为止,reques 终于执行了
    • 关键是,response 如何返回呢? ? ?
      • 其实是因为算法SDK 中的异步执行没有实现导致,没有联通。
        在这里插入图片描述
        在这里插入图片描述
      • 在异步执行完之后,调用int OnEvent(PluginEvent event, IResponse *response) override;
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
    • 到此,就可以获取到响应了。
    • zh-cn/readme/AI业务子系统.md · OpenHarmony/docs - 码云 - 开源中国 (gitee.com)
    • (18条消息) 跨平台:GN实践详解(ninja, 编译, windows/mac/android实战)_TechGhost的博客-CSDN博客_gn编译
  • 相关阅读:
    学习心得07:C#
    ZEMAX | 在OpticStudio中通过几何光线追迹来模拟杨氏双缝干涉实验
    如何在postman中实现自动化测试?
    MYSQL存储过程注释详解
    Nginx之日志模块解读
    (经典dp) I型 L型 铺盖2*n
    CSS隐藏滚动条
    Revit中土建模块【精准生梁】快速生成
    阿里云安装docker与vulhub靶场
    二叉树习题总结
  • 原文地址:https://blog.csdn.net/weixin_37210821/article/details/127415882