-
问题描述:某局点在测试客户侧连接上在线坐席,坐席侧发送消息后(经确认都发送成功),客户侧通过poll轮询接口只能获取到部分消息内容。 1.客户侧与坐席侧通过connect接口成功连接,客户开启poll接口轮询。频率为每次请求成功后间隔1秒再次调用。 2.坐席侧连续发送多条消息(测试时以文本消息为主),客户侧多次出现丢消息的情况。(坐席侧发送六七条文本消息,客户侧只能接收到两三条) 3.成功接入之后的几秒内发送的第一条消息丢消息概率较高;连续发送消息中间的消息丢失概率较高。 4.检查ccucs日志时发现轮询接口经常出现org.springframework.web.context.request.async.AsyncRequestTimeoutException: null这类异常报错 5.尝试调整调用接口的超时时长,原设定1000ms,曾调整至3000ms,效果不太明显。且对客户侧界面端会有卡顿影响,最终调回。 6.poll调用返回时也常出现短时间内返回同一个结果串(场景:坐席发送一条消息后,紧接着又发送一条,poll接口一直返回第一条消息的内容。坐席测发送第三条消息时,poll接口直接返回第三天条消息的内容,偶发出现第三条也不推送。直接返回空) 问题分析:1.坐席侧发送的消息可以看一下icd库中的tSocialMessage表,发了几条消息就应该有几条记录,确认已经发送了消息 2.确认poll轮询是使用单线程在跑,使用多线程去轮询获取事件会存在事件被其他线程获取走了的情况,导致消息接收不完全。
-
【问题现象】 Dms agent进程在长时间运行(4-5个月后)占用内存过多,导致内核无法分配内存,进程异常。【问题版本】 所有使用DMS,内核版本为810 、811的版本都涉及。 包含:HCS 8.0.2版本,HCS 8.0.3版本,HCSO 0130,0430,0730。【问题根因】 进程缓存信息占用【恢复方案】Kill agent_service.py对应的的进程。
-
1. 找到系统当前所有的端口使用 netstat 命令查找本机各端口的网络连接情况~]netstat -nulpt #结果如下Active Internet connections (only servers)Proto Recv-Q Send-Q Local Address Foreign Address Statetcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTENtcp 0 0 0.0.0.0:1997 0.0.0.0:* LISTENtcp 0 0 0.0.0.0:80 0.0.0.0:* LISTENtcp 0 0 0.0.0.0:22 0.0.0.0:* LISTENtcp 0 0 127.0.0.1:25 0.0.0.0:* LISTENtcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTENtcp6 0 0 :::80 :::* LISTENtcp6 0 0 :::22 :::* LISTENtcp6 0 0 ::1:25 :::* LISTENtcp6 0 0 :::3306 :::* LISTEN这里我们要找的是 80与443 端口2. 找到对应端口在系统中的进程 ID(PID)依据查找到的 1997 端口找到对应进程, lsof -i :1997, 注意 : 冒号不要漏掉了lsof -i:80 #结果如下COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEtke-gatew 6360 root 10u IPv6 6410452 0t0 TCP *:http (LISTEN)[root@VM-0-114-centos ~]# lsof -i:443COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEtke-gatew 7627 root 9u IPv6 7717644 0t0 TCP *:https (LISTEN).........->172.80.31.2:https (ESTABLISHED)galaxy 25638 root 8u IPv4 6276795 0t0 TCP <机密省略>:57802-..........3. 使用 kill -9 [PID] 命令结束进程通过 lsof 命令我们找到了进程的 PID: 29416,接下来就是使用 kill -9 [PID] 把进程结束就好了kill -9 6360kill -9 25638kill -9 7627到这里就 OK 了,不过为了保险起见,再次执行 netstat -tln 确认是否结束了端口占用 前情提要在布置k8s的时候发现容器报错如~]kubectl get pods -A -o wide.....pot nginx-ingress-nginx-ingress-85d747cfb4-9hk72 0/1 CrashLoopBackOff.....然后想describe查看了一下报错如下~]kubectl describe pod -n pot nginx-ingress-nginx-ingress-85d747cfb4-9hk72......... Warning BackOff 3m14s (x613 over 137m) kubelet Back-off restarting failed container最后logs查看发现端口被占用~]# kubectl logs pods -n pot nginx-ingress-nginx-ingress-85d747cfb4-9hk72Error from server (NotFound): pods "pods" not found[root@VM-0-114-centos ~]# kubectl logs -n pot nginx-ingress-nginx-ingress-85d747cfb4-9hk72I1104 10:51:46.738108 1 main.go:169] Starting NGINX Ingress controller Version=1.6.3 GitCommit=b9378d562021/11/04 10:51:46 [emerg] 19#19: bind() to 0.0.0.0:80 failed (98: Address already in use)nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)2021/11/04 10:51:46 [emerg] 19#19: bind() to 0.0.0.0:443 failed (98: Address already in use).......2021/11/04 10:51:46 [notice] 19#19: try again to bind() after 500ms2021/11/04 10:51:46 [emerg] 19#19: still could not bind()nginx: [emerg] still could not bind()运用了以上步骤之后回复正常pot nginx-ingress-nginx-ingress-85d747cfb4-9hk72 1/1 Running
-
【功能模块】如题,我现在在使用Python API进行Atlas 500的开发,但是离线推理速度比较慢,而且NPU利用率只有30%左右,请问各路大神Atlas 500可以使用多线程进行数据预处理和离线推理吗?如果可以的话多线程具体的处理步骤是怎样的,有相应的文档么?
-
1.3.1 缓存处理和内核vs用户空间缓冲与缓冲的处理方式,是所有I/O操作的基础。术语输入、输出只对数据移入和移出缓存有意义。任何时候都要把它记在心中。通常,进程执行操作系统的I/O请求包括数据从缓冲区排出(写操作)和数据填充缓冲区(读操作)。这就是I/O的整体概念。在操作系统内部执行这些传输操作的机制可以非常复杂,但从概念上讲非常简单。上图显示了一个简化的逻辑图,它表示块数据如何从外部源,例如一个磁盘,移动到进程的存储区域(例如RAM)中。首先,进程要求其缓冲通过read()系统调用填满。这个系统调用导致内核向磁盘控制硬件发出一条命令要从磁盘获取数据。磁盘控制器通过DMA直接将数据写入内核的内存缓冲区,不需要主CPU进一步帮助。当请求read()操作时,一旦磁盘控制器完成了缓存的填写,内核从内核空间的临时缓存拷贝数据到进程指定的缓存中。有一点需要注意,在内核试图缓存及预取数据时,内核空间中进程请求的数据可能已经就绪了。如果这样,进程请求的数据会被拷贝出来。如果数据不可用,则进程被挂起。内核将把数据读入内存。1.3.2 虚拟内存所有现代操作系统都使用虚拟内存。虚拟内存意味着人工或者虚拟地址代替物理(硬件RAM)内存地址。虚拟地址有两个重要优势:多个虚拟地址可以映射到相同的物理地址一个虚拟地址空间可以大于实际可用硬件内存在上面介绍中,从内核空间拷贝到最终用户缓存看起来增加了额外的工作。为什么不告诉磁盘控制器直接发送数据到用户空间的缓存呢?好吧,这是由虚拟内存实现的。用到了上面的优势1。通过将内核空间地址映射到相同的物理地址作为一个用户空间的虚拟地址,DMA硬件(只能访问物理内存地址)可以填充缓存。这个缓存同时对内核和用户空间进程可见。这就消除了内核和用户空间之间的拷贝,但是需要内核和用户缓冲区使用相同的页面对齐方式。缓冲区必须使用的块大小的倍数磁盘控制器(通常是512字节的磁盘扇区)。操作系统将其内存地址空间划分为页面,这是固定大小的字节组。这些内存页总是磁盘块大小的倍数和通常为2倍(简化寻址)。典型的内存页面大小是1024、2048和4096字节。虚拟和物理内存页面大小总是相同的。1.3.3 内存分页为了支持虚拟内存的第2个优势(拥有大于物理内存的可寻址空间)需要进行虚拟内存分页(通常称为页交换)。这种机制凭借虚拟内存空间的页可以持久保存在外部磁盘存储,从而为其他虚拟页放入物理内存提供了空间。本质上讲,物理内存担当了分页区域的缓存。分页区是磁盘上的空间,内存页的内容被强迫交换出物理内存时会保存到这里。调整内存页面大小为磁盘块大小的倍数,让内核可以直接发送指令到磁盘控制器硬件,将内存页写到磁盘或者在需要时重新加载。事实证明,所有的磁盘I/O操作都是在页面级别上完成的。这是数据在现代分页操作系统上在磁盘与物理内存之间移动的唯一方式。现代CPU包含一个名为内存管理单元(MMU)的子系统。这个设备逻辑上位于CPU与物理内存之间。它包含从虚拟地址向物理内存地址转化的映射信息。当CPU引用一个内存位置时,MMU决定哪些页需要驻留(通常通过移位或屏蔽地址的某些位)以及转化虚拟页号到物理页号(由硬件实现,速度奇快)。1.3.4 面向文件、块I/O文件I/O总是发生在文件系统的上下文切换中。文件系统跟磁盘是完全不同的事物。磁盘按段存储数据,每段512字节 。 它是硬件设备,对保存的文件语义一无所知。它们只是提供了一定数量的可以保存数据的插槽。从这方面来说,一个磁盘的段与内存分页类似。它们都有统一的大小并且是个可寻址的大数组另一方面,文件系统是更高层抽象。文件系统是 安排和翻译保存磁盘(或其它可随机访问,面向块的设备)数据的一种特殊方法。你写的代码几乎总是与文件系统交互,而不与磁盘直接交互。文件系统定义了文件名、路径、文件、文件属性等抽象。一个文件系统(在硬盘中)组织了一系列均匀大小的数据块。有些块保存元信息,如空闲块的映射、目录、索引等。其它块包含实际的文件数据。单个文件的元信息描述哪些块包含文件数据、数据结束位置、最后更新时间等。当用户进程发送请求来读取文件数据时,文件系统实现准确定位数据在磁盘上的位置。然后采取行动将这些磁盘扇区放入内存中。文件系统也有页的概念,它的大小可能与一个基本内存页面大小相同或者是它的倍数。典型的文件系统页面大小范围从2048到8192字节,并且总是一个基本内存页面大小的倍数。分页文件系统执行I/O可以归结为以下逻辑步骤:确定请求跨越了哪些文件系统分页(磁盘段的集合)。磁盘上的文件内容及元数据可能分布在多个文件系统页面上,这些页面可能是不连续的。分配足够多的内核空间内存页面来保存相同的文件系统页面。建立这些内存分页与磁盘上文件系统分页的映射。对每一个内存分页产生分页错误。虚拟内存系统陷入分页错误并且调度pagins(页面调入),通过从磁盘读取内容来验证这些页面。一旦pageins完成,文件系统分解原始数据来提取请求的文件内容或属性信息。需要注意的是,这个文件系统数据将像其它内存页一样被缓存起来。在随后的I/O请求中,一些数据或所有文件数据仍然保存在物理内存中,可以直接重用不需要从磁盘重读。1.3.5 文件锁定文件加锁是一种机制,一个进程可以阻止其它进程访问一个文件或限制其它进程访问该文件。虽然名为文件锁定,意味着锁定整个文件(经常做的)。锁定通常可以在一个更细粒度的水平。随着粒度下降到字节级,文件的区域通常会被锁定。锁与特定文件相关联,起始于文件的指定字节位置并运行到指定的字节范围。这一点很重要,因为它允许多个进程协作访问文件的特定区域而不妨碍别的进程在文件其它位置操作。文件锁有两种形式:共享和独占多个共享锁可以同时在相同的文件区域有效。独占锁要求没有其它锁对请求的区域有效。1.3.6 流I/O并非所有的I/O是面向块的。还有流I/O,它是管道的原型,必须顺序访问I/O数据流的字节。常见的数据流有TTY(控制台)设备、打印端口和网络连接。数据流通常但不一定比块设备慢,提供间歇性输入。大多数操作系统允许在非阻塞模式下工作。允许一个进程检查数据流的输入是否可用,不必在不可用时发生阻塞。这种管理允许进程在输入到达时进行处理,在输入流空闲时可以执行其他功能。比非阻塞模式更进一步的是有条件的选择(readiness selection)。它类似于非阻塞模式(并且通常建立在非阻塞模式基础上),但是减轻了操作系统检查流是否就绪准备的负担。操作系统可以被告知观察流集合,并向进程返回哪个流准备好的指令。这种能力允许进程通过利用操作系统返回的准备信息,使用通用代码和单个线程复用多个活动流。这种方式被广泛用于网络服务器,以便处理大量的网络连接。准备选择对于大容量扩展是至关重要的。
-
【功能模块】C# demojava demoC++ demo【操作步骤&问题现象】 环境: win10 winpcap 4.1.3 java1.8 32位 IDEA2020 VS2019 MFC C# demo : 运行显示界面 可以添加设备 双击可以预览 聚焦功能可以使用 其他抓人脸 智能分析,管理 设备 等功能无反应 java demo运行显示界面 可以添加设备 人脸抓拍可以抓,但是间隔比较长,预览无效果空白 布防告警 设备,管理 等功能点了没效果C++ demo 添加依赖后 运行报错“ClientDemo.exe”(Win32): 已加载“C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.9625_none_508ef7e4bcbbe589\msvcr90.dll”。“ClientDemo.exe”(Win32): 已加载“C:\Windows\SysWOW64\wldp.dll”。0x771F63D4 (ntdll.dll)处(位于 ClientDemo.exe 中)引发的异常: 0xC0000139: Entry Point Not Found。线程 0x3fbc 已退出,返回值为 -1073741511 (0xc0000139)。线程 0x3110 已退出,返回值为 -1073741511 (0xc0000139)。线程 0x15b4 已退出,返回值为 -1073741511 (0xc0000139)。程序“[1220] ClientDemo.exe”已退出,返回值为 -1073741511 (0xc0000139) 'Entry Point Not Found'。 弹出错误框: 无法定位程序输入点IVS_PU_GetStreamSmoothPara 于动态链接库 clientDemo.exe上替换过多个版本头文件,库文件均不生效三种demo都存在一些问题,请问java版本如何配置才能运行所有功能? C++版本如何编译通过,并运行所有功能?
-
** mindspore\lite\src\lite_session.cc"注释5** ======================================= ```python std::vector LiteSession::GetInputs() const { return this->input_vec_; }//创建一个获取输入值的容器 //运行graph函数 int LiteSession::RunGraph(const KernelCallBack &before, const KernelCallBack &after) { bool expected = false;//初始化期望值 if (!is_running_.compare_exchange_strong(expected, true)) {//不支持多线程 MS_LOG(ERROR) "Not support multi-threading"; return RET_ERROR; } STATUS ret;//初始化ret状态 MS_ASSERT(this->context_);//检测context的状态 if (before == nullptr && after == nullptr) {//判断是否为首项或者尾项 ret = executor_->Run(this->inputs_, this->outputs_, this->kernels_, this->context_->allocator.get());//运行 } else { ret = executor_->Run(this->inputs_, this->outputs_, this->kernels_, this->context_->allocator.get(), before, after);//运行函数 } if (ret != RET_OK) {//判断运行状态,是否运行成功 MS_LOG(ERROR) "RunGraph failed : " ret; } is_running_.store(false);//运行结束 return ret; } //初始化函数 int LiteSession::Init(const Context *context) { bool expected = false; if (!is_running_.compare_exchange_strong(expected, true)) {//判断是否为多线程运行 MS_LOG(ERROR) "Not support multi-threading"; return RET_ERROR; } #if SUPPORT_NPU//当支持NPU时 npu_manager_ = new (std::nothrow) NPUManager();//创建一个新的NPUManager对象 if (npu_manager_ == nullptr) {//判断是否创建成功 MS_LOG(ERROR) "New npu_manager_ failed"; is_running_.store(false); return RET_ERROR; } npu_pass_manager_ = new (std::nothrow) NPUPassManager(); if (npu_pass_manager_ == nullptr) { MS_LOG(ERROR) "New npu_pass_manager_ failed"; is_running_.store(false);//停止运行 return RET_ERROR; } #endif if (context == nullptr) {//检测输入的context MS_LOG(ERROR) "context is nullptr";//输出错误 is_running_.store(false);//停止运行 return RET_NULL_PTR; } #if SUPPORT_NPU this->context_ = new (std::nothrow) InnerContext(context, npu_manager_);//创建新的场景对象 #else this->context_ = new (std::nothrow) InnerContext(context); #endif if (this->context_ == nullptr) {//判断是否创建成功 MS_LOG(ERROR) "New Context failed"; is_running_.store(false); return RET_MEMORY_FAILED; } auto ret = this->context_->Init();//初始化运行场景 if (ret != RET_OK) {//判断是否初始化成功 MS_LOG(ERROR) "Init Context failed"; is_running_.store(false); return ret; } ret = KernelRegistry::GetInstance()->Init();//进行内核创建初始化 if (ret != RET_OK) { MS_LOG(ERROR) "KernelRegistry Init Failed.";//输出初始化错误 is_running_.store(false); return ret; } ret = InitGPURuntime();//初始化GPU运行时间 if (ret != RET_OK) {//判断是否初始化成功 MS_LOG(ERROR) "Init GPU runtime failed."; is_running_.store(false); return ret; } is_running_.store(false);//结束运行 return RET_OK; } void LiteSession::BindThread(bool if_bind) { // 禁止代码 // 在执行程序中绑定线程 return; } LiteSession::~LiteSession() { bool expected = false; if (!is_running_.compare_exchange_strong(expected, true)) {//判断是否为多线程运行 MS_LOG(ERROR) "Not support multi-threading"; return; } for (auto *kernel : kernels_) { delete kernel;//清空内核 } for (auto tensor : tensors_) { MS_ASSERT(tensor != nullptr); // 不拥有数据的 const 张量数据将不会被释放。 // 例如元图中的常量数据,在释放元图时将被释放。 if (tensor->IsConst() && !tensor->own_data()) { tensor->set_data(nullptr);// } delete tensor; } // 输入映射输出映射中的张量在张量列表中被释放 input_map_.clear();//释放所有的张量 output_node_map_.clear(); output_tensor_map_.clear(); input_vec_.clear(); delete this->context_; delete this->executor_; this->executor_ = nullptr; #if SUPPORT_NPU MS_ASSERT(npu_manager_ != nullptr); MS_ASSERT(npu_pass_manager_ != nullptr); npu_pass_manager_->Clear(); delete npu_pass_manager_; npu_manager_->Reset(); delete npu_manager_; #endif #if GPU_OPENCL delete opencl_runtime_wrapper_; #endif delete (model_); is_running_.store(false);//结束运行 } ```
-
# mindspore\mindspore\ccsrc\backend\kernel_compiler\cpu\slice_cpu_kernel.cc代码标注 ```c++ /** * Copyright 2020 Huawei Technologies Co., Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //导入自定义的库 #include "backend/kernel_compiler/cpu/slice_cpu_kernel.h" //导入系统自带的库 #include #include //导入自定义的库 #include "common/thread_pool.h" #include "runtime/device/cpu/cpu_device_address.h" namespace mindspore {//声明一个空间 namespace kernel {//空间嵌套 int NormalizeBeginPos(int begin_pos, int dim_len) { if (begin_pos 0) { int normal_pos = begin_pos + dim_len; return std::max(normal_pos, 0); } return std::min(begin_pos, dim_len - 1); } //初始化内核 void SliceCPUKernel::InitKernel(const CNodePtr &kernel_node) { static const std::unordered_map type_size_map = {{kNumberTypeBool, sizeof(bool)}, {kNumberTypeInt32, sizeof(int)}, {kNumberTypeFloat32, sizeof(float)}, {kNumberTypeFloat64, sizeof(double)}}; auto input_shape = AnfAlgo::GetPrevNodeOutputInferShape(kernel_node, 0);//获取输入形状 if (input_shape.size() > DIMENSION_8D || input_shape.empty()) {//Slice仅支持1D to 8D的参数变量 MS_LOG(EXCEPTION) "Slice only support 1D to 8D input tensor, but got " input_shape.size() "D."; } auto size = AnfAlgo::GetNodeAttr>(kernel_node, SIZE); auto begin = AnfAlgo::GetNodeAttr>(kernel_node, BEGIN); if (begin.size() != input_shape.size() || size.size() != input_shape.size()) { //切片要求起始长度和大小必须等于输入维度 MS_LOG(EXCEPTION) "Slice requires the length of begin and size must be equal to input dimension."; } InitSliceParam(input_shape, begin, size); TypeId dtype = AnfAlgo::GetPrevNodeOutputInferDataType(kernel_node, 0); auto size_pair = type_size_map.find(dtype); if (size_pair == type_size_map.end()) {//"Slice支持bool, int32, float32和float64输入张量,但得到了... MS_LOG(EXCEPTION) "Slice supports bool, int32, float32 and float64 input tensor, but got " TypeIdToType(dtype)->ToString(); } data_size_ = size_pair->second; } //多线程运行 void SliceCPUKernel::ParallelRun(void *input_addr, void *output_addr, int thread_num) { std::vector tasks; int thread_index = 0;//初始化变量 while (thread_index thread_num) {//若线程数小于支持线程数 auto block = [&, thread_index]() { DoSlice(input_addr, output_addr, &slice_param_, thread_index, data_size_); return common::SUCCESS; }; tasks.emplace_back(block); thread_index++; } common::ThreadPool::GetInstance().SyncRun(tasks); } //初始化内核参数 void SliceCPUKernel::InitSliceParam(const std::vector &input_shape, const std::vector &begin, const std::vector &size) { for (size_t i = 0; i DIMENSION_8D; i++) { if (i input_shape.size()) { int dim_len = SizeToInt(input_shape[i]); int begin_pos = LongToInt(begin[i]); int slice_size = LongToInt(size[i]); if (slice_size = 0) {//Slice要求每个维度的切片大小必须大于0 MS_LOG(EXCEPTION) "Slice requires the each dimension slice size must be greater than 0."; } slice_param_.shape_[i] = dim_len; slice_param_.size_[i] = slice_size; slice_param_.begin_[i] = NormalizeBeginPos(begin_pos, dim_len); int end = slice_param_.begin_[i] + slice_param_.size_[i]; slice_param_.end_[i] = std::min(end, dim_len); } else { slice_param_.shape_[i] = 1; slice_param_.begin_[i] = 0; slice_param_.size_[i] = 1; slice_param_.end_[i] = 1; } } slice_param_.param_length_ = DIMENSION_8D; size_t max_thread_num = common::ThreadPool::GetInstance().GetSyncRunThreadNum(); slice_param_.op_parameter_.thread_num_ = std::min(slice_param_.size_[1], SizeToInt(max_thread_num)); } //检查参数 bool SliceCPUKernel::Launch(const std::vector &inputs, const std::vector & /*workspace*/, const std::vector &outputs) { if (outputs[0]->size == 0) { return true; } auto input_addr = inputs[0]->addr;//获取输入地址 auto output_addr = outputs[0]->addr;//h int thread_num = slice_param_.op_parameter_.thread_num_; if (parallel_ && thread_num >= 2) { ParallelRun(input_addr, output_addr, thread_num); } else { DoSliceNoParallel(input_addr, output_addr, &slice_param_, data_size_); } return true; } } // namespace kernel } // namespace mindspore ```
-
## 模型转换器工具类之多进程篇Multi_process 文件路径:mindspore\ccsrc\cxx_api\model\model_converter_utils\multi_process 这次我为大家来解读一下多进程篇Multi_process,其中很多与上一篇的共享内存篇Shared Memory有交互,两部分是相辅相成的关系,读者可参考[模型转换器工具类之共享内存篇Shared Memory](https://forum.trustie.net/forums/5859/detail "模型转换器工具类之共享内存篇Shared Memory") ## **multiprocess** 模块介绍 **multiprocess** 模块是Mindspore**中的****多进程管理模块。** 运行中的程序就是一个进程。**所有的进程都是通过它的父进程来创建的**。因此,运行起来的[python](https://so.csdn.net/so/search?from=pc_blog_highlight&q=python)程序也是一个进程,那么我们也可以在程序中再创建进程。多个进程可以实现并发效果,当我们的程序中存在多个进程的时候,在某些时候,就会让程序的执行速度变快。 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程,因此我们的Mindspore提供了特色化的multiprocess。 **功能:** multiprocess模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似 multiprocess模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。 **注意:**与线程不同的是,进程没有任何共享状态,多个进程的内存空间相互物理隔离, 进程修改的数据,改动仅限于该进程内 ## **Process类创建子进程的两种方式** **multiprocess** 模块提供了 **Process** 类,该类可用来在 **Windows** 平台上创建新进程 使用 **Process** 类创建实例化对象,其本质是调用该类的构造方法创建新进程。 #### **开启子进程方式一** - **直接创建 Process 类的实例对象,由此就可以创建一个新的进程** #### 开启子进程方式二 - **通过继承 Process 类的子类,创建实例对象,也可以创建新的进程** - **继承 Process 类的子类需重写父类的 方法** ### **multiprocess** 主要函数介绍: ```c++ static void HeartbeatThreadFunc(MultiProcess *multi_process);//传播间隔函数 void HeartbeatThreadFuncInner();//定义传播函数工具 Status ParentProcess(const ProcessFuncCall &parent_process);//父进程 void ChildProcess(const ProcessFuncCall &child_process);//子进程 ``` 重点参数: heartbeat:传播间隔 stop = false:停止(结束)标记 msg_len = 0:单个消息长度 msg_total_len = 0:消息总长 pid : 进程号 返回进程的 ID 号。大多数操作系统都会为每个进程配备唯一的 ID 号 ```c++ uint64_t read_ready_flag = false;//正在read标记 uint64_t read_finish_flag = false;//read完成标记 ``` ```c++ private: uint8_t *shmat_addr_ = nullptr;//无符号8位shmat_addr 连接以后返回的地址设置为空 uint8_t *shmat_data_addr_ = nullptr;// uint64_t shmat_data_max_size_ = 0;//最大数据大小初始化 uint64_t memory_size_ = 0;//记忆内存大小 bool peer_stopped_ = false;//同伴停止标记 用于判断同伴进程是否停止 bool stopped_ = false;//设置结束标记初始化 MessageFlag *send_msg_ = nullptr;//指向发送的消息 初始化 MessageFlag *receive_msg_ = nullptr;// 指向接受的消息 初始化 ``` multi_process.h: ```c++ #ifndef MINDSPORE_CCSRC_CXXAPI_MULTI_PROCESS_H #define MINDSPORE_CCSRC_CXXAPI_MULTI_PROCESS_H #include #include #include "include/api/status.h" namespace mindspore { struct MessageFlag {//包含的消息标志 uint64_t heartbeat = 0;//传播间隔 uint64_t stop = false;//停止(结束)标记 uint64_t msg_len = 0;//单个消息长度 uint64_t msg_total_len = 0;//消息总长 uint64_t read_ready_flag = false;//正在read标记 uint64_t read_finish_flag = false;//read完成标记 }; class MultiProcess;//多进程 using ProcessFuncCall = std::function;//处理函数调用 using CreateBufferCall = std::function;//创建缓冲区调用 class MultiProcess { public: MultiProcess(); ~MultiProcess(); Status MainProcess(const ProcessFuncCall &parent_process, const ProcessFuncCall &child_process);//主进程 Status SendMsg(const void *buffer, uint64_t msg_len);//发送消息 Status ReceiveMsg(const CreateBufferCall &create_buffer_call);//接受消息 private: uint8_t *shmat_addr_ = nullptr;//无符号8位shmat_addr 连接以后返回的地址设置为空 uint8_t *shmat_data_addr_ = nullptr;// uint64_t shmat_data_max_size_ = 0;//最大数据大小初始化 uint64_t memory_size_ = 0;//记忆内存大小 bool peer_stopped_ = false;//同伴停止标记 用于判断同伴进程是否停止 bool stopped_ = false;//设置结束标记初始化 MessageFlag *send_msg_ = nullptr;//指向发送的消息 初始化 MessageFlag *receive_msg_ = nullptr;// 指向接受的消息 初始化 static void HeartbeatThreadFunc(MultiProcess *multi_process);//传播间隔函数 void HeartbeatThreadFuncInner();//传播函数内部 Status ParentProcess(const ProcessFuncCall &parent_process);//父进程 void ChildProcess(const ProcessFuncCall &child_process);//子进程 }; } // namespace mindspore #endif // MINDSPORE_CCSRC_CXXAPI_MULTI_PROCESS_H ``` multi_process.cpp: ```c++ #include "cxx_api/model/model_converter_utils/multi_process.h" #include #include wait.h> #include #include #include #include "mindspore/core/utils/log_adapter.h" #include "cxx_api/model/model_converter_utils/shared_memory.h" namespace mindspore { namespace { constexpr uint64_t kSharedMemorySize = 100ull 20; // 100 MB 定义可共享内存的大小 } MultiProcess::MultiProcess() = default; MultiProcess::~MultiProcess() = default; //主进程 Status MultiProcess::MainProcess(const ProcessFuncCall &parent_process, const ProcessFuncCall &child_process) { MS_EXCEPTION_IF_NULL(parent_process); MS_EXCEPTION_IF_NULL(child_process); Status ret; memory_size_ = kSharedMemorySize; // 100 MB 可共享内存的大小 SharedMemory shared_memory; ret = shared_memory.Create(memory_size_);//调用Create函数创建共享内存 返回ret标记 if (ret != kSuccess) {//判断ret MS_LOG_ERROR "Create shared memory failed";//创建共享内存失败 return ret; } pid_t pid = fork();//调用判断是否销毁结束函数 if (pid 0) {//判断pid 返回进程的 ID 号。大多数操作系统都会为每个进程配备唯一的 ID 号 shared_memory.Destroy();//销毁开始的共享内存 防止故障 MS_LOG_ERROR "Fork process to convert model failed";//转换模型的Fork进程失败 return kMEFailed; } ret = shared_memory.Attach();//调用 shared_memory.Attach 去关联共享内存 if (ret != kSuccess) { MS_LOG_ERROR "Process attach shared memory failed, pid " pid;//进程附加共享内存失败,并返回pid return ret; } shmat_addr_ = shared_memory.GetSharedMemoryAddr();//shmat_addr获取到的共享内存地址 if (shmat_addr_ == nullptr) {//如果连接以后返回的地址为空 MS_LOG_ERROR "Get shared memory failed";//获取共享内存失败 return ret; } constexpr size_t kMsgStructNum = 2;//kMsg结构编号 shmat_data_addr_ = shmat_addr_ + sizeof(MessageFlag) * kMsgStructNum;// shmat_data_max_size_ =//shmat_data数据大小 memory_size_ - (reinterpret_cast(shmat_data_addr_) - reinterpret_cast(shmat_addr_)); MS_LOG_INFO "Shm addr " (uint64_t)shmat_addr_; if (pid == 0) { ChildProcess(child_process); shared_memory.Detach();//实现拆卸(清除)功能 MS_LOG_INFO "Model converter: child process sleep waiting for exit signal."; //模型转换器:子进程睡眠等待退出信号 while (1) { // waiting for signal //等待信号量 } } else { // parent process 父进程 ret = ParentProcess(parent_process); shared_memory.Detach();//调用shared_memory 实现拆卸(清除)功能 MS_LOG_INFO "Model converter: parent process kills child of fork."; (void)kill(pid, SIGKILL);//杀死进程 constexpr uint32_t kMaxLoopCount = 5;//最大循环数 bool child_exited = false;//将判断子进程是否存在函数初始化为false for (uint32_t i = 0; i kMaxLoopCount; ++i) { int status; if (waitpid(pid, &status, WNOHANG) == pid) {//等待pid信号量 MS_LOG(INFO) "Child process " pid " exits success."; child_exited = true;//更新函数状态 break; } (void)sleep(1); } if (!child_exited) { MS_LOG(WARNING) "Child process " pid " has been killed but waitpid failed."; } shared_memory.Destroy(); //销毁共享内存 删除共享内存,不必担心返回代码 } return ret; } //父进程 Status MultiProcess::ParentProcess(const ProcessFuncCall &parent_process) { auto parent_msg = reinterpret_cast(shmat_addr_); auto child_msg = reinterpret_cast(shmat_addr_ + sizeof(MessageFlag)); send_msg_ = parent_msg; receive_msg_ = child_msg; std::thread heartbeat_thread(MultiProcess::HeartbeatThreadFunc, this); Status ret; try { ret = parent_process(this); if (ret != kSuccess) { MS_LOG_ERROR "Parent process process failed";//父进程进程失败 } } catch (const std::runtime_error &ex) { MS_LOG_ERROR "Catch parent process runtime error: " ex.what();//捕获父进程运行时错误 并返回失败原因 ret = kMEFailed; } stopped_ = true;//更新结束标记 send_msg_->stop = 1; heartbeat_thread.join(); return ret; } //子进程 void MultiProcess::ChildProcess(const ProcessFuncCall &child_process) { auto parent_msg = reinterpret_cast(shmat_addr_); auto child_msg = reinterpret_cast(shmat_addr_ + sizeof(MessageFlag)); send_msg_ = child_msg; receive_msg_ = parent_msg; std::thread heartbeat_thread(MultiProcess::HeartbeatThreadFunc, this); try { MS_EXCEPTION_IF_NULL(child_process); auto ret = child_process(this); if (ret != kSuccess) { MS_LOG_ERROR "Child process process failed"; } } catch (const std::runtime_error &ex) { MS_LOG_ERROR "Catch child process runtime error: " ex.what(); } stopped_ = true; send_msg_->stop = true; heartbeat_thread.join();//主线程等待子进程终止(强调:是主线程处于等的状态,而p是处于运行的状态),timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程 } //发送消息 Status MultiProcess::SendMsg(const void *buffer, uint64_t msg_len) { MS_EXCEPTION_IF_NULL(buffer); MS_LOG_INFO "Start to send message to peer process, msg len " msg_len; send_msg_->msg_total_len = msg_len; uint64_t cur_offset = 0; while (msg_len > cur_offset) { uint64_t sub_msg_len = std::min(msg_len - cur_offset, shmat_data_max_size_); if (sub_msg_len == 0) { MS_LOG(ERROR) "Invalid message len " sub_msg_len; return kMEFailed; } auto ret = memcpy_s(shmat_data_addr_, shmat_data_max_size_, static_cast(buffer) + cur_offset, sub_msg_len); if (ret != EOK) { MS_LOG(ERROR) "memcpy_s failed, ret = " ret; return kMEFailed; } cur_offset += sub_msg_len; send_msg_->msg_len = sub_msg_len; send_msg_->read_finish_flag = 0; send_msg_->read_ready_flag = 1; MS_LOG_INFO "Send start " cur_offset ", msg len " sub_msg_len ", total len " msg_len; while (!send_msg_->read_finish_flag && !peer_stopped_) { (void)usleep(1000); // 1ms } if (peer_stopped_) { if (!send_msg_->read_finish_flag) { return kMEFailed; } break; } MS_LOG_INFO "Send end " cur_offset ", msg len " sub_msg_len ", total len " msg_len; } MS_LOG_INFO "End to send message to peer process, msg len " msg_len; return kSuccess; } //接受消息 Status MultiProcess::ReceiveMsg(const CreateBufferCall &create_buffer_call) { uint64_t cur_offset = 0; uint8_t *msg_buffer = nullptr; uint64_t msg_len = 0; do { MS_LOG_INFO "Receive start from " cur_offset; while (!receive_msg_->read_ready_flag && !peer_stopped_) { (void)usleep(1000); // 1ms } if (peer_stopped_) { return kMEFailed; } if (msg_buffer == nullptr) { msg_len = receive_msg_->msg_total_len; msg_buffer = create_buffer_call(msg_len); } auto ret = memcpy_s(msg_buffer + cur_offset, msg_len - cur_offset, shmat_data_addr_, receive_msg_->msg_len); if (ret != EOK) { MS_LOG(INFO) "memcpy_s failed, ret = " ret; return kMEFailed; } cur_offset += receive_msg_->msg_len; receive_msg_->read_ready_flag = 0; receive_msg_->read_finish_flag = 1; MS_LOG_INFO "Receive end, current length " cur_offset ", total length " msg_len std::endl; } while (msg_len > cur_offset); return kSuccess; } //传播交互间隔函数 void MultiProcess::HeartbeatThreadFunc(MultiProcess *multi_process) { multi_process->HeartbeatThreadFuncInner(); } //传播函数内部 void MultiProcess::HeartbeatThreadFuncInner() { constexpr uint64_t kOvertime = 1024;//时间 uint64_t last_beat_cnt = 0;//最长发送时间 uint64_t repeat_cnt = 0;//回复时间 while (!stopped_) {//判断是否停止 if (receive_msg_->stop) { peer_stopped_ = true; MS_LOG_WARNING "Peer stopped"; break; } uint64_t heartbeat_gap = receive_msg_->heartbeat - last_beat_cnt; if (heartbeat_gap > 0 && heartbeat_gap kOvertime) { last_beat_cnt = receive_msg_->heartbeat; repeat_cnt = 0; } else { repeat_cnt++; if (repeat_cnt > 30) { // 30*100ms = 3s no reply peer_stopped_ = true; MS_LOG_WARNING "Peer stopped"; break; } } send_msg_->heartbeat += 1; (void)usleep(100000); // sleep 100 ms } } } // namespace mindspore ``` 模型转换器工具类到此结束,其中多有不足之处,还请包涵!
-
## 模型转换器工具类之共享内存篇Shared Memory 文件路径:mindspore\ccsrc\cxx_api\model\model_converter_utils\shared_memory 在这一篇博客中我将为大家介绍的是模型转换器工具类之共享内存篇Shared Memory。 ###### 什么是共享内存 共享内存是系统出于多个进程之间通讯的考虑,而预留的的一块内存区。 顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。 不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。共享内存是进程间通信中最简单的方式之一。 特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。 共享内存的通信原理示意图: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202111/01/194528tu3wdxkso0njoxyh.png) 对于上图我的理解是:当两个进程通过页表将虚拟地址映射到物理地址时,在物理地址中有一块共同的内存区,即共享内存,这块内存可以被两个进程同时看到。这样当一个进程进行写操作,另一个进程读操作就可以实现进程间通信。但是,我们要确保一个进程在写的时候不能被读,因此我们使用信号量来实现同步与互斥。 对于一个共享内存,实现采用的是引用计数的原理,当进程脱离共享存储区后,计数器减一,挂架成功时,计数器加一,只有当计数器变为零时,才能被删除。当进程终止时,它所附加的共享存储区都会自动脱离。 ##### 主要函数组成及其功能介绍 Create函数:创建共享内存 Attach函数:标记数据 去关联共享内存 当一个进程不需要共享内存的时候,就需要去关联。该函数并不删除所指定的共享内存区,而是将之前用shmat函数连接好的共享内存区脱离目前的进程。 Detach函数:实现拆卸(清除)功能 Destroy函数:销毁共享内存 删除共享内存,不必担心返回代码 shmget函数:创建共享内存 用于开辟或指向一块共享内存,返回获得共享内存区域的ID,如果不存在指定的共享区域就创建相应的区域。 shmat 函数:挂接共享内存 ```c++ Status Create(uint64_t memory_size);//创建共享内存 Status Attach();//标记 去关联共享内存 void Detach();//中断 清除数据 void Destroy();//杀死删除共享内存 销毁共享内存 ``` ##### 重点参数介绍 [参数shmid]:共享存储段的标识符。 [参数*shmaddr]:连接以后返回的地址。 [参数shmid]:共享存储段标识符。 [参数IPC_RMID]:指定的执行操作,设置为IPC_RMID时表示可以删除共享内存 ```c++ private: friend class MultiProcess;//声明友元类 多进程 uint8_t *GetSharedMemoryAddr() { return shmat_addr_; }//获取(无符号八位)共享内存地址 int shm_id_ = -1;// shm标记 共享存储段的标识符。 uint8_t *shmat_addr_ = nullptr;//连接共享内存 先定义为无符号八位并指向空 如果shmaddr = 0,则存储段连接到由内核选择的第一个可以地址上 ``` ##### 标注代码部分: - shared_memory.h ```c++ #ifndef MINDSPORE_CCSRC_CXXAPI_SHARED_MEMORY_H #define MINDSPORE_CCSRC_CXXAPI_SHARED_MEMORY_H #include #include "include/api/status.h" namespace mindspore { class SharedMemory {//共享内存类 public: Status Create(uint64_t memory_size);//创建共享内存 Status Attach();//标记 去关联共享内存 void Detach();//中断 清除数据 void Destroy();//杀死删除共享内存 销毁共享内存 private: friend class MultiProcess;//声明友元类 多进程 uint8_t *GetSharedMemoryAddr() { return shmat_addr_; }//获取(无符号八位)共享内存地址 int shm_id_ = -1;// shm标记 共享存储段的标识符。 uint8_t *shmat_addr_ = nullptr;//连接共享内存 先定义为无符号八位并指向空 如果shmaddr = 0,则存储段连接到由内核选择的第一个可以地址上 }; } // namespace mindspore #endif // MINDSPORE_CCSRC_CXXAPI_SHARED_MEMORY_H ``` - shared_memory.cpp ```c++ #include "cxx_api/model/model_converter_utils/shared_memory.h" #include shm.h> #include stat.h> #include #include "mindspore/core/utils/log_adapter.h" namespace mindspore { Status SharedMemory::Create(uint64_t memory_size) {//创建共享内存 auto access_mode = S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP; shm_id_ = shmget(IPC_PRIVATE, memory_size, IPC_CREAT | IPC_EXCL | access_mode); //shmget函数 创建共享内存 用于开辟或指向一块共享内存,返回获得共享内存区域的ID,如果不存在指定的共享区域就创建相应的区域。 if (shm_id_ == -1) {//判断是否是初始的-1 MS_LOG_ERROR "Shared memory creation failed. Errno " + std::to_string(errno);//共享内存创建失败。 return kMCFailed;//返回失败标记 } MS_LOG_INFO "shmget success, shm id " shm_id_;//获取shm成功,返回shm id return kSuccess;//返回成功标记 } Status SharedMemory::Attach() {//去关联共享内存 //标记 shmaddr = 0,则存储段连接到由内核选择的第一个可以地址上 void *shmat_addr = shmat(shm_id_, nullptr, 0);//连接以后返回的地址。shmat_地址含shm id shmat函数挂接共享内存 就是把共享内存区对象映射到调用进程的地址空间 if (shmat_addr == reinterpret_cast(-1)) {//如果shmat_地址和reinterpret_cast相同即shmat_地址指向重新解释部件 MS_LOG_ERROR "Shared memory attach failed. Errno " + std::to_string(errno);//共享内存连接失败 return kMCFailed;//返回失败标记 } shmat_addr_ = reinterpret_cast(shmat_addr); return kSuccess;//返回成功标记 } void SharedMemory::Detach() {//实现拆卸(清除)功能 if (shmat_addr_ != nullptr) {// 如果 shmat_地址不为空 auto err = shmdt(shmat_addr_); if (err == -1) { MS_LOG_ERROR "Shared memory detach failed. Errno " + std::to_string(errno);//共享内存分离失败。 return; } } shmat_addr_ = nullptr;//连接共享内存更新为初始空状态 便于下一次的使用 } void SharedMemory::Destroy() { // Remove the shared memory and never mind about the return code. //删除共享内存,不必担心返回代码 auto err = shmctl(shm_id_, IPC_RMID, nullptr); //销毁共享内存 //shmid:共享存储段标识符。IPC_RMID:指定的执行操作,设置为IPC_RMID时表示可以删除共享内存。 if (err == -1) { std::string errMsg = "Unable to remove shared memory with id " + std::to_string(shm_id_);//无法删除id为--的共享内存 errMsg += ". Errno :" + std::to_string(errno); errMsg += "\nPlesae remove it manually using ipcrm -m command";//请使用ipcrm-m命令手动删除它 MS_LOG_ERROR errMsg; } } } // namespace mindspore ``` ##### 总结: (1)优点:我们可以看到使用共享内存进行进程之间的通信是非常方便的,而且函数的接口也比较简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,加快了程序的效率。 (2)缺点:共享内存没有提供同步机制,这使得我们在使用共享内存进行进程之间的通信时,往往需要借助其他手段来保证进程之间的同步工作。 因学识与能力有限,如有不足之处,请多多包涵与指教。 比赛即将结束我将把这一段时间所做的东西进行一次比较完善的总结,由于是第一次参加比赛的刚大二小萌新,所做的东西比较乱且杂,可能比较难以理解,希望大家多多包含,最后也祝愿大家都能收获多多,能学到东西,对昇思这一深度学习框架有更加深的理解。
-
## **对mindspore\lite\include\context.h的注释学习** 路径:mindspore\lite\include\context.h。 Context的中文翻译为:语境; 上下文; 背景; 环境,在开发中我们经常说称之为“上下文”,那么这个“上下文”到底是指什么意思呢? - 我们可以理解为“上下文”:它贯穿整个应用; - 也可以理解成“运行环境”:它提供了一个应用运行所需要的信息,资源,系统服务等; - 同样可以理解成“场景”:用户操作和系统交互这一过程就是一个场景,比如Activity之间的切换,服务的启动等都少不了Context。 一个Activity就是一个Context,一个Service也是一个Context。Android程序员把“场景”抽象为Context类,他们认为用户和操作系统的每一次交互都是一个场景,比如打电话、发短信,这些都是一个有界面的场景,还有一些没有界面的场景,比如后台运行的服务(Service)。一个应用程序可以认为是一个工作环境,用户在这个环境中会切换到不同的场景,这就像一个前台秘书,她可能需要接待客人,可能要打印文件,还可能要接听客户电话,而这些就称之为不同的场景,前台秘书可以称之为一个应用程序。 总结一下什么是context: 一个应用环境的全局信息,字面意思是上下文的意思; Context是一个抽象类; 允许我们通过Context获取各种资源,服务,或者去启动一个Activity,发送一个广播,等等; 在lite\include\context.h这里呢主要是用来获取相关的CpuGpuNpu以及设备的相关信息。并在后端和运行时获取相关的设备描述表,用于保存执行期间的环境变量。 # mindspore ## **Context** **Context**类用于保存执行中的环境变量。 ### 公有成员函数 #### SetThreadNum ``` void SetThreadNum(int32_t thread_num); ``` 设置运行时的线程数,该选项仅MindSpore Lite有效。 - 参数 - `thread_num`: 运行时的线程数。 #### GetThreadNum ``` int32_t GetThreadNum() const; ``` 获取当前线程数设置。 - 返回值 当前线程数设置。 #### SetAllocator ``` void SetAllocator(const std::shared_ptr &allocator); ``` 设置Allocator,Allocator定义了用于动态内存分配和释放的内存池,该选项仅MindSpore lite有效。 - 参数 - `allocator`: Allocator指针。 #### GetAllocator ``` std::shared_ptr GetAllocator() const; ``` 获取当前Allocator设置。 - 返回值 当前Allocator的指针。 #### MutableDeviceInfo ``` std::vector> &MutableDeviceInfo(); ``` 修改该**context**下的[DeviceInfoContext](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/mindspore.html?highlight=context#deviceinfocontext)数组,仅mindspore lite支持数组中有多个成员是异构场景。 - 返回值 存储DeviceInfo**Context**的vector的引用。 ## DeviceInfo**Context** DeviceInfo**Context**类定义不同硬件设备的环境信息。 ### 公有成员函数 #### GetDeviceType ``` virtual enum DeviceType GetDeviceType() const = 0 ``` 获取该DeviceInfo**Context**的类型。 - 返回值 该DeviceInfo**Context**的类型。 ``` enum DeviceType { kCPU = 0, kMaliGPU, kNvidiaGPU, kKirinNPU, kAscend910, kAscend310, // add new type here kInvalidDeviceType = 100, }; ``` #### Cast ``` template std::shared_ptr Cast(); ``` 在打开`-fno-rtti`编译选项的情况下提供类似RTTI的功能,将DeviceInfo**Context**转换为`T`类型的指针,若转换失败返回`nullptr`。 - 返回值 转换后`T`类型的指针,若转换失败则为`nullptr`。 ## CPUDeviceInfo 派生自[DeviceInfo**Context**](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/mindspore.html?highlight=context#deviceinfocontext),模型运行在CPU上的配置,仅mindspore lite支持该选项。 ### 公有成员函数 | 函数 | 说明 | | ---------------------------------- | ------------------------------------------------------------ | | `void SetThreadAffinity(int mode)` | 设置线程亲和性模式 - `mode`: 0:无亲和性, 1:大核优先, 2:小核优先。 | | `int GetThreadAffinity() const` | - 返回值: 已配置的线程亲和性模式 | | `void SetEnableFP16(bool is_fp16)` | 用于指定是否以FP16精度进行推理 - `is_fp16`: 是否以FP16精度进行推理 | | `bool GetEnableFP16() const` | - 返回值: 已配置的精度模式 | ## MaliGPUDeviceInfo 派生自[DeviceInfo**Context**](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/mindspore.html?highlight=context#deviceinfocontext),模型运行在GPU上的配置,仅mindspore lite支持该选项。 ### 公有成员函数 | 函数 | 说明 | | ---------------------------------- | ------------------------------------------------------------ | | `void SetEnableFP16(bool is_fp16)` | 用于指定是否以FP16精度进行推理 - `is_fp16`: 是否以FP16精度进行推理 | | `bool GetEnableFP16() const` | - 返回值: 已配置的精度模式 | ## KirinNPUDeviceInfo 派生自[DeviceInfo**Context**](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/mindspore.html?highlight=context#deviceinfocontext),模型运行在NPU上的配置,仅mindspore lite支持该选项。 ### 公有成员函数 | 函数 | 说明 | | ---------------------------------- | ------------------------------------------------------------ | | `void SetFrequency(int frequency)` | 用于指定NPU频率 - `frequency`: 设置为1(低功耗)、2(均衡)、3(高性能)、4(极致性能),默认为3 | | `int GetFrequency() const` | - 返回值: 已配置的NPU频率模式 | ## NvidiaGPUDeviceInfo 派生自[DeviceInfo**Context**](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/mindspore.html?highlight=context#deviceinfocontext),模型运行在GPU上的配置,mindspore lite不支持该选项。 ### 公有成员函数 | 函数 | 说明 | | -------------------------------------- | ------------------------------------- | | `void SetDeviceID(uint32_t device_id)` | 用于指定设备ID - `device_id`: 设备ID | | `uint32_t GetDeviceID() const` | - 返回值: 已配置的设备ID | ## Ascend910DeviceInfo 派生自[DeviceInfo**Context**](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/mindspore.html?highlight=context#deviceinfocontext),模型运行在Ascend910上的配置,mindspore lite不支持该选项。 ### 公有成员函数 | 函数 | 说明 | | -------------------------------------- | ------------------------------------- | | `void SetDeviceID(uint32_t device_id)` | 用于指定设备ID - `device_id`: 设备ID | | `uint32_t GetDeviceID() const` | - 返回值: 已配置的设备ID | ## Ascend310DeviceInfo 派生自[DeviceInfo**Context**](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/mindspore.html?highlight=context#deviceinfocontext),模型运行在Ascend310上的配置,mindspore lite不支持该选项。 ### 公有成员函数 | 函数 | 说明 | | ------------------------------------------------------------ | ------------------------------------------------------------ | | `void SetDeviceID(uint32_t device_id)` | 用于指定设备ID - `device_id`: 设备ID | | `uint32_t GetDeviceID() const` | - 返回值: 已配置的设备ID | | `void SetInsertOpConfigPath(const std::string &cfg_path)` | 模型插入[AIPP](https://support.huaweicloud.com/adevg-ms-atlas200dkappc32/atlasadm_01_0023.html)算子 - `cfg_path`: [AIPP](https://support.huaweicloud.com/adevg-ms-atlas200dkappc32/atlasadm_01_0023.html)配置文件路径 | | `std::string GetInsertOpConfigPath()` | - 返回值: 已配置的[AIPP](https://support.huaweicloud.com/adevg-ms-atlas200dkappc32/atlasadm_01_0023.html) | | `void SetInputFormat(const std::string &format)` | 指定模型输入formatt - `format`: 可选有`"NCHW"`,`"NHWC"`等 | | `std::string GetInputFormat()` | - 返回值: 已配置模型输入format | | `void SetInputShape(const std::string &shape)` | 指定模型输入shape - `shape`: 如`"input_op_name1:1,2,3,4;input_op_name2:4,3,2,1"` | | `std::string GetInputShape()` | - 返回值: 已配置模型输入shape | | `void SetOutputType(enum DataType output_type)` | 指定模型输出type - `output_type`: 仅支持uint8、fp16和fp32 | | `enum DataType GetOutputType()` | - 返回值: 已配置模型输出type | | `void SetPrecisionMode(const std::string &precision_mode)` | 配置模型精度模式 - `precision_mode`: 可选有`"force_fp16"`,`"allow_fp32_to_fp16"`,`"must_keep_origin_dtype"`或者`"allow_mix_precision"`,默认为`"force_fp16"` | | `std::string GetPrecisionMode(t)` | - 返回值: 已配置模型精度模式 | | `void SetOpSelectImplMode(const std::string &op_select_impl_mode)` | 配置算子选择模式 - `op_select_impl_mode`: 可选有`"high_performance"`和`"high_precision"`,默认为`"high_performance"` | | `std::string GetOpSelectImplMode()` | - 返回值: 已配置算子选择模式 | ```C #ifndef MINDSPORE_LITE_INCLUDE_CONTEXT_H_ #define MINDSPORE_LITE_INCLUDE_CONTEXT_H_ #include #include "include/ms_tensor.h" #include "include/lite_utils.h" #include "include/lite_types.h" namespace mindspore::lite { /// \brief CpuDeviceInfo defined for CPU's configuration information. // 为CPU配置信息定义的简要CpuDeviceInfo。 typedef struct CpuDeviceInfo {//Cpu设备信息 bool enable_float16_ = false; /** prior enable float16 inference */ //先验16位推理 CpuBindMode cpu_bind_mode_ = MID_CPU;//cpu绑定模式 中央处理器 } CpuDeviceInfo; /// \brief GpuDeviceInfo defined for GPU's configuration information. // 为GPU的配置信息定义的GpuDeviceInfo简介。 typedef struct GpuDeviceInfo {//Gpu设备信息 bool enable_float16_ = false; /** prior enable float16 inference */ //先验16位推理 uint32_t gpu_device_id_ = 0;// gpu设备id } GpuDeviceInfo; /// \brief NpuDeviceInfo defined for NPU's configuration information. // 为NPU的配置信息定义的简要NpuDeviceInfo。 typedef struct NpuDeviceInfo {//Npu设备信息 int frequency_ = 3; /** npu frequency inference, low 1, medium 2, high 3, extreme 4, other values will be set to 3 */ //npu频率推断,低1、中2、高3、极端4,其他值将设置为3 } NpuDeviceInfo; /// \brief Ascend310DeviceInfo defined for Ascend's configuration information. // 为Ascend的配置信息定义的简要Ascend310DeviceInfo typedef struct AscendDeviceInfo {//提升设备信息 uint32_t device_id_;//设备id } AscendDeviceInfo; /// \brief DeviceInfo defined for backend's configuration information. // 为后端配置信息定义的简要DeviceInfo。 struct DeviceInfo {//struct类型,设置不同硬件的环境变量。设备信息包含前面的四个信息 CpuDeviceInfo cpu_device_info_; GpuDeviceInfo gpu_device_info_; NpuDeviceInfo npu_device_info_; AscendDeviceInfo ascend310_device_info_; }; /// \brief DeviceContext defined for holding backend's configuration information. // 为保存后端配置信息而定义的简要DeviceContext。 struct DeviceContext {//设备描述表 DeviceType device_type_ = DT_CPU;//设备类型 dtu处理器 DeviceInfo device_info_;//设备信息 std::string provider_{}; std::string provider_device_{};//提供程序设备 AllocatorPtr allocator_ = nullptr; }; /// \brief Context defined for holding environment variables during runtime. // 为在运行时保存环境变量而定义的简短上下文。 struct Context {//上下文(描述表) String vendor_name_; int thread_num_ = 2; /** thread number config for thread pool */ //线程池的线程编号配置 bool enable_parallel_ = false; Vector affinity_core_list_; /** explicitly specify the core to be bound. priority use affinity core list */ //显式指定要绑定的核心。优先使用关联核心列表 AllocatorPtr allocator = nullptr;//分配器Ptr分配器 #ifndef NOT_USE_STL DeviceContextVector device_list_ = {{DT_CPU, {false, MID_CPU}}};//设备上下文向量 设备列表 #else DeviceContextVector device_list_; #endif // NOT_USE_STL 不使用STL DelegatePtr delegate = nullptr; //使用delegate代表空 }; } // namespace mindspore::lite #endif // MINDSPORE_LITE_INCLUDE_CONTEXT_H_ ``` ## Device**Context**Vector 元素为[**Device\**Context\****](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/lite.html#devicecontext) 的**vector**。 ## Device**Context** Device**Context**类定义不同硬件设备的环境信息。 ### 公有属性 #### device_type ``` device_type ``` [**DeviceType**](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/lite.html#devicetype) 枚举类型。默认为**DT_CPU**,标明设备信息。 #### device_info_ ``` device_info_ ``` **struct**类型,包含[**CpuDeviceInfo**](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/lite.html#cpudeviceinfo)、[**GpuDeviceInfo**](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/lite.html#gpudeviceinfo)和[**NpuDeviceInfo**](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/lite.html#npudeviceinfo)。 ## DeviceInfo **struct**类型,设置不同硬件的环境变量。 ### 公有属性 #### cpu_device_info_ ``` cpu_device_info_ ``` [**CpuDeviceInfo**](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/lite.html#cpudeviceinfo) 类型,配置CPU的环境变量。 #### gpu_device_info_ ``` gpu_device_info_ ``` [**GpuDeviceInfo**](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/lite.html#gpudeviceinfo) 类型,配置GPU的环境变量。 #### npu_device_info_ ``` npu_device_info_ ``` [**NpuDeviceInfo**](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/lite.html#npudeviceinfo) 类型,配置NPU的环境变量。 ## CpuDeviceInfo CpuDeviceInfo类,配置CPU的环境变量。 ### Public Attributes #### enable_float16_ ``` enable_float16_ ``` **bool**值,默认为**false**,用于使能float16 推理。 > 使能float16推理可能会导致模型推理精度下降,因为在模型推理的中间过程中,有些变量可能会超出float16的数值范围。 #### cpu_bind_mode_ ``` cpu_bind_mode_ ``` [**CpuBindMode**](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/lite.html#cpubindmode) 枚举类型,默认为**MID_CPU**。 ## GpuDeviceInfo GpuDeviceInfo类,用来配置GPU的环境变量。 ### 公有属性 #### enable_float16_ ``` enable_float16_ ``` **bool**值,默认为**false**,用于使能float16 推理。 > 使能float16推理可能会导致模型推理精度下降,因为在模型推理的中间过程中,有些变量可能会超出float16的数值范围。 ## NpuDeviceInfo NpuDeviceInfo类,用来配置NPU的环境变量。 ### 公有属性 #### frequency ``` frequency_ ``` **int**值,默认为**3**,用来设置NPU频率,可设置为1(低功耗)、2(均衡)、3(高性能)、4(极致性能)。 参考学习:[相关官方手册](https://www.mindspore.cn/lite/api/zh-CN/r1.3/api_cpp/lite.html?highlight=context#devicecontextvector "相关官方手册")
-
对mindspore\ccsrc\common\thread_pool的理解注释 thread_pool 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在mindspore中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这就是”池化资源”技术产生的原因。线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。 线程运行时间 假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。 ` 如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。` 合理利用线程池能够带来三个好处。第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。 mindspore\ccsrc\common\thread_pool.h ```cpp #ifndef MINDSPORE_CCSRC_COMMON_THREAD_POOL_H_ #define MINDSPORE_CCSRC_COMMON_THREAD_POOL_H_ #include #include #include #include #include #include #include #include #include #include #include #include "utils/log_adapter.h" namespace mindspore { namespace common { enum Status { FAIL = -1, SUCCESS = 0 };//成功失败 using Task = std::function; class ThreadPool { public: ~ThreadPool(); ThreadPool(const ThreadPool &) = delete; ThreadPool &operator=(const ThreadPool &) = delete; static ThreadPool &GetInstance();//获取实例 bool SyncRun(const std::vector &tasks);//判断是否同步运行 size_t GetSyncRunThreadNum() { return max_thread_num_; }//获取同步运行线程数 void ClearThreadPool();//清除线程池 private: ThreadPool();//线程池 void SyncRunLoop();//同步运行循环 size_t max_thread_num_{1};//最大线程数 std::mutex pool_mtx_;//池 std::atomic_bool exit_run_ = {false};//退出运行 std::queue task_queue_;//任务队列 std::mutex task_mutex_;//任务互斥 std::condition_variable task_cond_var_; size_t task_finished_count_{0};//任务完成计数器 std::condition_variable finished_cond_var_;//完成状态变量 std::vector sync_run_threads_{};//同步运行线程 }; } // namespace common } // namespace mindspore #endif // MINDSPORE_CCSRC_COMMON_THREAD_POOL_H_ ``` 线程池的工作过程 process_core_num/ corePoolSize:线程池的核心线程数,线程池中运行的线程数永远不会超过 process_core_num/corePoolSize 个,默认情况下可以一直存活。 max_thread_num :线程池允许的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。当提交一个新任务到线程池时,线程池会做如下判断: 如果正在运行的线程数量小于 kcorePoolSize,那么马上创建线程运行这个任务; 如果正在运行的线程数量大于或等于process_core_num/ kcorePoolSize,那么将这个任务放入队列; 如果这时候队列满了,而且正在运行的线程数量小于 max_thread_num_,那么还是要创建非核心线程立刻运行这个任务; 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么会采取任务拒绝策略. mindspore\ccsrc\common\thread_pool.cc ```cpp #include "common/thread_pool.h" #include #include #include "utils/log_adapter.h" #include "utils/convert_utils_base.h" #include "utils/ms_exception.h" namespace mindspore { namespace common { #ifdef ENABLE_D const size_t kDeviceNum = 8;//设置k设备数 #endif const size_t kMaxThreadNum = 23;//最大线程数 ThreadPool::ThreadPool() //线程池 size_t process_core_num = std::thread::hardware_concurrency() - 1;//硬件并发s数 if (process_core_num 1) {//进程核心数小于1时 process_core_num = 1; } #ifdef ENABLE_D max_thread_num_ = process_core_num / kDeviceNum; //最大线程数=进程核心数/k设备数 #else max_thread_num_ = process_core_num; #endif if (max_thread_num_ 1) {//最大在线线程数小于1时 max_thread_num_ = 1; } if (max_thread_num_ > kMaxThreadNum) {//当最大在线线程数大于可最大线程数 max_thread_num_ = kMaxThreadNum; } } void ThreadPool::SyncRunLoop() {//同步运行循环 while (true) { Task task; { std::unique_lock lock(task_mutex_); task_cond_var_.wait(lock, [this] { return !task_queue_.empty() || exit_run_; });//任务队列为空 if (exit_run_) { return; } task = task_queue_.front(); task_queue_.pop(); } try { task(); } catch (std::exception &e) { MsException::Instance().SetException();//设置例外 } { std::unique_lock task_lock(task_mutex_); task_finished_count_ = task_finished_count_ + 1; } finished_cond_var_.notify_one();//完成条件变量通知1 } } bool ThreadPool::SyncRun(const std::vector &tasks) {//判断是否同步运行 if (tasks.size() == 1) { auto ret = tasks[0](); return ret == SUCCESS; } std::unique_lock lock(pool_mtx_); exit_run_ = false; size_t task_num = tasks.size(); size_t thread_num = sync_run_threads_.size(); if (thread_num max_thread_num_ && thread_num task_num) { auto new_thread_num = max_thread_num_; if (task_num max_thread_num_) { new_thread_num = task_num; } for (size_t i = thread_num; i new_thread_num; ++i) { sync_run_threads_.emplace_back(std::thread(&ThreadPool::SyncRunLoop, this)); } } for (auto &task : tasks) { std::lock_guard task_lock(task_mutex_); task_queue_.push(task); task_cond_var_.notify_one(); } { std::unique_lock task_lock(task_mutex_); finished_cond_var_.wait(task_lock, [this, task_num] { return task_num == task_finished_count_; }); task_finished_count_ = 0; } return true; } ThreadPool &ThreadPool::GetInstance() {//获取实例 static ThreadPool instance; return instance; } void ThreadPool::ClearThreadPool() {//清除线程池 std::lock_guard sync_run_lock(pool_mtx_);//同步运行锁 if (exit_run_) {//如果退出运行 return; } exit_run_ = true; task_cond_var_.notify_all();//任务条件变量通知所有 for (auto &it : sync_run_threads_) { if (it.joinable()) { it.join(); } } sync_run_threads_.clear();//清除同步运行线程 } ThreadPool::~ThreadPool() { ClearThreadPool(); } } // namespace common } // namespace mindspore ```
-
相对路径:mindspore\ccsrc\common\duplex_pipe.h/duplex_pipe.cc duplex_pipe。双向管道,目的是让网络上的两个程序能进行自动化交流。 传统的管道只能从一端输入、一端输出。双向管道不仅可以让进程 A 的输出作为进程 B 的输入,也会让进程 B 的输出作为进程 A 的输入。这样就可以让两个进程实现交流。 一个例子:我们想让计算机自动计算结果,那我先运行程序1,生成数据;运行程序2,输入程序1生成的数据,获得结果后再反馈给程序1。但转念一想:我们这样手工进行输入输出的工作,不是比计算本身更枯燥、单调?而且是重复的劳动!理所当然应该由计算机自动完成。 为完成这个任务,计算机要做的就是让“程序1”和“程序2”进行聊天:“程序1”的输出作为“程序2”的输入、“程序2”的输出作为“程序1”的输入。这一定义会让人立刻联想到“管道”(exe1 | exe2),但遗憾的是“管道”是单向的——只能满足“程序1”的输出作为“程序2”的输入。 这就到duplex_pipe展示的时候了。 ```cpp h文件是C语言和【C++】语言的头文件,一般在【.h】类的头文件里面只放入函数声明,宏定义,函数原型,而具体的实现在【.cpp】文件里面。 ``` mindspore\ccsrc\common\duplex_pipe.h ```cpp #ifndef MINDSPORE_CCSRC_COMMON_DUPLEX_PIPE_H_ #define MINDSPORE_CCSRC_COMMON_DUPLEX_PIPE_H_ #include //unistd.h为Linux/Unix系统中内置头文件,包含了许多系统服务的函数原型,例如read函数、write函数和getpid函数等。 //其作用相当于windows操作系统的"windows.h",是操作系统为用户提供的统一API接口,方便调用系统提供的一些服务。 #include //signal.h头文件定义了一个变量类型sig_atomic_t、两个函数调用和一些宏来处理程序执行期间报告的不同信号. #include #include #include //初始值设定项列表 #include #include "utils/log_adapter.h" #define DP_DEBUG MS_LOG(DEBUG) "[DuplexPipe] " #define DP_INFO MS_LOG(INFO) "[DuplexPipe] " #define DP_ERROR MS_LOG(ERROR) "[DuplexPipe] " #define DP_EXCEPTION MS_LOG(EXCEPTION) "[DuplexPipe] " namespace mindspore { // A tool to run a command as child process and build a duplex pipe between them. // Similar to 'popen()', but use duplex not simplex pipe, more like 'socketpair'. //作为子进程运行命令并在它们之间构建双工管道的工具。 //类似于“popen()”,但使用双工而不是单工管道,更像“socketpair”。 class DuplexPipe : public std::enable_shared_from_this { public: constexpr inline static int kBufferSize = 4096;//设置缓冲区大小 constexpr inline static unsigned int kTimeOutSeconds = 5;//超时秒数 DuplexPipe() = default;//预设 ~DuplexPipe(); // Create a subprocess and open a duplex pipe between local and remote //创建子流程并打开本地和远程之间的双工管道 int Open(std::initializer_list arg_list, bool append_fds = false);//初始值设定项列表 void Close(); void SetTimeOutSeconds(unsigned int secs) { time_out_secs_ = secs; }//设置超时秒数 void SetTimeOutCallback(const std::shared_ptr> cb) { time_out_callback_ = cb; }//设置超时回调 void SetFinalizeCallback(const std::shared_ptr> cb) { finalize_callback_ = cb; }//设置终结回调 // Write the 'buf' to remote stdin //将“buf”写入远程标准输入 void Write(const std::string &buf, bool flush = true); // Read from remote stdout/stderr into 'c_buf_' //从远程标准输出/标准输出读入“c_buf_” std::string Read(); void WriteWithStdout(const std::string &buf, bool flush); std::string ReadWithStdin();//用标准符号书写 用标准文本阅读 DuplexPipe &operator(const std::string &buf); DuplexPipe &operator>>(std::string &buf); private: void SetTimeOut() {//设置超时 if (time_out_callback_ != nullptr && signal_handler_ != nullptr) { signal_handler_->SetAlarm(time_out_secs_); } } void CancelTimeOut() {//取消超时 if (time_out_callback_ != nullptr && signal_handler_ != nullptr) { signal_handler_->CancelAlarm(); } } void NotifyTimeOut() {//通知超时 if (time_out_callback_ != nullptr) { (*time_out_callback_)(); } Close(); DP_EXCEPTION "Time out when read from pipe"; //从管道读取时超时 } void NotifyFinalize() { if (finalize_callback_ != nullptr) {//如果完成回调 (*finalize_callback_)(); } } // Pipe: { Local:fd1_[1] --> Remote:fd1_[0] } // Remote:fd1_[0] would be redirected by subprocess's stdin. // Local:fd1_[1] would be used by 'Write()' as output. int fd1_[2]; // Pipe: { Remote:fd2_[1] --> Local:fd2_[0] } // Remote:fd2_[1] would be redirected by subprocess's stdout. // Local:fd2_[0] would be used by 'Read()' as input. int fd2_[2]; // // Used and returned by 'Read()'. //由“Read()”使用并返回。 // std::string buf_; char c_buf_[kBufferSize];//设置预缓冲区大小的空间 int local_stdin_;//本地标准输入 int local_stdout_;//本地标准输出 int remote_stdin_;//远程标准输入 int remote_stdout_;//远程标准输出 class SignalHandler {//信号处理器 public: SignalHandler(const std::weak_ptr &dp, pid_t *pid);//信号处理器 ~SignalHandler(); void SetAlarm(unsigned int interval_secs);//设置警报 void CancelAlarm();//取消警报 private: static void SigAlarmHandler(int sig);//信号报警处理器 static void SigPipeHandler(int sig);//Sig管道处理器 static void SigChildHandler(int sig);//Sig子处理程序 inline static std::weak_ptr dp_; inline static pid_t *child_pid_; }; unsigned int time_out_secs_ = kTimeOutSeconds;//设置暂停秒 std::shared_ptr> time_out_callback_;//超时回调 std::shared_ptr> finalize_callback_;//完成回调 // signal_handler_ has a pid_ pointer, so it must be ahead of pid_ //信号处理器有一个pid指针,所以它必须在pid之前_ std::shared_ptr signal_handler_; // Subprocess id in parent process, // otherwise zero in child process. //父进程中的子进程名, //否则在子进程中为零。 pid_t pid_; }; } // namespace mindspore #endif // MINDSPORE_CCSRC_COMMON_DUPLEX_PIPE_H_ ``` mindspore\ccsrc\common\duplex_pipe.cc ```cpp #include "common/duplex_pipe.h" #include wait.h> #include #include #include namespace mindspore { DuplexPipe::~DuplexPipe() { // pid_ 0 means the child process is invalid or closed, pid_ == 0 means this process is child //pid_u0表示子进程无效或已关闭,pid_u==0表示此进程为子进程 if (pid_ > 0) { (void)kill(pid_, SIGKILL);//pid>0结束子进程 } } int DuplexPipe::Open(std::initializer_list arg_list, bool append_fds) {//打开管道开始工作 if (pipe(fd1_) == -1) {//判断pipe DP_EXCEPTION "pipe 1 failed, errno: " errno;//双联管道1失败,错误号: } if (pipe(fd2_) == -1) { close(fd1_[0]); close(fd1_[1]); DP_EXCEPTION "pipe 2 failed, errno: " errno;//双联管道1失败,错误号: } pid_ = fork(); if (pid_ 0) {//pid_u0表示子进程无效或已关闭 close(fd1_[0]); close(fd1_[1]); close(fd2_[0]); close(fd2_[1]); DP_EXCEPTION "fork failed, errno: " errno;//fork失败,错误号: } else if (pid_ == 0) { // Remote process 远程进程 此进程为子进程 remote_stdout_ = dup(STDOUT_FILENO); remote_stdin_ = dup(STDIN_FILENO); close(fd1_[1]); close(fd2_[0]); if (!append_fds) { dup2(fd1_[0], STDIN_FILENO); dup2(fd2_[1], STDOUT_FILENO); } std::vector args; std::transform(arg_list.begin(), arg_list.end(), std::back_inserter(args), [](const std::string &arg) -> const char * { return arg.c_str(); }); if (append_fds) { std::string fd10 = std::to_string(fd1_[0]).c_str(); args.emplace_back(fd10.c_str()); std::string fd21 = std::to_string(fd2_[1]).c_str(); args.emplace_back(fd21.c_str()); } args.emplace_back(nullptr); if (execvp(args[0], const_cast(&args[0])) == -1) { DP_EXCEPTION "execute " args[0] " failed, errno: " errno; } } else { // Local process 本地进程 DP_INFO "Local process, id: " getpid() ", " fd2_[0] "/" fd1_[1]; local_stdout_ = dup(STDOUT_FILENO); local_stdin_ = dup(STDIN_FILENO); close(fd1_[0]); close(fd2_[1]); signal_handler_ = std::make_shared(weak_from_this(), &pid_); } return 0; } void DuplexPipe::Write(const std::string &buf, bool flush) {//写入函数 // Write the string into pipe 将字符串写入管道 if (write(fd1_[1], buf.data(), buf.size()) == -1) { DP_ERROR "write failed, errno: " errno;//写入失败,错误号: return; } if (flush) {//如果flush为真 // Flush into the pipe 存入管道 if (write(fd1_[1], "\n", 1) == -1) { DP_ERROR "write failed, errno: " errno; return; } } DP_DEBUG " [" buf "]"; } std::string DuplexPipe::Read() {//读取函数 // Read the string from pipe 从管道中读取字符串 std::string buf; // Read one line or multiple lines 读一行或多行 while (1) { SetTimeOut();//设置超时 ssize_t size = read(fd2_[0], c_buf_, kBufferSize); // MAYBE BLOCKED, Till reading something //可能被封锁了,直到读到一些东西 if (size = 0) { break; } CancelTimeOut();//取消超时 bool line_end = c_buf_[size - 1] == '\n';//判断线路长度是否到最长 buf.append(c_buf_, line_end ? size - 1 : size); // Copy without the last '\n' 不带最后一个“\n”的复制 if (line_end) {//判断是否到最后 break; } } DP_DEBUG ">> [" buf "]"; return buf; } void DuplexPipe::WriteWithStdout(const std::string &buf, bool flush) {//用标准符号书写 dup2(fd1_[1], STDOUT_FILENO); // Write the string into pipe 将字符串写入管道 std::cout buf; if (flush) { // Flush into the pipe 存入管道 std::cout std::endl; } dup2(local_stdout_, STDOUT_FILENO); } std::string DuplexPipe::ReadWithStdin() {//用标准文本阅读 std::string buf; dup2(fd2_[0], STDIN_FILENO); // Maybe blocked 可能被封锁了 SetTimeOut();//设置超时 std::getline(std::cin, buf); // Not use 'std::cin >>' to include space 不使用“std::cin>>”包含空格 CancelTimeOut();//取消超时 dup2(local_stdin_, STDIN_FILENO); return buf; } DuplexPipe &DuplexPipe::operator(const std::string &buf) { Write(buf);//写入 return *this; } DuplexPipe &DuplexPipe::operator>>(std::string &buf) { buf = Read();//读取 return *this; } void DuplexPipe::Close() {//关闭函数 close(fd1_[0]); close(fd1_[1]); close(fd2_[0]); close(fd2_[1]); pid_ = -1; } DuplexPipe::SignalHandler::SignalHandler(const std::weak_ptr &dp, pid_t *pid) {//信号处理器 dp_ = dp;//转化一下 child_pid_ = pid;//转化一下 signal(SIGCHLD, SigChildHandler); signal(SIGPIPE, SigPipeHandler); } DuplexPipe::SignalHandler::~SignalHandler() {} void DuplexPipe::SignalHandler::SetAlarm(unsigned int interval_secs) {//设置警报 signal(SIGALRM, SigAlarmHandler); alarm(interval_secs);//间隔秒 } void DuplexPipe::SignalHandler::CancelAlarm() { alarm(0); }//取消警报 void DuplexPipe::SignalHandler::SigAlarmHandler(int sig) {//信号报警处理器 DP_INFO "Signal: " sig ", child_pid_: " child_pid_; auto shared_dp = dp_.lock(); if (shared_dp != nullptr) {//共享dp空间不为空 shared_dp->NotifyTimeOut(); } if (child_pid_ != nullptr) {//且如果子进程不为空 *child_pid_ = -1; } } void DuplexPipe::SignalHandler::SigPipeHandler(int sig) {//Sig管道处理器 DP_INFO "Signal: " sig ", child_pid_: " child_pid_; auto shared_dp = dp_.lock(); if (shared_dp != nullptr) {//共享dp空间不为空 shared_dp->NotifyFinalize(); } if (child_pid_ != nullptr) {//且如果子进程不为空 *child_pid_ = -1; } } void DuplexPipe::SignalHandler::SigChildHandler(int sig) {//Sig子处理程序 int status; if (child_pid_ != nullptr) {//如果子进程不为空 (void)waitpid(*child_pid_, &status, WNOHANG | WUNTRACED); *child_pid_ = -1; } } } // namespace mindspore ```
-
在某些电脑安装完设计器后,去拾取网页元素或者点击录制器,在设计器右上角有如下提示,有时重启电脑也解决不了这个问题。原因分析: 这机器使用的这个端口被其本地的其他程序占用了,导致studio就用不了这个接口了。解决方法:法一、简单快速的方法是本地电脑新建一个用户,环境是干净的,使端口都是空闲的。法二、使用cmd命令查找到相应的程序并做杀进程使端口空闲先来。1、退出WeAutomate设计器2、打开一个cmd窗口,并输入netstat -ano | findstr 578893、根据查询出来的PID(上图红框所示),查询进程,可以看到是哪个程序,这个可以略过tasklist|findstr 53764、使用taskkill杀掉进程,注意替换查询到的PID号码taskkill /pid 5376 -t -f基本既可以解决“请检查57889端口是否被占用类问题”如有解决不了的情况请大家留言。
-
**对mindspore\ccsrc\pipeline\jit\parse\parse.h的部分注释** 知识浅薄,还有许多未理解之处,欢迎各位纠正、讨论。 相对路径:ore\ccsrc\pipeline\jit\parse\parse.h 这部分代码的主要功能就是作语法分析。 h文件是C语言和【C++】语言的头文件,一般在【.h】类的头文件里面只放入函数声明,宏定义,函数原型,而具体的实现在【.cpp】文件里面。 C程序的定义文件以.c为后缀,C++程序的定义文件通常以.cpp为后缀(也有一些系统以.cc或.cxx为后缀) **头文件** ```cpp #ifndef MINDSPORE_CCSRC_PIPELINE_JIT_PARSE_PARSE_H_//防止双重定义 #define MINDSPORE_CCSRC_PIPELINE_JIT_PARSE_PARSE_H_//预编译命令 //导入系统库 #include /*limits.h 头文件决定了各种变量类型的各种属性。 定义在该头文件中的宏限制了各种变量类型(比如 char、int 和 long)的值。 这些限制指定了变量不能存储任何超出这些限制的值*/ #include #include #include #include /*一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2, 并且最多包含一个 null 元素。正如其名称所暗示的,此接口模仿了数学上的 set 抽象。*/ #include //栈数据类型相关结构和操作 #include //引用自定义头文件 #include "utils/misc.h" #include "ir/anf.h" #include "pipeline/jit/parse/parse_base.h" #include "pipeline/jit/parse/python_adapter.h" #include "pipeline/jit/parse/function_block.h" ``` **资源导入** ```cpp namespace mindspore {//创建一个名为mindspore的空间,并在其中嵌套一个名为parse(作语法分析)的空间 namespace parse { // Parse status define 解析状态定义 enum ParseStatusCode : int64_t {//解析状态代码 PARSE_SUCCESS = 0, PARSE_FUNCTION_IS_NULL, // python function is null python函数为空 PARSE_PARAMETER_INVALID, // parameter is invalid 解析参数无效 PARSE_NO_RETURN, // function no return node 函数不返回节点 PARSE_NODE_TYPE_NO_MATCH, // ast node type is error ast节点类型为错误 PARSE_NODE_TYPE_UNKNOWN, // node type is unknown 节点类型未知 PARSE_NODE_METHOD_UNSUPPORTED, // no method to parse the node 没有解析节点的方法 PARSE_DONT_RESOLVE_SYMBOL, // can't resolve the string 无法解析字符串 PARSE_NOT_SUPPORTED_COMPARE_EXPR, // the comparison is not supported 不支持这种比较 PARSE_FAILURE = 0xFF //错误0xFF 32进制-1 }; // max loop count of for statement, when loop count is less then this value, the for loop will be unrolled, otherwise it // will be sunk(i.e. not unrolled) // NOTE: Since when the for loop was unrolled, it depends backend operators `tuple_getitem` and `scalar_add` which were // not implemented, so here set MAX_FOR_LOOP_COUNT to int64_t max limit to override default value `600`. This will make // the for loop will always be unrolled, but don't worry about the memory were exhausted, an exception will be raised // when function call depth exceeds the limit `context.get_context('max_call_depth')`. /*for语句的最大循环计数,当循环计数小于此值时,for循环将展开,否则 将下沉(即未展开) 注意:因为当for循环展开时,它取决于后端操作符“tuple getitem”元组获取项和“scalar add”标量加法,它们是 未实现,因此此处将循环计数的最大值设置为int64_t MAX limit以覆盖默认值“600”。这将使 for循环将始终展开,但不要担心内存耗尽,将引发异常 当函数调用深度超过限制上下文时。获取上下文最大调用深度 */ const int64_t MAX_FOR_LOOP_COUNT = std::numeric_limits::max(); //循环计数的最大值 数值极限 class AstNodeType;//Ast节点类型 class ParseAst;//解析Ast // Save loop info for 'continue' and 'break' statements. //保存“continue”和“break”语句的循环信息 struct Loop {//循环模块 // Loop header block. //循环头块 FunctionBlockPtr header;//功能块PTR头 // Loop iterator node, used in 'for loop'. //循环迭代器节点,用于“for循环” AnfNodePtr iterator;//Anf节点Ptr迭代器 // Loop end block. //循环结束块。 FunctionBlockPtr end;//功能块Ptr端 Loop(const FunctionBlockPtr &header, const AnfNodePtr &iterator, const FunctionBlockPtr &end) : header(header), iterator(iterator), end(end) {} ~Loop() = default; }; // Loop context for loop stack management. //用于循环堆栈管理的循环上下文 class LoopContext {//循环上下文 public: LoopContext(std::stack *loops, const FunctionBlockPtr &header, const AnfNodePtr &iterator) : loops_(loops) { loops_->emplace(header, iterator, nullptr);//回路的定位 } ~LoopContext() { loops_->pop(); } const FunctionBlockPtr &EndBlock() const { return loops_->top().end; } //功能块 循环顶(起始) private: std::stack *loops_; }; ``` **实现解析的主要功能部分** ```cpp // Parser to parse python function // 解析python函数的解析器 class Parser { public: explicit Parser(const std::shared_ptr &ast); ~Parser() {} FuncGraphPtr ParseFuncGraph();//解析函数图 FuncGraphPtr func_graph() const { return func_graph_; }//获取函数图 ParseStatusCode errcode() const { return errcode_; }//解析状态:代码错误代码 std::shared_ptr ast() const { return ast_; }//获取解析Ast // get location info from the ast node // 从ast节点获取位置信息 LocationPtr GetLocation(const py::object &node) const;//获取位置 位置Ptr static void InitParserEnvironment(const py::object &obj);//在其解析器环境中 static void CleanParserResource();//清除解析资源 static FuncGraphPtr GetTopFuncGraph() { return top_func_graph_.lock(); }//功能块获取顶函数图 static void UpdateTopFuncGraph(const FuncGraphPtr &func_graph);//更新顶函数图 private://功能块 // process the stmt node method list //解析处理stmt节点方法列表 FunctionBlockPtr ParseReturn(const FunctionBlockPtr &block, const py::object &node); // parse expression //解析表达式 FunctionBlockPtr ParseExpr(const FunctionBlockPtr &block, const py::object &node); // process a if statement //解析处理if语句 FunctionBlockPtr ParseIf(const FunctionBlockPtr &block, const py::object &node); // process a while statement //解析处理while语句 FunctionBlockPtr ParseWhile(const FunctionBlockPtr &block, const py::object &node); // process a for statement //解析处理一条语句 FunctionBlockPtr ParseFor(const FunctionBlockPtr &block, const py::object &node);//解析for语句 FunctionBlockPtr ParseForIter(const FunctionBlockPtr &block, const py::object &node);//Iter的解析 FunctionBlockPtr ParseForLoop(const FunctionBlockPtr &block, const py::object &node);//对循环解析 // process a function def statement //解析处理函数def语句 FunctionBlockPtr ParseFunctionDef(const FunctionBlockPtr &block, const py::object &node);//解析函数定义 // process a augment assign //解析处理扩充分配 FunctionBlockPtr ParseAugAssign(const FunctionBlockPtr &block, const py::object &node);//解析Aug分配 // process a global declaration //解析处理全局声明 FunctionBlockPtr ParseGlobal(const FunctionBlockPtr &block, const py::object &node);//解析全局 // process assign statement //解析进程分配语句 FunctionBlockPtr ParseAssign(const FunctionBlockPtr &block, const py::object &node);//解析赋值语句 // process break statement //解析进程中断语句 FunctionBlockPtr ParseBreak(const FunctionBlockPtr &block, const py::object &node);//解析(break)中断语句 // process continue statement //解析进程继续语句 FunctionBlockPtr ParseContinue(const FunctionBlockPtr &block, const py::object &node);//解析(Continue)继续语句 // process pass statement //解析进程通过语句 FunctionBlockPtr ParsePass(const FunctionBlockPtr &block, const py::object &node);//解析过程通过语句 // process the expr and slice node method list //解析处理expr和切片节点方法列表 AnfNodePtr ParseBinOp(const FunctionBlockPtr &block, const py::object &node);//解析二进制 // process a variable name //解析处理变量名 AnfNodePtr ParseName(const FunctionBlockPtr &block, const py::object &node);//解析变量名称 // process NoneType //解析处理非类型 AnfNodePtr ParseNone(const FunctionBlockPtr &block, const py::object &node);//无解析 // process Ellipsis //解析过程省略 AnfNodePtr ParseEllipsis(const FunctionBlockPtr &block, const py::object &node);//解析省略 // process a integer or float number //解析处理整数或浮点数 AnfNodePtr ParseNum(const FunctionBlockPtr &block, const py::object &node);//解析整数或浮点数 // process a string variable //解析处理字符串变量 AnfNodePtr ParseStr(const FunctionBlockPtr &block, const py::object &node);//解析字符串变量 // process a Constant //解析处理常数 AnfNodePtr ParseConstant(const FunctionBlockPtr &block, const py::object &node);//解析常数 // process a name //解析理名称 AnfNodePtr ParseNameConstant(const FunctionBlockPtr &block, const py::object &node);//解析名称常量 // process a function call //解析处理函数调用 AnfNodePtr ParseCall(const FunctionBlockPtr &block, const py::object &node);//解析调用 // process function 'super' //解析过程函数“super超级” AnfNodePtr ParseSuper(const FunctionBlockPtr &block, const py::list &args);//解析超级 // process the if expression // 解析处理if表达式 AnfNodePtr ParseIfExp(const FunctionBlockPtr &block, const py::object &node);//解析if表达式 // process class type define // 解析进程类类型定义 AnfNodePtr ParseAttribute(const FunctionBlockPtr &block, const py::object &node);//解析属性 // process a compare expression //解析处理比较表达式 AnfNodePtr ParseCompare(const FunctionBlockPtr &block, const py::object &node);//解析比较表达式 // process a bool operation //解析处理布尔运算 AnfNodePtr ParseBoolOp(const FunctionBlockPtr &block, const py::object &node);//解析布尔运算 // process a lambda operation //解析处理lambda操作 AnfNodePtr ParseLambda(const FunctionBlockPtr &block, const py::object &node);//解析Lambda操作 // process a tuple //解析处理元组 AnfNodePtr ParseTuple(const FunctionBlockPtr &block, const py::object &node);//解析元组 // process a List // 解析处理列表 AnfNodePtr ParseList(const FunctionBlockPtr &block, const py::object &node);//解析列表 // process a Subscript //解析处理下标 AnfNodePtr ParseSubscript(const FunctionBlockPtr &block, const py::object &node);//解析下标 // process a slice // 解析处理切片 AnfNodePtr ParseSlice(const FunctionBlockPtr &block, const py::object &node);//解析切片 // process a extslice // 解析处理extslice外部切片 AnfNodePtr ParseExtSlice(const FunctionBlockPtr &block, const py::object &node);//解析ExtSlice外部切片 //解析ExtSlice // process a tuple // 解析处理元组 AnfNodePtr ParseIndex(const FunctionBlockPtr &block, const py::object &node);//解析索引 // process a unaryop // 解析一元处理 AnfNodePtr ParseUnaryOp(const FunctionBlockPtr &block, const py::object &node);//解析一元运算 // process a dict ast node expression // 解析处理dict ast节点表达式 AnfNodePtr ParseDict(const FunctionBlockPtr &block, const py::object &node);//解析指令 // generate argument nodes for ast function node // 为ast函数节点生成参数节点 void GenerateArgsNodeForFunction(const FunctionBlockPtr &block, const py::object &function_node); //为函数生成Args节点 // generate argument default value for ast function node // 为ast函数节点生成参数默认值 void GenerateArgsDefaultValueForFunction(const FunctionBlockPtr &block, const py::object &function_node); //为函数生成Args默认值 // parse ast function node // 解析ast功能节点 FunctionBlockPtr ParseFunction(const py::object &function_node, const FunctionBlockPtr &block = nullptr); //解析函数 // parse ast statements // 解析ast语句 FunctionBlockPtr ParseStatements(FunctionBlockPtr block, const py::object &stmt_node); //解析语句 // parse one ast statement node //解析一个ast语句节点 FunctionBlockPtr ParseStatement(const FunctionBlockPtr &block, const py::object &node); // parse an ast expression node //解析ast表达式节点 AnfNodePtr ParseExprNode(const FunctionBlockPtr &block, const py::object &node); //解析表达式节点 void MakeConditionBlocks(const FunctionBlockPtr &block, const FunctionBlockPtr &trueBlock, const FunctionBlockPtr &falseBlock); //制作条件块 void RemoveUnnecessaryPhis();//删除不必要的网络钓鱼 // write a new var // 声明一个新的变量 void WriteAssignVars(const FunctionBlockPtr &block, const py::object &targ, const AnfNodePtr &value_node); // 声明赋值变量 // assign value to single variable name // 为单个变量名赋值 void HandleAssignName(const FunctionBlockPtr &block, const py::object &targ, const AnfNodePtr &assigned_node); // 句柄分配名称 // assign value to tuple //给元组赋值 void HandleAssignTuple(const FunctionBlockPtr &block, const py::object &targ, const AnfNodePtr &assigned_node); // 句柄分配元组 // assign value to class member //为类成员赋值 void HandleAssignClassMember(const FunctionBlockPtr &block, const py::object &targ, const AnfNodePtr &assigned_node); //句柄指定类成员 // assign value to subscript //给下标赋值 void HandleAssignSubscript(const FunctionBlockPtr &block, const py::object &targ, const AnfNodePtr &assigned_node); //句柄分配下标 // process a bool operation value list //处理布尔操作值列表 AnfNodePtr ProcessBoolOpValueList(const FunctionBlockPtr &block, const py::list &value_list, AstSubType mode); //进程布尔操作值列表 CNodePtr GenerateIteratorInFor(const FunctionBlockPtr &block, const pybind11::object &node, const AnfNodePtr &op_iter); //生成迭代器 CNodePtr GenerateCondInFor(const ParameterPtr &iter_param, const FunctionBlockPtr &header_block, const AnfNodePtr &op_hasnext); //生成条件信息 FunctionBlockPtr GenerateBlockInFor(const TraceInfoPtr &trace_info);//功能块:生成块信息 bool ParseKeywordsInCall(const FunctionBlockPtr &block, const py::object &node, std::vector *packed_arguments); //判断是否声明中的关键字已经解析 bool ParseArgsInCall(const FunctionBlockPtr &block, const py::list &args, std::vector *packed_arguments, std::vector *group_arguments); //判断调用中的参数是否解析 AnfNodePtr GenerateAnfNodeForCall(const FunctionBlockPtr &block, const AnfNodePtr &call_function_anf_node, const std::vector &packed_arguments, const std::vector &group_arguments, bool need_unpack) const; //为调用生成Anf节点 ScopePtr GetScopeForParseFunction();//获取解析函数的作用域 void BuildMethodMap();//构建方法映射 FunctionBlockPtr MakeFunctionBlock(const Parser &parse) { FunctionBlockPtr block = std::make_shared(parse); // In order to keep effect order in the sub-graphs which generated by control flow. // We copy the flags from the top graph to the sub-graphs. //为了保持控制流生成的子图中的效果顺序。 // //我们将标志从顶部图复制到子图。 if (func_graph_ && !func_graph_->attrs().empty()) {//如果函数图不为空且函数图的属性为空 block->func_graph()->set_attrs(func_graph_->attrs()); } func_block_list_.push_back(block);//功能块列表。撤回 return block;//返回块 } // return a make tuple for input elements list //返回输入元素列表的生成元组 AnfNodePtr GenerateMakeTuple(const FunctionBlockPtr &block, const std::vector &element_nodes); //生成生成元组 // shared_ptr will be hold by GraphManager, so just hold a weak ref here. // 共享ptr将由Graph Manager保持,因此只需在此处保持弱引用即可。 static FuncGraphWeakPtr top_func_graph_; // Python function id, used to indicate whether two CNodes come from the same Python function //Python函数id,用于指示两个节点是否来自同一个Python函数 const std::shared_ptr &ast_; FuncGraphPtr func_graph_; // error code setwhen parsing ast tree // 分析ast树时出现错误代码SET ParseStatusCode errcode_;//解析状态代码:错误代码 // hold all reference for FunctionBlock in this round of parsing, // so in FunctionBlock class we can use FunctionBlock* in member // pre_blocks_ and jumps_ to break reference cycle. //在这一轮解析中保留功能块的所有引用, //所以在函数块类中,我们可以在成员中使用函数块 //预块和跳转以中断参考循环。 std::vector func_block_list_; using pStmtFunc = FunctionBlockPtr (Parser::*)(const FunctionBlockPtr &block, const py::object &node); using pExprFunc = AnfNodePtr (Parser::*)(const FunctionBlockPtr &block, const py::object &node); // define the function map to parse ast Statement // 定义函数映射以解析ast语句 std::map stmt_method_map_; // define the function map to parse ast expression //定义函数映射以解析ast表达式 std::map expr_method_map_; // Save current loops to support 'continue', 'break' statement. // 保存当前循环以支持“continue”、“break”语句。 std::stack loops_; }; // AST node type define code to ast //AST节点类型定义AST的代码 class AstNodeType { public: AstNodeType(const py::object &node, const std::string &name, AstMainType type) : node_(node), node_name_(name), main_type_(type) {} ~AstNodeType() {} std::string node_name() const { return node_name_; } py::object node() const { return node_; } AstMainType main_type() const { return main_type_; } private: const py::object &node_;//常量化目标节点 const std::string node_name_;//常量化节点名称 AstMainType main_type_;//Ast主类型 }; using AstNodeTypePtr = std::shared_ptr;//使用Ast节点类型Ptr空间 // A helper class to parse python function //用于解析python函数的帮助器类 class ParseAst { public: explicit ParseAst(const py::object &obj) : obj_(obj), target_type_(PARSE_TARGET_UNKNOW), function_line_offset_(-1) {} ~ParseAst() = default; bool InitParseAstInfo(const std::string &python_mod_get_parse_method = PYTHON_MOD_GET_PARSE_METHOD); //判断是否解析Ast信息 py::object GetAstNode();//获取Ast节点 py::list GetArgs(const py::object &func_node);//获取Args py::list GetArgsDefaultValues(const py::object &func_node);//获取参数的默认值 AstNodeTypePtr GetNodeType(const py::object &node);//获取节点类型 AstSubType GetOpType(const py::object &node);//获取类型 template //调用python方法 py::object CallParserObjMethod(const std::string &method, const T &... args) { return python_adapter::CallPyObjMethod(parser_, method, args...);//调用python适配器模块方法 } template //调用python模块函数 py::object CallParseModFunction(const std::string &function, const T &... args) { return python_adapter::CallPyModFn(module_, function, args...);//调用python适配器模块函数 } const std::string &function_name() const { return function_name_; }//返回目标函数名 const std::string &function_module() const { return function_module_; }//返回目标函数模块 const std::string &function_filename() const { return function_filename_; }//返回目标函数文件名 int64_t function_line_offset() const { return function_line_offset_; }//返回目标功能线偏移量 py::function function() { return function_; }//返回函数 ParseTargetTypeDef target_type() const { return target_type_; }//返回目标目标类型 //解析目标类型定义目标类型 py::object obj() { return obj_; }//返回目标对象 py::object parser() { return parser_; }//返回目标解析器 py::object module() { return module_; }//返回目标模块 py::object ast_tree() { return ast_tree_; }//返回目标ast树 bool IsClassMember(const py::object &node);//(bool)判断是否是class的一员 private: // save obj,eg: class instance or function // 保存对象,例如:类实例或函数 py::object obj_;//对象 // function or class method. //函数或类方法。 py::function function_;//功能函数 py::object ast_tree_;//对象ast树 py::object parser_;//对象解析器 py::module module_;//模块 // Is function or method // 是函数还是方法 ParseTargetTypeDef target_type_;//解析目标类型Def目标类型 std::string function_name_;//函数名 std::string function_module_;//函数模块 std::string function_filename_;//函数文件名 int64_t function_line_offset_;//功能线偏移量 }; // update the graph flags // 更新图形标志 bool UpdateFuncGraphFlags(const py::object &obj, const FuncGraphPtr &func_graph); // 判断函数图是否更新 AnfNodePtr GetMixedPrecisionCastHelp(const FuncGraphPtr &func_graph, const AnfNodePtr ¶m);//获得混合精密帮助 TypePtr GetMixedPrecisionTargetType(const FuncGraphPtr &func_graph);//获取混合精度目标类型 } // namespace parse } // namespace mindspore #endif // MINDSPORE_CCSRC_PIPELINE_JIT_PARSE_PARSE_H_ ``` 以上即为本篇的所有内容,因学识与能力有限,如有不足之处,请多多包涵与指教。
上滑加载中
推荐直播
-
全面解析华为云EI-API服务:理论基础与实践应用指南
2024/11/29 周五 18:20-20:20
Alex 华为云学堂技术讲师
本期直播给大家带来的是理论与实践结合的华为云EI-API的服务介绍。从“主要功能,应用场景,实践案例,调用流程”四个维度来深入解析“语音交互API,文字识别API,自然语言处理API,图像识别API及图像搜索API”五大场景下API服务,同时结合实验,来加深开发者对API服务理解。
回顾中 -
企业员工、应届毕业生、在读研究生共探项目实践
2024/12/02 周一 19:00-21:00
姚圣伟 在职软件工程师 昇腾社区优秀开发者 华为云云享专家 HCDG天津地区发起人
大神带你一键了解和掌握LeakyReLU自定义算子在ONNX网络中应用和优化技巧,在线分享如何入门,以及在工作中如何结合实际项目进行学习
即将直播 -
昇腾云服务ModelArts深度解析:理论基础与实践应用指南
2024/12/03 周二 14:30-16:30
Alex 华为云学堂技术讲师
如何快速创建和部署模型,管理全周期AI工作流呢?本期直播聚焦华为昇腾云服务ModelArts一站式AI开发平台功能介绍,同时结合基于ModelArts 的实践性实验,帮助开发者从理论到实验更好地理解和使用ModelArts。
去报名
热门标签