• [技术干货] linux+docker+MindSpore安装与使用
    由于Docker的安装较为容易,在各种Linux发行版的源中一般都有,这里就不过多介绍,我们就先假设Docker已经安装成功。在使用时,需要先启动Docker的服务:systemctl daemon-reloadsystemctl start dockersystemctl status docker在Docker上配置MindSpore环境我们按照官方文件,从华为云的镜像库中拉取MindSpore的cpu版本的镜像文件:docker pull swr.cn-south-1.myhuaweicloud.com/mindspore/mindspore-cpu:1.5.0 拉取完成后,可以通过docker images指令查看镜像列表, 在准备好MindSpore的容器化编程环境之后,我们可以进入这个镜像去测试一下MindSpore的一些基础用例。运行容器的方式是docker run,但是为了持久化的运行镜像中的/bin/bash,我们需要加上-it指令配置。拉取容器镜像时一般是用REPOSITORY这一列的镜像名称来拉取,但是由于这个名字实在是有点长,我们也可以通过拉取IMAGE ID来进入容器实例内。创建的python测试用例:import numpy as npimport mindspore.context as contextimport mindspore.ops as opsfrom mindspore import Tensor context.set_context(mode=context.PYNATIVE_MODE, device_target="CPU") x = Tensor(np.ones([1,3,3,4]).astype(np.float32))y = Tensor(np.ones([1,3,3,4]).astype(np.float32))print(ops.tensor_add(x, y))在官方提供的这个容器镜像中,只存在一个python3的python版本,因此一般情况下直接使用python指令来运行相关代码即可: 在得到这一串数字之后,我们知道即使有一些版本相关的告警信息,但是这个例子是已经被成功的执行了的,现在我们再来回顾一下这个例子执行的是一个什么样的功能。这个案例其实是创建两个多维的数组,这里相关的数据结构名为Tensor,也就是张量。但是这里我们将容器作为一个编程环境来使用,因此我们希望可以把相关的数据写入到新的容器镜像中。首先我们用docker ps的指令来查看历史记录中的CONTAINER ID。 在获取到需要保存的CONTAINER ID之后,我们就可以使用docker commit指令。 前面所创建的python测试用例是没有保存到这个原始镜像中。MindSpore自动微分测试计算一个目标函数的导数,在一个可微的函数下,其最大值或最小值一定会满足一阶导数为0这一个条件,因此我们常常会去寻找构建的这个函数在某一个点的导数。寻找极值点的方法有很多,自动微分现在成为一个比较主流的方案,通过机器来优化计算一个给定模型的导数。创建的python测试用例:import numpy as npimport mindspore.context as contextimport mindspore.nn as nnimport mindspore.ops as opsfrom mindspore import Tensorfrom mindspore import ParameterTuple, Parameterfrom mindspore import dtype as mstypecontext.set_context(mode=context.GRAPH_MODE, device_target="CPU")class Net(nn.Cell):    def __init__(self):        super(Net, self).__init__()        self.matmul = ops.MatMul()        self.z = Parameter(Tensor(np.array([1.0], np.float32)), name='z')    def construct(self, x, y):        x = x * self.z        out = self.matmul(x, y)        return out class GradNetWrtX(nn.Cell):    def __init__(self, net):        super(GradNetWrtX, self).__init__()        self.net = net        self.grad_op = ops.GradOperation()    def construct(self, x, y):        gradient_function = self.grad_op(self.net)        return gradient_function(x, y) x = Tensor([[0.8, 0.6, 0.2], [1.8, 1.3, 1.1]], dtype=mstype.float32)y = Tensor([[0.11, 3.3, 1.1], [1.1, 0.2, 1.4], [1.1, 2.2, 0.3]], dtype=mstype.float32)output = GradNetWrtX(Net())(x, y)print(output)这个案例中给出了一个关于x,y,zx,y,z三个变量的函数,光看函数形式的话其实就是f(x,y,z)=xyzf(x,y,z)=xyz。但是这里面展开来看的话,x,yx,y分别是2×3的矩阵与3×3的张量/多维矩阵,zz可以当成是一个单独的变量来计算,这里值就是1。具体矩阵乘法的调用方法可以参考官网上给出的接口文档.结果: 精度缺失在误差范围内,可以接受。成功。 
  • [技术干货] web场景-Tomcat调优
    1.1 Tomcat简介Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。1.2 物理机调优方法1.2.1 Tomcat参数调优目的:通过修改tomcat的配置文件,可以有效提高tomcat性能方法:按照如下所示信息,配置/home/apache-tomcat-9.0.20/conf/server.xml的各个参数。<Connector  executor="tomcatThreadPool"    port="10000" protocol="HTTP/1.1"    acceptCount="10000"    maxConnections="10000"    connectionTimeout="20000"    compression="off"    compressionMinSize="2048"    URIEncoding="utf-8"    tcpNoDelay="true"    enableLookups="false"    useURIValidationHack="false"    disableUploadTimeout="false"    connectionUploadTimeout="150000"    keepAliveTimeout="12000"    maxKeepAliveRequests="1000"redirectPort="8443" /> 1.2.2 Tomcat 亲和性设置Tomcat支持cpu绑核,利用cpu亲和性设置,可以使用taskset或numctl工具进行绑核操作方法:taskset -c N ./startup.sh或numactl -C N ./startup.shN表示要指定核的序号,例如 将tomcat进程绑定到0-3core上:taskset -c 0-3  ./startup.sh1.2.3 容器场景调优方法容器与物理机共享网口,在物理机上执行绑核脚本。以4core的http短连接场景的绑核脚本为例,脚本内容如下,如果要修改绑核脚本,只需修改要绑定的网口名eth1,以及要绑定的core,然后在关闭irqbalance.service的情况下使用脚本即可:#!/bin/bashcnt=2eth1=enp3s0ethtool -L $eth1 combined $cntirq1=`cat /proc/interrupts| grep -E ${eth1} | head -1 | awk -F ':' '{print $1}'`irq1=`echo $irq1`i=0while(( $i < 1))do    for cpunum in 2 3    do        echo $cpunum "->" $irq1        echo  $cpunum > /proc/irq/$irq1/smp_affinity_list        let "irq1++"    done   let "i++"done脚本中参数及命令说明参数及命令名称参数及命令解释cnt 网口队列数eth1使用的网口名irq1网口eth1对应的中断号cpunum分配给网口eth1用于处理网卡中断的核ethtool -L $eth1 combined $cnt设置网口队列长度为核数cat /proc/interrupts | grep $eth1 | awk -F ':' '{print $1}'查询网口中断数echo $cpunum > /proc/irq/$irq/smp_affinity_list根据中断号,将每个中断各绑定在一个核上此脚本只是容器场景下网卡调优方法1.3 虚拟机调优方法虚拟机采用网卡直通的模式,每个虚拟机配置一个虚拟网口,在虚拟机内部执行绑核脚本。以4core的http短连接场景的绑核脚本为例,脚本内容如下,若要修改绑核脚本,只需修改要绑定的网口eth1,以及要绑定的core,在关闭irqbalance.service的情况下使用脚本即可。#!/bin/bashcnt=2eth1=enp5s0ethtool -L $eth1 combined $cntirq1=`cat /proc/interrupts| grep -E ${eth1} | head -1 | awk -F ':' '{print $1}'`irq1=`echo $irq1`i=0while(( $i < 2))do    for cpunum in  3    do        echo $cpunum "->" $irq1        echo  $cpunum > /proc/irq/$irq1/smp_affinity_list        let "irq1++"    done  let "i++"done脚本中参数名称及解释参数名称参数解释cnt网口队列数eth1实际使用的网口名irq1网口eth1对应的中断号cpunum分配给网口eth1用于处理网卡中断的核此脚本只是虚拟机场景下的网卡调优,虚拟机场景下的亲和性设置与物理机一致.
  • [运维技巧] DWS登录容器
    1.简述登录DWS服务容器,首先登录COP--》CDK中EI集群的master节点,然后使用k8s命令登录对应的容器2.DWS管控面涉及的服务或容器有命名空间(namespace)为 dwsdms-collectiondms-monitoringdwscontroller命名空间(namespace)为 ecfdbseventdbsinsightDbsmonitorecfclustermanager命名空间(namespace)为 dws-maintaindwsmaintaintool3.登录管控面CDK集群Master节点步骤1 登录ManageOne运维面OC。步骤2 选择该Region的ServiceOM。步骤3 进入计算资源->虚拟机->搜索EICommon。找到EICommon-Region-Master-01机器的地址。步骤4 使用ssh登录opsadmin用户到该机器,并切换到root。root密码为统一密码表查到的。一般为Hwsimg@sre2019。802的默认密码:x86:HxVg#1uJd$YQWU3yK2sarm:Hwsimg@sre2019803的默认密码:opsadmin/Hwsimg#ops2019$  root/Hwsimg@sre20194.登录运维容器密文解密,mysql登录,节点登录步骤1 使用root用户登录Master节点之后(参考(三)),输入kubectl get pod –n dws-maintain。查看运维容器命名空间下的容器。步骤2 输入kubectl exec –ti {容器名} –n dws-maintain bash。进入容器。5.登录dwscontroller容器定位管控面问题查看服务日志步骤1 使用root用户登录Master节点之后,输入kubectl get pod –n dws。查看运维容器命名空间下的容器。选择dwscontroller前缀步骤2 输入kubectl exec –ti {容器名} –n dws bash。进入容器6.登录dms-monitoring,dms-collection容器定位监控面板问题查看日志步骤1 使用root用户登录Master节点之后,输入kubectl get pod –n dws。查看dws命名空间下的容器。选择dms-monitoring,dms-collection对应的前缀步骤2 输入kubectl exec –ti {容器名} –n dws bash。进入容器7.登录dbsevent,dbsinsight,dbsmonitor,ecfclustermanager容器   定位ecf集群,DWS集群状态,事件管理问题查看日志步骤1 使用root用户登录Master节点之后,输入kubectl get pod –n ecf。查看ecf命名空间下的容器。选择对应容器前缀步骤2 输入kubectl exec –ti {容器名} –n ecf bash。进入容器
  • [交流分享] 【Atlas 500 14】Atlas 500老架构版本升级新架构版本后通过FD下发容器失败
    硬件配置:Atlas 500 Model 3010/3000问题现象:Atlas 500从老架构版本升级到新架构版本后,通过FD下发业务容器失败。关键过程:1.  Atlas 500从2.2.209.020(老架构)升级至20.02.07.010(新架构)版本后,通过FD下发容器、部署业务软件,任务失败;2.  查看对应日志,common.log中有ERROR记录,显示容器挂载目录/tmp验证失败,不在系统白名单内;3.  SSH登录系统后,修改“/opt/middleware/AtlasEdge/software/edge_core/conf/podconfig.json”文件,将容器的挂载目录/tmp添加至白名单,并重启系统生效后,再次通过FD下发容器,任务成功。结论、解决方案及效果结论:新架构版本增加了容器挂载路径的白名单,而老架构版本没有这个限制,从而出现升级版本后,通过FD下发容器失败的问题。解决方案:修改“/opt/middleware/AtlasEdge/software/edge_core/conf/podconfig.json”文件,将容器的挂载路径添加到白名单内,再通过FD下发容器,任务成功。
  • [技术干货] K8S基础
    ## 一、K8S是什么?### 1.1 概述K8S全名Kubernetes。因k与s之间有8个字符,故缩写为K8S。K8S是一个可自动实施 Linux 容器管理的可移植、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。## 二、为什么需要K8S?要了解这个问题,需要回顾一下应用程序的部署方式。### 2.1 传统部署传统部署直接在物理服务器上运行应用程序。存在缺陷:+ 无法为服务器中的应用程序定义资源边界,导致资源分配出现问题(一个程序占用大部分资源,其它程序性能下降)。+ 若将应用程序运行在不同物理服务器上,一方面导致资源浪费,另一方面提高成本。### 2.2 虚拟化部署虚拟化技术允许我们在单个的物理服务器上运行多个虚拟机(VM),应用程序在VM之间完全隔离。每个VM是一个完整的计算机,在虚拟化硬件上运行包括自己的操作系统在内的所有组件。VM共享主机硬件资源。但因为VM需要运行硬件虚拟副本和完整的操作系统副本,会占用大量的系统资源。### 2.3 容器部署容器将应用程序软件代码和所需的所有组件打包在一起,使得容器内的用意可以在任何基础架构上一致的运行。+ **隔离性**:容器同样可以虚拟化基础计算机,应用程序可在不同容器间实现进程级隔离。+ **轻量性**:每个容器共享物理服务器的OS内核,二进制文件和库,但具有自己的文件系统、CPU、内存、进程空间等。这样的共享可以大大减少重现操作系统代码的需求。因此容器非常轻量,容量小且启动快。+ **可移植**:容器与基础架构分离,可以实现跨云和OS发行版本进行移植。**K8S**即是在大规模服务器环境中,负责部署和管理容器组,用于解决容器的复制,扩展,健康,启动,负载均衡等问题。只需告诉 Kubernetes 您希望在哪里运行软件,该平台就会负责执行部署和管理容器所需的几乎一切工作。## 三、K8S有哪些组件?我们会在一组用于**运行容器化应用**的节点计算机(Node)的上部署K8S,这一组节点计算机即称为K8S集群。正常运行的K8S集群包含以下组件。### 3.1 集群相关术语+ **控制平面(Control Plane):**控制 Kubernetes 节点的进程的集合。所有任务分配都来自于此。+ **节点(Node):**这些机器负责执行由控制平面分配的请求任务。+ **容器集(Pod):**部署到单个节点上且包含一个或多个容器的容器组。容器集是最小、最简单的 Kubernetes 对象。+ **服务(Service):**一种将运行于一组容器集上的应用开放为网络服务的方法。它将工作定义与容器集分离。+ **卷(Volume):**一个包含数据的目录,可供容器集内的容器访问。Kubernetes 卷与所在的容器集具有相同的生命周期。卷的生命周期要长于容器集内运行的所有容器的生命周期,并且在容器重新启动时会保留相应的数据。+ **命名空间(Namespace):**一个虚拟集群。命名空间允许 Kubernetes 管理同一物理集群中的多个集群(针对多个团队或项目)。### 3.2 控制平面组件控制平面的组件对集群做出全局决策(比如调度),以及检测和响应集群事件。控制平面组件可以在集群中的任何节点上运行。 然而,为了简单起见,设置脚本通常会**在同一个计算机上启动所有控制平面组件, 并且不会在此计算机上运行用户容器**。+ **kube-apiserver**:该组件开放 Kubernetes API。API服务器是K8S控制平面的前端。+ **etcd**:etcd 是兼具一致性和高可用性的键值数据库,可以作为保存 Kubernetes 所有集群数据的后台数据库+ **kube-scheduler**:该组件负责监视新创建的、为指定运行节点(node)的Pods,并选择节点让Pod在上面运行。+ **kube-controller-manager**:运行控制器进程的控制平面组件。多中控制器被编译到一个可执行文件,并在一个进程中进行运行。+ **cloud-controller-manager**:云控制器管理器是指嵌入特定云的控制逻辑的 [控制平面](https://kubernetes.io/zh/docs/reference/glossary/?all=true#term-control-plane)组件。 云控制器管理器使得你可以将你的集群连接到云提供商的 API 之上, 并将与该云平台交互的组件同与你的集群交互的组件分离开来。### 3.3 Node组件节点组件在每个节点上运行,维护运行的Pod并提供Kubernetes 运行环境。+ **Kubelet**:每个节点上运行的代理。它保证容器都运行在Pod中。+ **kube-proxy**:kube-proxy 是集群中每个节点上运行的网络代理, 实现 Kubernetes [服务(Service)](https://kubernetes.io/zh/docs/concepts/services-networking/service/) 概念的一部分。它维护节点上的网络规则。这些网络规则允许从集群内部或外部的网络会话与 Pod 进行网络通信。### 3.4 容器运行时容器运行环境是负责运行容器的软件。支持包括像Docker,iSula以及任何实现k8s CRI的容器运行环境。### 3.5 其它可用插件插件并非严格意义上的必须组件。仅列举以下常用的两种。+ **DNS:**几乎所有 Kubernetes 集群都应该有集群DNS。+ **Web界面**:Dashboard 是 Kubernetes 集群的通用的、基于 Web 的用户界面。
  • [技术干货] 谈谈你对标签语义化得理解【面试题】
    为什么需要标签语义化:回答:第一眼看到一堆div不是我写得代码真不知道在干嘛。标准回答:  1、易维护,易修改  2、无障碍阅读  3、搜索引擎友好,利于 SEO。 4、面向未来的 HTML,浏览器在未来可能提供更丰富的支持。结构化语义语义化标签只是单单提供与语义,本质还是容器,并不会去影响容器内得内容。(还是div只是换了一个马甲而已)<header> 头部标签,包括一些搜索和登录得容器,当然也有一些是将nav也一块包含进来得。<nav> 导航标签,功能和<a>得功能类型,栗子淘宝得各个专区<aside>元素并不仅仅是侧栏,它表示与它周围文本没有密切关系的内容。文章中同样可以使用<aside>元素,来说明文章的附加内容、解释说明某个观点、相关内容链接等等。当<aside>用于侧栏时,其表示整个网页的附加内容。通常的广告区域、搜索、分享链接则位于侧栏。侧栏中的<section>元素规定了一个区域,通常是带有标题的内容。<section>标签适合标记的内容区块:与页面主体并列显示的小内容块。独立性内容,清单、表单等。分组内容,如 CMS 系统中的文章分类区块。比较长文档的一部分,可能仅仅是为了正确规定页面大纲。(这个大概就是<aside>标签写的把)社区是万能divfooter 标准规定<footer>标签仅仅可以包含版权、来源信息、法律限制等等之类的文本或链接信息。 <article>元素表示文档、页面、应用或网站中的独立结构,其意在成为可独立分配的或可复用的结构,如在发布中,它可能是论坛帖子、杂志或新闻文章、博客、用户提交的评论、交互式组件,或者其他独立的内容项目。​​(articlen. 文章,论文;物品;条款,条文;冠词;) 理解:是将一个显示内容细节化,整个为一个模块参考文章:https://rainylog.com/post/ife-note-1/  
  • [技术干货] CSS水平和垂直居中的方法【碎片学习】
    1、绝对定位与负边距(已确定宽度高)原理:绝对定位是由最近的相对定位来确定起点,通过top left确定离父级元素的一般距离当然是相对于左上角顶点。// css部分 #container { position: relative; } #center { position: absolute; width:100px; height:100px; top: 50%; left: 50%; margin: -50px 0 0 -50px; }//html2、绝对定位与margin-auto(确定宽高)原理:通过相对定位父级元素作为标准点,设置 上下左右位置为0 margn为 auto 让浏览器自动计算 .container{ position: relative; height:100px;//必须有个高度 (父容器高度)}.content{ position: rabsolute; top:0px; left:0px; right:0px; bottom:0px; margin: auto; //左右上下都auto}3、绝对定位+ transform: translate (宽高未知)原理:当使用:top: 50%;left: 50%;, 是以左上角为原点,故不处于中心位置 translate(-50%,-50%) 作用是,往上(x轴),左(y轴)移动自身长宽的 50%,以使其居于中心位置。.container{ position: relative; height:100px;//必须有个高度 (父容器高度)}.content{ position: rabsolute; top:50%; left:50%; transform: translate(-50%, -50%);}4、flex 估计是以后的主导布局(应用广泛).container{ display: flex; justify-content: center; align-items: center; height:100px;//必须有个高度 (父容器高度)}
  • [基础知识] MindSpore/optimizer 更少的批量标准化(上)
    更少的批量标准化 optimizer / irpass / less_batch_normalization.h optimizer / irpass / less_batch_normalization.cc本模块降低了批量标准化的要求。先定义类LessBatchNormalization作为实现模块作用的基础。通过不使用参数的方法(函数RemoveBatchNormalizetionNotUseParameters)达到降低批量标准化要求。一、类LessBatchNormalizationusing kStructureTuple = std::tuple<size_t, std::vector<PrimitivePtr>, std::pair<size_t, size_t>>; //using的用法,在C++11中提出了通过using指定别名。所以之后的代码中kStructureTuple 指代的是tuple<size_t, std::vector<PrimitivePtr>, std::pair<size_t, size_t>>class LessBatchNormalization : public AnfVisitor { public: //公有成员 AnfNodePtr operator()(const OptimizerPtr &optimizer, const AnfNodePtr &node) override;//伪函数 void Visit(const CNodePtr &cnode) override; //访问节点//override保留字表示当前函数重写了基类的虚函数。 void Reset(); //重置 void IsRemoveNode(const CNodePtr &cnode, const std::vector<kStructureTuple> &match_pattern); bool MatchStructureNode(const CNodePtr &cnode, const int32_t index, const kStructureTuple &patternTuple); bool MatchGraphStructure(const CNodePtr &cnode, const std::vector<kStructureTuple> &match_pattern); private: //私有成员 std::unordered_set<CNodePtr> remove_node_list_{};//{0}表示初始化为0 std::vector<size_t> total_match_node_{0}; size_t match_node_{0}; //匹配的节点数 size_t match_branch_{0}; //匹配的分支 size_t match_pattern_{0}; //匹配的图//初始化为false bool is_match_{false};};二、类LessBatchNormalization 中的函数1.匹配节点的结构bool LessBatchNormalization::MatchStructureNode(const CNodePtr &cnode, const int32_t index, const kStructureTuple &patternTuple) {// patternTuple为存放图节点的数组if (index < 0) { return false; } const auto &use_pattern = std::get<1>(patternTuple); //get方法得到图节点的第一个元素 int32_t use_index = index % use_pattern.size(); if (!IsPrimitiveCNode(cnode, use_pattern)) {//如果不是最初的节点,返回错误 return false; } return true;}2.匹配图结构bool LessBatchNormalization::MatchGraphStructure(const CNodePtr &cnode, const std::vector<kStructureTuple> &match_pattern) { if ((match_branch_ + 1 >= total_match_node_.size()) || (match_branch_ >= match_pattern.size())) { //如果图的分支图比原图节点数多,返回不匹配 return false; } int32_t index = static_cast<int32_t>(match_node_) - static_cast<int32_t>(total_match_node_[match_branch_]); const auto &pattern = match_pattern[match_branch_]; if (!MatchStructureNode(cnode, index, pattern)) { return false;//不匹配 } match_node_++;//match_node中存放了匹配的节点数 if (match_node_ == total_match_node_.back()) { is_match_ = true; return false;//不匹配 } if (match_node_ == total_match_node_[match_branch_ + 1]) { match_branch_++; return false;//不匹配 } return true;}3.移除节点void LessBatchNormalization::IsRemoveNode(const CNodePtr &cnode, const std::vector<kStructureTuple> &match_pattern) { if (!IsPrimitiveCNode(cnode, prim::kPrimBatchNorm) && !IsPrimitiveCNode(cnode, prim::kPrimTupleGetItem)) { return; } if (match_pattern.empty()) {//empty()作为判断容器是否为空的函数//match_pattern为匹配图的个数,当没有个数为0时,证明节点全被移除,返回。 return; }//at()获取字符串元素 const auto &start_end_pair = std::get<2>(match_pattern.at(match_branch_)); if (match_node_ >= start_end_pair.first && match_node_ <= start_end_pair.second) { remove_node_list_.insert(cnode); //在已删除的节点列表中,加入刚被删除的节点 }}4.伪函数AnfNodePtr LessBatchNormalization::operator()(const OptimizerPtr &optimizer, const AnfNodePtr &node) { const auto &fg = node->func_graph(); //fg为传入参数对应的原图 MS_EXCEPTION_IF_NULL(fg);//排除图是否为空//判断图中是否有kLessBatchNormalizationPassName属性 if (!fg->has_attr(kLessBatchNormalizationPassName)) { return nullptr; } match_pattern_ = 0;//匹配的图的个数 while (match_pattern_ < kNeedMatchPattern.size()) { Reset();//重置 const auto &current_pattern = kNeedMatchPattern.at(match_pattern_); size_t sum_match_node = 0; std::for_each(current_pattern.begin(), current_pattern.end(), [&](const kStructureTuple &t) {//函数for_each遍历vector容器 sum_match_node += std::get<0>(t); total_match_node_.emplace_back(sum_match_node); }); AnfVisitor::Match(prim::kPrimAdd, {IsCNode, IsCNode})(node); if (is_match_) { break; } match_pattern_++; //统计匹配图的个数 } if (!is_match_ || remove_node_list_.empty()) { return nullptr; } auto manager = optimizer->manager(); MS_EXCEPTION_IF_NULL(manager); std::vector<AnfNodePtr> remove_load_list; std::vector<AnfNodePtr> remove_parameter_list; for (auto &iter : remove_node_list_) { // Need to remove batchnorm's parameter input. if (IsPrimitiveCNode(iter, prim::kPrimBatchNorm)) { std::copy_if(iter->inputs().begin() + kBNParametersStartIndex, iter->inputs().end(), std::back_inserter(remove_load_list), [](const AnfNodePtr &node) { return IsPrimitiveCNode(node, prim::kPrimLoad); }); std::transform(//函数transform 将back_inserter()操作应用于指定范围(容器remove_load_list的开始到结束范围内的每一个元素) remove_load_list.begin(), remove_load_list.end(), std::back_inserter(remove_parameter_list), [](const AnfNodePtr &node) { return node->cast<CNodePtr>()->input(kValidResidualStructureIndex); }); }auto input_cnode = iter->input(kValidResidualStructureIndex); manager->Replace(iter, input_cnode); //用输入的节点input_cnode替代原本的iter节点 }//函数RemoveBatchNormalizetionNotUseParameters: 移除掉没有用的节点 RemoveBatchNormalizetionNotUseParameters(manager, remove_parameter_list); return node;}5.访问void LessBatchNormalization::Visit(const CNodePtr &cnode) { if (cnode == nullptr) { //若节点为空,返回 return; } const auto &current_pattern = kNeedMatchPattern.at(match_pattern_); IsRemoveNode(cnode, current_pattern); //从current_pattern)移除cnode节点 if (!MatchGraphStructure(cnode, current_pattern)) { return; } auto search_input = cnode->input(kValidResidualStructureIndex); if (search_input != nullptr && search_input->isa<CNode>()) { this->Visit(search_input->cast<CNodePtr>()); //继续访问 } return;}6.重置void LessBatchNormalization::Reset() {//把类LessBatchNormalization中所有的变量重置 remove_node_list_.clear(); total_match_node_.clear(); total_match_node_.emplace_back(0); match_node_ = 0; match_branch_ = 0; is_match_ = false;}
  • [基础知识] mindspore\lite\src\weight_decoder.cc注释1
    ** "mindspore\lite\src\weight_decoder.cc注释1 **=======================================```python/** * 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 //数学计算包#include //String的操作集#include //内存管理工具包#include "src/weight_decoder.h"#include "src/huffman_decode.h"namespace mindspore::lite {//指定命名空间 //字符串转位向量函数std::vector StringToBitVector(const std::string &str) { //建立一个相应大小的容器 std::vector vec(str.size() * 8); size_t index = 0; //对str进行遍历,转化为BitVector,并存储进vec for (auto ch : str) { for (size_t shift = 8; shift > 0; shift--) { vec = (ch >> (shift - 1)) & 0x1; } } return vec;}//对索引进行解压STATUS IndexingDecompress(const schema::Tensor &src_tensor, Tensor *dst_tensor) { MS_LOG(DEBUG) Get(0)->numBits();//计算参数位数 std::string str(reinterpret_cast(src_tensor.data()->data()), src_tensor.data()->size());//对tensor进行转换类型 auto bit_vec = StringToBitVector(str);//将str转换为向量容器 size_t index = 0; // 解析唯一值cnt size_t unique_value_cnt = 0; for (int i = 0; i < bit_num; i++) {//将相应位数存进cnt bool bit = bit_vec; unique_value_cnt |= bit
  • [基础知识] lite\tools\benchmark\benchmark.cc"注释10
    ** "\lite\tools\benchmark\benchmark.cc"注释10**=================================```pythonint Benchmark::PrintResult(const std::vector &title, const std::map &result) { std::vector columnLenMax(5);//按照列最大长度创建容器 std::vector rows;//建立rows容器 for (auto &iter : result) {//对result进行迭代 char stringBuf[5][100] = {}; std::vector columns; size_t len = 0; len = iter.first.size();//获取迭代对象的size if (len > columnLenMax.at(0)) {//判断空间是否足够,不够就加 columnLenMax.at(0) = len + 4; } columns.push_back(iter.first);//添加第一个迭代对象进columns len = snprintf(stringBuf[1], sizeof(stringBuf[1]), "%f", iter.second.second / static_cast(flags_->loop_count_));//格式化数据长度并存储进stringBuf[1] if (len > columnLenMax.at(1)) {//调整存储大小 columnLenMax.at(1) = len + 4; } columns.emplace_back(stringBuf[1]);//存储进columns len = snprintf(stringBuf[2], sizeof(stringBuf[2]), "%f", iter.second.second / op_cost_total_);//格式化cottatal存进stringBuf[2] if (len > columnLenMax.at(2)) {//调整存储大小 columnLenMax.at(2) = len + 4; } columns.emplace_back(stringBuf[2]);//添加进columns len = snprintf(stringBuf[3], sizeof(stringBuf[3]), "%d", iter.second.first);//格式化iter.second.first if (len > columnLenMax.at(3)) {//调整存储大小 columnLenMax.at(3) = len + 4; } columns.emplace_back(stringBuf[3]); len = snprintf(stringBuf[4], sizeof(stringBuf[4]), "%f", iter.second.second); if (len > columnLenMax.at(4)) { columnLenMax.at(4) = len + 4; } columns.emplace_back(stringBuf[4]); rows.push_back(columns);//将columns存进rows中 }//分割线 printf("-------------------------------------------------------------------------\n"); for (int i = 0; i < 5; i++) { auto printBuf = title;//依次输出Buf if (printBuf.size() > columnLenMax.at(i)) { columnLenMax.at(i) = printBuf.size(); } printBuf.resize(columnLenMax.at(i), ' ');//调整大小 printf("%s\t", printBuf.c_str()); } printf("\n"); for (auto &row : rows) {//对rows进行遍历 for (int j = 0; j < 5; j++) { auto printBuf = row[j]; printBuf.resize(columnLenMax.at(j), ' ');//依次输出并调整到合适的大小 printf("%s\t", printBuf.c_str()); } printf("\n"); } return RET_OK;}//根据不同的处理器打印相应的性能结果#ifdef ENABLE_ARM64int Benchmark::PrintPerfResult(const std::vector &title, const std::map &result) { std::vector columnLenMax(5);//创建容器用来容纳结信息 std::vector rows; for (auto &iter : result) {//对result进行迭代 char stringBuf[5][100] = {}; std::vector columns; size_t len = 0; len = iter.first.size();//初始化大小为迭代的第一个对象大小 if (len > columnLenMax.at(0)) {//调整大小 columnLenMax.at(0) = len + 4; } columns.push_back(iter.first);//储存进columns中 float tmp = float_t(flags_->num_threads_) * iter.second.second.value[0] / float_t(flags_->loop_count_) / 1000.0f;//计算时间 len = snprintf(stringBuf[1], sizeof(stringBuf[1]), "%.2f", tmp);//转换类型并存进stringBuf if (len > columnLenMax.at(1)) { columnLenMax.at(1) = len + 4; } columns.emplace_back(stringBuf[1]); len = snprintf(stringBuf[2], sizeof(stringBuf[2]), "%f", iter.second.second.value[0] / op_cost_total_);//储存Value if (len > columnLenMax.at(2)) { columnLenMax.at(2) = len + 4; } columns.emplace_back(stringBuf[2]); tmp = float_t(flags_->num_threads_) * iter.second.second.value[1] / float_t(flags_->loop_count_) / 1000.0f; len = snprintf(stringBuf[3], sizeof(stringBuf[3]), "%.2f", tmp); if (len > columnLenMax.at(3)) { columnLenMax.at(3) = len + 4; } columns.emplace_back(stringBuf[3]); len = snprintf(stringBuf[4], sizeof(stringBuf[4]), "%f", iter.second.second.value[1] / op_cost2_total_); if (len > columnLenMax.at(4)) { columnLenMax.at(4) = len + 4; } columns.emplace_back(stringBuf[4]); rows.push_back(columns);//将columns存进rows } printf("-------------------------------------------------------------------------\n"); for (int i = 0; i < 5; i++) {//遍历并输出相应的数据 auto printBuf = title; if (printBuf.size() > columnLenMax.at(i)) { columnLenMax.at(i) = printBuf.size(); } printBuf.resize(columnLenMax.at(i), ' '); printf("%s\t", printBuf.c_str()); } printf("\n"); for (auto &row : rows) { for (int j = 0; j < 5; j++) { auto printBuf = row[j]; printBuf.resize(columnLenMax.at(j), ' '); printf("%s\t", printBuf.c_str()); } printf("\n"); } return RET_OK;}```
  • [数据加载及处理] 数据处理中的迭代器
    ```C++// 导入自定义头文件#include "minddata/dataset/include/dataset/iterator.h"#include "minddata/dataset/engine/consumers/pull_based_tree_consumer.h"#include "minddata/dataset/engine/consumers/tree_consumer.h"#include "minddata/dataset/engine/runtime_context.h"#include "minddata/dataset/include/dataset/datasets.h"// 双重命名空间namespace mindspore {namespace dataset {// 迭代器 (iterator) 是 C++ 程序中常用的一种设计模式 // 它最重要的作用是为访问容器提供了统一的接口Iterator::Iterator() : consumer_(nullptr) {}Iterator::~Iterator() { Stop(); }// 从数据管道中获取下一行Status Iterator::GetNextRowCharIF(MSTensorMapChar *row) { // 清理数据行 row->clear(); std::unordered_map md_map; Status rc = consumer_->GetNextAsMap(&md_map); if (rc.IsError()) { // 无法获取下一行 MS_LOG(ERROR) insert(std::make_pair(col_name, mindspore::MSTensor(std::make_shared(de_tensor.second)))); } return Status::OK();}// 从数据管道中获取下一行Status Iterator::GetNextRow(MSTensorVec *row) { // 清理数据行 row->clear(); // 创建一个数据集张量行并获取。 然后我们将输出转换为MSTensor std::vector md_row; Status rc = consumer_->GetNextAsVector(&md_row); if (rc.IsError()) { // 清理数据 row->clear(); return rc; } std::transform(md_row.begin(), md_row.end(), std::back_inserter(*row), [](auto t) { return mindspore::MSTensor(std::make_shared(t)); }); return Status::OK();}// 关闭数据管道void Iterator::Stop() { if (runtime_context_ != nullptr) { Status rc = runtime_context_->Terminate(); if (rc.IsError()) { // 输出错误 MS_LOG(ERROR) Init()); // auto可以在声明变量时根据变量初始值的类型自动为此变量选择匹配的类型 auto consumer = std::make_unique(num_epochs); consumer_ = consumer.get(); //判断consumer->Init(ds->IRNode()是否正确 RETURN_IF_NOT_OK(consumer->Init(ds->IRNode())); runtime_context_->AssignConsumer(std::move(consumer)); return Status::OK();}PullIterator::PullIterator() : pull_consumer_(nullptr) {}// 从数据管道中获取下一行.Status PullIterator::GetRows(int32_t num_rows, std::vector *const row) { for (int i = 0; i < num_rows; i++) { std::vector md_row; Status rc = pull_consumer_->GetNextAsVector(&md_row); if (rc.IsError()) { row->clear(); // 错误:无法获取下一行 MS_LOG(ERROR) push_back(ms_row); } return Status::OK();}Status PullIterator::GetNextRow(MSTensorVec *const row) { CHECK_FAIL_RETURN_UNEXPECTED(pull_consumer_ != nullptr, "Consumer is nullptr."); std::vector md_row; Status rc = pull_consumer_->GetNextAsVector(&md_row); if (rc.IsError()) { row->clear(); // 错误:无法获取下一行 MS_LOG(ERROR) push_back(mindspore::MSTensor(std::make_shared(de_tensor))); } return Status::OK();}// 用于构建和启动执行树的函数。 此功能启动了不同类型的消费者// 对于树,之所以会这样,是因为 PullBasedIterator 不需要// 为每个操作实例化线程。 因此,对消费者的调用将绕过执行树Status PullIterator::BuildAndLaunchTree(std::shared_ptr ds) { if (pull_consumer_ == nullptr) pull_consumer_ = std::make_unique(); RETURN_IF_NOT_OK(pull_consumer_->Init(std::move(ds->IRNode()))); return Status::OK();}Iterator::_Iterator::_Iterator(Iterator *lt) : lt_{lt}, cur_row_{nullptr} { if (lt_) { cur_row_ = new MSTensorMap(); Status rc = lt_->GetNextRow(cur_row_); if (rc.IsError()) { MS_LOG(ERROR)
  • [基础知识] 个人理解:pipeline/pynative/pynative_execute_ge.cc节选①
    pynative_execute_ge.cc部分解析//导入自定义对应的的头文件#include "pipeline/pynative/pynative_execute_ge.h"#include <typeinfo> //运行时类型信息工具/* Map是C++STL中众多的Container(容器)之一,与python的字典略类似Map作为一个关联容器,将key与value相互关联,其中key为关键字,是不可更改的而value是key值的相对应值。Map所提供的一对一的数据映射关系,在很多时候可以提供编程的极大便利。Map内部通过自建红黑树(一种非严格意义上的平衡二叉树)实现,可以对数据自动排序,因而在map内部的所有数据是有序存放的。Map具有的一大特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响*/#include <map> /*std::set 是关联容器,含有 Key 类型对象的已排序集。用比较函数 Compare 进行排序。搜索、移除和插入拥有对数复杂度。 set 通常以红黑树实现。*/#include <set>/*unordered_set是一种无序集合,既然跟底层实现基于hashtable那么它一定拥有快速的查找和删除,添加的优点.基于hashtable当然就失去了基于rb_tree的自动排序功能unordered_set无序,所以在迭代器的使用上,set的效率会高于unordered_set*/#include <unordered_set>//导入头文件#include "utils/any.h"#include "utils/utils.h"#include "utils/ms_context.h"#include "frontend/operator/ops.h"#include "pipeline/jit/parse/data_converter.h"#include "pipeline/jit/static_analysis/prim.h"#include "backend/session/session_factory.h"#include "pybind_api/ir/tensor_py.h"#include "transform/graph_ir/op_declare/array_ops_declare.h"//定义常量字符数组SINGLE_OP_GRAPH[]const char SINGLE_OP_GRAPH[] = "single_op_graph";//自定义的命名空间mindspore的定义using mindspore::tensor::TensorPy;//双重的命名空间namespace mindspore {namespace pynative {//定义:using MeTensor = mindspore::tensor::Tensor;using MeTensorPtr = mindspore::tensor::TensorPtr;using GeOperator = ge::Operator;using GeOperatorPtr = std::shared_ptr<GeOperator>;using transform::GraphRunner;using transform::GraphRunnerOptions;using transform::OperatorPtr;static std::shared_ptr<session::SessionBasic> session = nullptr;inline ValuePtr PyAttrValue(const py::object &obj) { ValuePtr converted_ret = nullptr;//定义converted_ret类型ValuePtr的值为nullptr //定义布尔类型converted bool converted = parse::ConvertData(obj, &converted_ret); if (!converted) { // 如果converted不为空,类型属性转换错误 MS_LOG(EXCEPTION) << "Attribute convert error with type:" << std::string(py::str(obj)); } return converted_ret; //返回converted传入的参数converted_ret}MeTensorPtr ConvertPyObjToTensor(const py::object &obj) { MeTensorPtr me_tensor_ptr = nullptr;// 定义me_tensor_ptr类型MeTensorPtr的值为nullptr //通过if-else语句,对me_tensor_ptr进行赋值 //在if语句里面,如果没有什么赋值语句,就是判断里面内容是否为空。不为空,true,进入循环 if (py::isinstance<MeTensor>(obj)) { me_tensor_ptr = py::cast<MeTensorPtr>(obj); } else if (py::isinstance<py::tuple>(obj)) { me_tensor_ptr = TensorPy::MakeTensor(py::array(py::cast<py::tuple>(obj)), nullptr); } else if (py::isinstance<py::float_>(obj)) { me_tensor_ptr = TensorPy::MakeTensor(py::array(py::cast<py::float_>(obj)), nullptr); } else if (py::isinstance<py::int_>(obj)) { me_tensor_ptr = TensorPy::MakeTensor(py::array(py::cast<py::int_>(obj)), nullptr); } else if (py::isinstance<py::list>(obj)) { me_tensor_ptr = TensorPy::MakeTensor(py::array(py::cast<py::list>(obj)), nullptr); } else if (py::isinstance<py::array>(obj)) { me_tensor_ptr = TensorPy::MakeTensor(py::cast<py::array>(obj), nullptr); } else { //运行操作输入类型无效! MS_LOG(EXCEPTION) << "Run op inputs type is invalid!"; } return me_tensor_ptr; // 返回修改后的me_tensor_ptr}bool BuildSingleOpGraph(const OpExecInfoPtr &op_exec_info, const std::vector<GeTensorPtr> &inputs, const std::unordered_map<std::string, ValuePtr> &attrs, const GeGraphPtr &graph) { MS_EXCEPTION_IF_NULL(op_exec_info); std::string op_name = op_exec_info->op_name; auto op_inputs = op_exec_info->op_inputs; //auto = const std::vector<GeTensorPtr> transform::OpAdapterPtr adapter = transform::DfGraphConvertor::FindAdapter(op_name, true); if (adapter == nullptr) { //找不到适配器 MS_LOG(ERROR) << "Unable to find Adapter for " << ((std::string)py::str(op_name)); return false; } OperatorPtr op = adapter->generate(op_name); MS_EXCEPTION_IF_NULL(op); std::vector<GeOperator> graph_input_nodes; // 设置图形的输入和输出后保存参数节点 // 设置输入 if (!SetInputsForSingleOpGraph(op_exec_info, inputs, op, &graph_input_nodes)) { return false; } // 设置属性 for (auto attr : attrs) { //auto = const std::unordered_map<std::string, ValuePtr> (void)adapter->setAttr(op, attr.first, attr.second); } // 设置默认属性 auto extra_attrs = adapter->GetExtraAttr(); for (auto attr : extra_attrs) { (void)adapter->setAttr(op, attr.first, attr.second); } // 设置输入属性 auto &input_attr_map = adapter->getInputAttrMap(); for (auto &it : input_attr_map) { //it.first 获取的是key,也就是键 //it.second 获取的是value,也就是值 if (op_inputs.size() < it.first) { continue; } auto const_value = PyAttrValue(op_inputs[it.first - 1]); if (const_value->isa<None>()) { continue; } it.second.set_attr(op, const_value); } // 构建输出数据节点 std::vector<GeOperator> graph_outputs{*op}; // 设置图形的输入和输出节点 MS_EXCEPTION_IF_NULL(graph); (void)graph->SetInputs(graph_input_nodes).SetOutputs(graph_outputs); MS_LOG(INFO) << "BuildSingleOpGraph done"; //BuildSingleOpGraph 完成 return true;}
  • [基础知识] shard_utils.cc文件的一些个人见解
    //导入.h头文件#include "minddata/mindrecord/include/common/shard_utils.h" #include "utils/ms_utils.h"#include "./securec.h"//访问自定义的命名空间mindspore的值using mindspore::LogStream;using mindspore::ExceptionType::NoExceptionType;using mindspore::MsLogLevel::DEBUG;using mindspore::MsLogLevel::ERROR;namespace mindspore {//嵌套命名空间mindrecord:namespace mindrecord {//使用字符separator来分割字符串fieldstd::vector<std::string> StringSplit(const std::string &field, char separator) { std::vector<std::string> res;//类函数列表初始化 uint64_t s_pos = 0;//unsigned long long初始化 while (s_pos < field.length()) {//当类函数列表field的长度大于0时,一直循环 /* size_t: 在64位系统中为long long unsigned int,非64位系统中为long unsigned int * 目的是:移植性强 */ //.find_first_of: 搜索字符串,以找到与其参数中指定的任何字符匹配的第一个字符 size_t e_pos = field.find_first_of(separator, s_pos);//从s_pos号位开始,依此寻找field中出现的第一个字符separator,记录当前位置 if (e_pos != std::string::npos) {//如果e_pos不等于size_t的最大值 //将类函数列表field从下标为s_pos开始复制,一直复制到e_pos,复制个数为:e_pos - s_pos,把复制的值传到res的末尾 res.push_back(field.substr(s_pos, e_pos - s_pos)); } else { //如果e_pos等于size_t的最大值 //将类函数列表field从下标为s_pos开始复制,一直复制到末尾,复制个数为:field.length()- s_pos,把复制的值传到res的末尾 res.push_back(field.substr(s_pos, field.length() - s_pos)); break;//跳出while循环 } s_pos = e_pos + 1;//赋值运算 } return res;//循环结束,StringSplit最终返回:类函数列表res;}//判断容器str是否容量大于1,并且里面的值是否都是下划线或者0-9的数,或者大小写字母bool ValidateFieldName(const std::string &str) { std::string::const_iterator it = str.begin();//容器类名为String的常量正向迭代器it初始化为str的开始下标位置 if (it == str.end()) {//如果容器str只有一个值或者没有值,直接返回false return false; } for (; it != str.end(); ++it) {//遍历容器str //如果该值是下划线或者0-9的数,或者大小写字母,则继续遍历 if (*it == '_' || ((*it >= '0') && (*it <= '9')) || ((*it >= 'A') && (*it <= 'Z')) || ((*it >= 'a') && (*it <= 'z'))) { continue; } return false;//否则,返回false } return true;}//std::pair<MSRStatus, std::string> GetFileName(const std::string &path) { //初始化定义: char real_path[PATH_MAX] = {0}; char buf[PATH_MAX] = {0}; if (strncpy_s(buf, PATH_MAX, common::SafeCStr(path), path.length()) != EOK) {//如果指定长度的字符串不等于EOK //输出内容:Securec func [strncpy_s] failed, path:对应路径path MS_LOG(ERROR) << "Securec func [strncpy_s] failed, path: " << path; return {FAILED, ""};//返回{FAILED, ""} } char tmp[PATH_MAX] = {0};#if defined(_WIN32) || defined(_WIN64)//如果在window32或者64操作系统下执行以下语句: //_Fullpath 函数将 dirname 中的相对路径名称扩展为其完全限定路径或绝对路径,并将此名称存储在 tmp 中 if (_fullpath(tmp, dirname(&(buf[0])), PATH_MAX) == nullptr) { //输出内容: Invalid file path, path: :对应路径buf MS_LOG(ERROR) << "Invalid file path, path: " << buf; return {FAILED, ""}; } //_Fullpath 函数将Safe 中的相对路径名称扩展为其完全限定路径或绝对路径,并将此名称存储在 real_path 中 if (_fullpath(real_path, common::SafeCStr(path), PATH_MAX) == nullptr) { //输出: MS_LOG(DEBUG) << "Path: " << common::SafeCStr(path) << "check successfully"; }#else//否则执行以下语句:(结果差不多,只是在不同的操作系统下的执行语句不同) if (realpath(dirname(&(buf[0])), tmp) == nullptr) { MS_LOG(ERROR) << "Invalid file path, path: " << buf; return {FAILED, ""}; } if (realpath(common::SafeCStr(path), real_path) == nullptr) { MS_LOG(DEBUG) << "Path: " << path << "check successfully"; }#endif //初始化: std::string s = real_path; char sep = '/'; size_t i = s.rfind(sep, s.length()); //从s.length()开始,向前查找符合条件的字符串; if (i != std::string::npos) {//如果不相同 if (i + 1 < s.size()) { return {SUCCESS, s.substr(i + 1)}; } } return {SUCCESS, s};}std::pair<MSRStatus, std::string> GetParentDir(const std::string &path) { char real_path[PATH_MAX] = {0}; char buf[PATH_MAX] = {0}; if (strncpy_s(buf, PATH_MAX, common::SafeCStr(path), path.length()) != EOK) {//如果指定的字符串不为EOK //输出:Securec func [strncpy_s] failed, path: 指定位置path MS_LOG(ERROR) << "Securec func [strncpy_s] failed, path: " << path; return {FAILED, ""}; } char tmp[PATH_MAX] = {0};#if defined(_WIN32) || defined(_WIN64)//如果在window32或者64操作系统下执行以下语句: if (_fullpath(tmp, dirname(&(buf[0])), PATH_MAX) == nullptr) { //输出:Invalid file path, path:指定位置buf MS_LOG(ERROR) << "Invalid file path, path: " << buf; return {FAILED, ""}; } if (_fullpath(real_path, common::SafeCStr(path), PATH_MAX) == nullptr) { MS_LOG(DEBUG) << "Path: " << common::SafeCStr(path) << "check successfully"; }#else//否则执行以下语句:(结果差不多,只是在不同的操作系统下的执行语句不同) if (realpath(dirname(&(buf[0])), tmp) == nullptr) { MS_LOG(ERROR) << "Invalid file path, path: " << buf; return {FAILED, ""}; } if (realpath(common::SafeCStr(path), real_path) == nullptr) { MS_LOG(DEBUG) << "Path: " << path << "check successfully"; }#endif//与106-119行代码类似,不做重复解释 //初始化s为绝对路径 std::string s = real_path; if (s.rfind('/') + 1 <= s.size()) { return {SUCCESS, s.substr(0, s.rfind('/') + 1)}; } return {SUCCESS, "/"};}bool CheckIsValidUtf8(const std::string &str) { //初始化n与ix int n = 0; int ix = str.length(); for (int i = 0; i < ix; ++i) {//遍历str //static_cast<unsigned char>是c++的类型转换操作符 uint8_t c = static_cast<unsigned char>(str[i]);//将str强制类型转化为unsigned char类型 //将c与十六进制数比较,对n的赋值 //下面均为十六进制数,只解释第一个,其他合理推导即可 if (c <= 0x7f) {//0x7f表示的是一个十六进制数7f,换算成十进制数是127 n = 0; } else if ((c & 0xE0) == 0xC0) { n = 1; } else if (c == 0xed && i < (ix - 1) && (static_cast<unsigned char>(str[i + 1]) & 0xa0) == 0xa0) { return false; } else if ((c & 0xF0) == 0xE0) { n = 2; } else if ((c & 0xF8) == 0xF0) { n = 3; } else { return false; } for (int j = 0; j < n && i < ix; ++j) {//嵌套循环 //如果++i == ix或者转换成unsigned char类型的str) & 0xC0不等于128,则返回false if ((++i == ix) || ((static_cast<unsigned char>(str[i]) & 0xC0) != 0x80)) { return false; } } } return true;}bool IsLegalFile(const std::string &path) { //struct stat这个结构体是用来描述一个linux系统文件系统中的文件属性的结构 struct stat s; if (stat(common::SafeCStr(path), &s) == 0) { if (s.st_mode & S_IFDIR) {//如果s.st_mode & S_IFDIR不为0,返回false return false; } return true; } return false;}std::pair<MSRStatus, uint64_t> GetDiskSize(const std::string &str_dir, const DiskSizeType &disk_type) {#if defined(_WIN32) || defined(_WIN64) || defined(__APPLE__) //如果在win32或者64,或者苹果操作系统下,运行以下代码 return {SUCCESS, 100};#else uint64_t ll_count = 0;//unsigned long long ll_count 初始化 struct statfs disk_info; if (statfs(common::SafeCStr(str_dir), &disk_info) == -1) {//如果不存在,输出Get disk size error MS_LOG(ERROR) << "Get disk size error"; return {FAILED, 0}; } switch (disk_type) { //switch语句: case kTotalSize://如果disk_type为:kTotalSize ll_count = disk_info.f_bsize * disk_info.f_blocks;//赋值 ll_count = ll_count >> 20;//左移20,也就是乘以2的20次方 break; case kFreeSize://如果disk_type为:kFreeSize ll_count = disk_info.f_bsize * disk_info.f_bavail;//赋值 ll_count = ll_count >> 20;//左移运算符 break; default://如果disk_type为俩者都不为: ll_count = 0; break; } return {SUCCESS, ll_count};#endif}uint32_t GetMaxThreadNum() { // 定义线程的数量 uint32_t thread_num = std::thread::hardware_concurrency(); if (thread_num == 0) {//如果线程数量为0 thread_num = kMaxConsumerCount;//线程数量为:kMaxConsumerCount } return thread_num;//返回线程数量}} // namespace mindrecord} // namespace mindspore
  • [基础知识] minddata/mindrecord/include/common/shard_utils.h注释
    一、原码链接:https://forgeplus.trustie.net/projects/Huawei_Technology/mindspore/tree/master/mindspore/ccsrc/minddata/mindrecord/include/common/shard_utils.h二、代码标注://在头文件中使用#ifdef和#ifndef是非常重要的,可以防止双重定义的错误#ifndef MINDSPORE_CCSRC_MINDDATA_MINDRECORD_INCLUDE_COMMON_SHARD_UTILS_H_ //预编译命令#define MINDSPORE_CCSRC_MINDDATA_MINDRECORD_INCLUDE_COMMON_SHARD_UTILS_H_//用的多的函数放在.h头文件中定义声明。//尽量不要在.h头文件中设置全局变量,或者静态全局变量。//导入系统文件或者是自定义文件:#include <libgen.h>#include <limits.h>#include <stdlib.h>#include <sys/stat.h>//非活动预处理器块#if !defined(_WIN32) && !defined(_WIN64) && !defined(__APPLE__)//如果不是在win32或者win64或者apple平台,就追加运行下面俩个代码#include <sys/statfs.h>#include <sys/wait.h>#endif#include <unistd.h>#include <cassert>#include <cmath>#include <cstdio>#include <ctime>#include <future>#include <iostream>#include <map>#include <random>#include <set>#include <sstream>#include <string>#include <thread>#include <unordered_map>#include <utility>#include <vector>#include "minddata/mindrecord/include/shard_error.h"#include "nlohmann/json.hpp"//引用头文件①#include "./sqlite3.h"#include "utils/log_adapter.h"#ifdef DEBUG#define MS_ASSERT(f) assert(f) //如果标识符DEBUG已被#define命令定义,则编译该语句#else#define MS_ASSERT(f) ((void)0) //如果标识符DEBUG没有被#define命令定义,则编译该语句#endifnamespace mindspore {namespace mindrecord {//nlohmann/json是一个用于解析json的开源c++库/*引用头文件具体规范:#include "nlohmann/json.hpp"using json = nlohmann::json;*///引用头文件②using json = nlohmann::json;//定义五个整型常量:const int kInt0 = 0;const int kInt1 = 1;const int kInt2 = 2;const int kInt3 = 3;const int kUnsignedInt4 = 4;//声明枚举:enum LabelCategory { kSchemaLabel, kStatisticsLabel, kIndexLabel };const char kVersion[] = "3.0";//容器kSupportedVersion包含俩个值,分别是2.0和kVersion(3.0):const std::vector<std::string> kSupportedVersion = {"2.0", kVersion};//定义枚举:enum ShardType { kNLP = 0, kCV = 1,};enum TaskType { kCommonTask = 0, kPaddedTask = 1,};enum SamplerType { kCustomTopNSampler, kCustomTopPercentSampler, kSubsetRandomSampler, kPKSampler, kSubsetSampler };enum ShuffleType { kShuffleCategory, kShuffleSample };//定义双精度kEpsilonconst double kEpsilon = 1e-7;const int kThreadNumber = 14;// uint64_t别名:unsigned long int (无符号长整型)//1<<24表示将1左移24位const uint64_t kDefaultHeaderSize = 1 << 24; // 16MBconst uint64_t kDefaultPageSize = 1 << 25; // 32MB// HeaderSize范围是: [16KB, 128MB]const int kMinHeaderSize = 1 << 14; // 16KBconst int kMaxHeaderSize = 1 << 27; // 128MB// PageSize范围是: [32KB, 256MB]const int kMinPageSize = 1 << 15; // 32KBconst int kMaxPageSize = 1 << 28; // 256MBconst uint64_t kInt64Len = 8;//无符号长整型常量kInt64Lenconst uint64_t kMinFileSize = kInt64Len;// 最小的Filesize = 8;const int kMinShardCount = 1;const int kMaxShardCount = 1000; // writeconst int kMaxFileCount = 4096; // readconst int kMinConsumerCount = 1;const int kMaxConsumerCount = 128;const int kMaxSchemaCount = 1;const int kMaxThreadCount = 32;const int kMaxFieldCount = 100;const int kMinFreeDiskSize = 10;// 最小的FreeDiskSize为10Mconst json kDummyId = R"({"id": 0})"_json;//对kDummyId内容进行构造//Unordered_map容器通过键访问单个元素的速度比map容器快,尽管它们通常在通过元素子集进行范围迭代时效率较低//依次对map中kDbJsonMap的枚举类型的字符串做初始化//将下面模式中的类型转换为sqlite3中的类型(NULL, INTEGER, REAL, TEXT, BLOB)const std::unordered_map<std::string, std::string> kDbJsonMap = { {"string", "TEXT"}, {"date", "DATE"}, {"date-time", "DATETIME"}, {"null", "NULL"}, {"integer", "INTEGER"}, {"boolean", "BOOLEAN"}, {"array", "BLOB"}, {"number", "NUMERIC"}, {"int32", "INTEGER"}, {"int64", "INTEGER"}, {"float32", "NUMERIC"}, {"float64", "NUMERIC"}, {"bytes", "BLOB"}};const char kPoint = '.';//set里面的元素是有序的且唯一的,只要你往set里添加元素,它就会自动排序//而且,如果你添加的元素set里面本来就存在,那么这次添加操作就不执行const std::set<std::string> kFieldTypeSet = {"bytes", "string", "int32", "int64", "float32", "float64"};//检查模式验证使用的字段类型const std::set<std::string> kScalarFieldTypeSet = {"string", "int32", "int64", "float32", "float64"};//是否可以搜索字段列表const std::set<std::string> kNumberFieldTypeSet = {"int32", "int64", "float32", "float64"};//数域列表//依次对map中kTypesMap的枚举类型的字符串做初始化const std::unordered_map<std::string, std::string> kTypesMap = { {"bool", "int32"}, {"int8", "int32"}, {"uint8", "bytes"}, {"int16", "int32"}, {"uint16", "int32"}, {"int32", "int32"}, {"uint32", "int64"}, {"int64", "int64"}, {"float16", "float32"}, {"float32", "float32"}, {"float64", "float64"}, {"string", "string"}};/*StringSplit使用字符分割字符串fieldseparator分隔用于分割的字符返回vector类型的结果*/std::vector<std::string> StringSplit(const std::string &field, char separator);/*验证字段名是否由'0-9'或'a-z'或'a-z'或'_'或'-'组成目标字符串在str中储存返回是/否*/bool ValidateFieldName(const std::string &str);/*简要获取文件名的路径s储存参数的文件路径返回文件路径*/std::pair<MSRStatus, std::string> GetFileName(const std::string &s);/*简要获取父目录patn储存参数路径文件路径返回父路径*/std::pair<MSRStatus, std::string> GetParentDir(const std::string &path);bool CheckIsValidUtf8(const std::string &str);//判断一个路径是否是合法文件,返回是/否bool IsLegalFile(const std::string &path);//定义枚举:enum DiskSizeType { kTotalSize = 0, kFreeSize };/*获取磁盘的空闲空间参数str_dir储存文件路径参数disk_type储存枚举类型DiskSizeType:kTotalSize / kFreeSize返回大小(以mb为单位)*/std::pair<MSRStatus, uint64_t> GetDiskSize(const std::string &str_dir, const DiskSizeType &disk_type);uint32_t GetMaxThreadNum();//得到最大的硬件并发性,返回最大并发const uint32_t LAZY_LOAD_THRESHOLD = 5000000;//启用延迟加载的最大样本数无符号整型常量五百万} // namespace mindrecord} // namespace mindspore#endif // MINDSPORE_CCSRC_MINDDATA_MINDRECORD_INCLUDE_COMMON_SHARD_UTILS_H_
  • [数据加载及处理] 数据处理中的设备矩阵
    ```C++// using声明using RankList = std::vector;using Shape = std::vector;// 定义一个DeviceMatrix类,里面的方法都没有方法体,具体的实例化在后面的.cc文件中class DeviceMatrix { public: DeviceMatrix(int64_t rank, RankList devices, Shape dev_shape); DeviceMatrix() = default; ~DeviceMatrix() = default; std::vector group_list() const { return group_list_; } Status CreateGroupList(); Status GetDevicesByTensorMap(const Shape &tensor_map, RankList *rank_list); Status GetDevicesAlongDim(const uint64_t &dim, RankList *devices); private: int64_t rank_ = -1; RankList dev_list_; // 从低暗到高暗,例如:[D0 D1 D2 D3] Shape dev_shape_; std::vector group_list_;};std::string ShapeToString(const Shape &shape);std::string ListToString(const RankList &list);.cc文件(详细)// 导入与该.cc完全对应的头文件#include "frontend/parallel/device_matrix.h"//导入系统自带的标准库#include // 算法库// 此头文件是类型支持库的一部分,提供定宽整数类型和部分 C 数值极限接口#include // 此头文件是函数对象库的一部分并提供标准散列函数#include // 数值库包含常用数学函数及类型,以及优化的数值数组及对随机数生成的支持#include // 通用工具库的一部分#include // 容器库的一部分#include // 导入其他的自定义头文件#include "frontend/parallel/ops_info/operator_info.h"#include "frontend/parallel/status.h"#include "utils/log_adapter.h"// 双重命名空间namespace mindspore {namespace parallel {// 对头文件的DeviceMatrix类里面的方法实例化DeviceMatrix::DeviceMatrix(int64_t rank, RankList dev_list, Shape dev_shape) : rank_(rank), dev_list_(std::move(dev_list)), dev_shape_(std::move(dev_shape)) { // any_of是算法库新增的判断算法 // any_of:检查区间[first, last)中是否至少有一个元素都满足一元判断式p,只要有一个元素满足条件就返回true,否则返回false if (!std::any_of(dev_list_.begin(), dev_list_.end(), [rank](int64_t a) { return a == rank; })) { // rank不在现阶段! MS_LOG(EXCEPTION)
总条数:289 到第
上滑加载中