• [技术干货] WeAutomate Assistant 助手2.17版本以及后续版本点击启动无反应,检查日志报错提示SQLError
    问题现象:助手安装路径下logs文件夹中agent.log日志文件,报错提示如下:可能原因:    助手异常退出,进程没有结束,重新启动新的进程,导致连接数据库冲突解决办法:1. 删除助手安装目录\agent目录中的db文件夹2. 打开任务管理器,结束WeAutomate Assistant助手全部进程与javaw.exe进程(也可使用cmd命令结束进程)3. 重新启动助手注:如以上操作仍无法解决问题,可通过华为云论坛进行提问
  • [低码] appcube支持异步或者多线程吗?
    我在脚本里面需要循环调用第三方接口,需要异步操作或者多线程操作,不然前端超时,如果支持,应该用什么函数或者引入哪个库,如果不支持,有没有什么解决方案
  • [交流吐槽] 第三章笔记
    # 第三章笔记 ## 任务管理 ### 任务管理基本概念 ![屏幕截图 2022-07-19 095909.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310313063857726.png) ### 任务状态 就绪(Ready):该任务在就绪列表中,只等待CPU 运行(Running):该任务正在执行 阻塞(Blocked):该任务不在就绪列表中,包含任务被挂起、被延时、任务正在等待信号量、读写队列或者等待读写事件等 退出态(Dead):该任务运行结束,等待系统回收资源 ### 任务相关概念 ![屏幕截图 2022-07-19 100355.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310340704306143.png) ### 任务调度机制 ![屏幕截图 2022-07-19 100910.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310364389785084.png) ### 实现任务管理 ![屏幕截图 2022-07-19 101034.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310387024922301.png) 删除某个任务:osThreadTerminate(os Threadld_t thread_id); 任务挂起:osThreadSuspend(os Threadld_t thread_id); 任务恢复:os ThreadResume(osThreadld_t thread_id); 创建任务接口: ![屏幕截图 2022-07-19 101327.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310405496618669.png) func:任务函数 argument:作为启动函数传递给任务函数的指针 attr:任务入口函数的参数列表 返回值:任务ID ## 软件定时器 ### 基本概念 硬件定时器受硬件的限制,不满足用户的需求,提供更多定时器,LiteOS操作系统提供软件定时器 ![屏幕截图 2022-07-19 103853.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310424438908216.png) ![屏幕截图 2022-07-19 104724.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310434097328091.png) ### 运作机制 ![屏幕截图 2022-07-19 104834.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310449852805341.png) ### 实现软件定时器创建 ![屏幕截图 2022-07-19 104933.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310487352485631.png) ![屏幕截图 2022-07-19 105216.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310495770350755.png) 可以创建单次或者是周期性定时器,如果是单次定时器,想要停止定时器,需要在运行期间停止,删除定时器任意时期都可以 ## 信号量 ### 概念 ![屏幕截图 2022-07-19 112327.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310525821114263.png) ![屏幕截图 2022-07-19 112354.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310539672775982.png) ### 运作机制 运作原理 ![屏幕截图 2022-07-19 162509.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310558273789193.png) ![屏幕截图 2022-07-19 162541.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310563658579032.png) 信号量运作示意图 ![屏幕截图 2022-07-19 162702.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310586044708842.png) 线程1、2、3、4都获取到了公共资源的任务数,这时线程5会被阻塞,当线程1完成并离开之后,线程5可以执行 ### 实现信号量 ![屏幕截图 2022-07-19 163137.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310603011329545.png) ### 信号量释放与信号量计数的关系 每次释放信号量时,信号量计数器的计数值会增加1,每申请一次信号量,计数值会减少一,能不能申请到,取决于计数值是否大于1 ## 事件管理 ### 概念 ![屏幕截图 2022-07-20 165156.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310679571535384.png) ### 运作机制 ![屏幕截图 2022-07-20 165326.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310706177404220.png) 事件2发生的时候,任务a都会被唤醒 b任务是在事件2和事件5都发生时才会被唤醒 即一个事件可以对应多个任务,或者是多个事件来对应一个任务 ### 实现事件功能 ![屏幕截图 2022-07-20 165629.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310725781856096.png) ## 互斥锁 ### 概念 ![JPCI82BC5OK{S~G0J}403AL.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310743772472451.png) ### 运作机制 ![屏幕截图 2022-07-20 170906.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310763568941772.png) 线程1,2都要访问公共资源,线程1访问之后,公共资源会上锁,线程2被挂起,也就是第一个公共资源只能被独占式访问,当线程1释放互斥锁时,线程2才能访问,确保同一时刻只有一个线程访问公共资源,以保证公共资源的完整性。 ### 实现互斥锁 ![屏幕截图 2022-07-20 170748.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310792847734015.png) ## 消息队列 ### 概念 ![屏幕截图 2022-07-20 172245.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310809118936670.png) ### 特性 ![屏幕截图 2022-07-20 172555.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310826209936903.png) ### 运作机制 ![屏幕截图 2022-07-20 172632.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310845568424408.png) 消息头head和消息尾tail表示单型队列,head表示队列中被占用消息的起始位置,tail表示队列中被空闲消息的起始位置 ![屏幕截图 2022-07-20 172849.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310861256616220.png) ### 实现消息队列 ![屏幕截图 2022-07-20 173025.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/20/1658310878281658012.png) ## 心得 代码不难理解,但是需要按照教学视频的步骤的学习
  • [技术干货] 第八讲:鲲鹏性能分析工具基础知识
    课程链接:https://www.hikunpeng.com/zh/developer/live/detail/1542787444268032001 今天的主要内容是讲解鲲鹏 DevKit 性能分析工具的一个主要介绍。下面分为三章。第一个的话是工具的简介,功能性能分析工具的话主要分为四大块,它主要是一个工具集,它包括调助手、系统性能分析工具、Java性能分析工具,还有系统诊断工具。然后现在市面上它其实有很多的这种分析工具,比如说像客服、space、at state 等等。那么鹏鹏为什么还要做这么一个系统性的分析工具呢?主要有以下几点,因为鲲鹏性能,鲲鹏的话性能分析工具的话目标它是提供业界工具以外的能力,而不是说仅仅包含比如说比业界的这些工具还要的能力的。当前的一个能力主要是提供他之外,比如说像处理器,他我们的工具提供了软硬件的一个结合能力,比如说像采集和分析硬件的性能指标,还有硬件性能。在软件层的一个表现,就是让用户能够在整体的对一个系统进行的运行情况有一个比较好的了解。表格的话是系统化的分析,建立各层指标之间的一个关联,然后并以用户的视角去呈现这些指标和关系,就用户可以更好地发现一些问题。然后第三个的话主要是像业界的一些指标,它主要是采集和获取一些数据。然后鲲鹏的话这边会提供一些那个性能优化经验,这些经验的话主要是在共同处理器上之前的一些专家提供在实际操作中或者说他的一些经验中给出了一些优化思路和建议,可以快速的帮助用户去定位修复一些问题。第四个的话就是公共性能分析工具的话,它提供了一个分析过程的一个管理。比如说在分析前后,它可以进行两次的一个优化结果的一个对比,可以更好地去发现你性能的点在哪里。第二个,比如说还有像优化结果的标记,优化过程的一个记录,可以让你在后续的优化中有一个更好的经验。工具的功能的话,功能性能分析工具就刚刚说的主要分为四大功能。然后每个功能里面它其实还有一些比较小的功能。比如说像系统性能分析工具包括了通用分析,系统部件分析等,然后里面有全景分析、热点函数,这个后续大家在去实践的过程中可以去实际操作一下工具的总的目标。用户的话包含部分,一部分的话是开发人员,第二部分的话是系统管理员或者用人。作为开发人员的话,她对应用腾讯的是这一位了解。那么开发人员的视角,他可以从软件运行的情况入口去分层次的一个分析。比如说可以通过应用程序,它可以放到应用程序的一些比如说像比如这两步电网站等等,然后从这一块,然后他可以更深地去看他们的一些也系统调用,然后再抛到的调用内核函数等等,然后最终还可以去查看。最后是硬件设备导致了一些性能,这是作为点开发人员的一个然后的一个思路。然后作为运维人员的一个输入的话,运维人员他其实或者系统管理员他其实对资源的情况比较敏感,比如说像设备的 CPO 的指标或者系统配置,他是不是非常高。那么他可以从设备进行入口,入口之后还可以进一步的这样看去进行一个分析。 BPO 率, BPO 用率配置都不账号影响,可以看内核数据,但在于系统最终还可以和开发人一起去分析它的一种程序,这是运维的视角,就是。第二个,主要介绍工具软件的一个架构。我们系统分析工具的零架构的话是从目前独立上分成部分,一部分是都有,这部分的话主要是实现的是技能分析的一个数据的分析,还有一个结果的实现。 agent 这部分主要是实现性能分析的采集。为什么稍微能看?因为采集的话它肯定是相对于分析来说,它的获得然后资源会比较少。然后分析和数据呈现的话,它的那个服务器的资源消耗它会比较多。所以说在这种情况下对待熟悉进行分析,但是又没必要去消耗它太中的性能,会影响业务的一个功能的运行。所以说我们就把供应有数据采集的时候看看采集服务器数据,然后它不会消耗太多的资源,然后不会对业务造成太大的影响。然后分析的时候他在其他的没有业务的如期上进行分析,那么可以也不会对业务造成影响,这样的话就可以很好的解决这样的问题。软件部署的话主要分为两部分,一部分是单机部署,第二部分是混合部署。单机部署的话主要是部署在三台服务器,然后 server agent 然后混合部署的话它是在一台节点上部署 server 的 agent agent 然后可以在其他的节点上去部署 agent 进行数据的采集。然后这个时候就可以部署一个这种集群的方式去进行一个分析。右边的话主要是他的硬件和 OS 的一个要求,可以关注一下第三个的话主要讲功能原理。第一部分的话是系统性能分析,就是第一个是全景分析。全景分析主要就是获取整个服务器的一些配置资源,然后 CPO 内存、磁盘、网络等等一些这种资源的运行情况。还有一些这种比如说进程的一些像这边的话比如说像展示服务器系统以及各个子系统的一个 top 结构,比如说像 CPU 那块 CPU 它上面挂了多少的一个网络,估计网络,我看上面挂了多少内存条多少内存的系统,然后他挂了多少块磁盘等着。然后这边的话可以看到这边可以有一些这种比较动态的一些数据,像 CPU 的内存的预警的情况,我想请教的利用率。然后还有针对大数据数据库、分布式存储等等的一些硬件配置和系统配置组件配置进行一个检查分析。如果就是比如说他的配置不是在共同服务器上的一个最优的配置项,那么就会给出一些比较典型的一些硬件配置,还有一些优化建议等等。这样的话用户可以根据这些优化进一进进行一个调整,然后提高提高各种场景的一个运行的性能。然后这边的话主要是结合那个系统性能和系统配置的一些情况,然后检测出系统的一个佣金点,然后给出一些优化建议。然后用户的话可以根据这些优化建议去进行一个修改。全景分析的原理的话,可以看一下主要是基于 use 的性能分析方式获取系统资源。系统资源就包括比如说像 CPU 内存还有网络等等他们的一些比如说像饱和率、饱和度、使用率、饱和度错误等等一些指标,然后从这些指标中去识别这个系统的一个瓶颈。下面主要是这些指标的一个含义。后续大家可以自己去看一下他的一个分析的过程。主要是比如说从开始他选择了一项资源,比如说选择了 CBO 的内存的一个内存的情况,可以看出他是否出现了错误。如果错误了,那肯定要对这个错误进行一个性能分析,看之后得到解决。然后如果没有错误,看他是否已经饱和了。比如说 CQ 他。硬件是否已经站不住了?表第三件。降低一些复合,他们主要是还有一个就是使用率的情况,这个主要是一个分析的过程。下面主要是那个一些指标,这些指标的话在在工具的界面上它不会有一个详细的解释和介绍,大家后续在这时间中可以自己去看一下。这边的话主要是比如说像 CPU 重点,这第一个主要是 CPU 的一个截图像,比如说 face 主要是 BPO 划在内核空间的一个时间,他的话主要是占用率。就是占该指标占用率过高的时候,那意味着就是 L 操作。设备主要是 CPU 处于等待的状况是吧状况还有一直不点赞说如果这个指标较低的话,就可以建议去比如说更换高性能的还有设备或者减少一些还有操作以提高非标的一个进度。内存的话,内存这边的话主要是比如说像内存的一些分配情况分配,然后已分配未影射的等等,去做一个详细的。这系统一定要去进行页面的清理以及释放。如果是比如说做了这个操作,那么就就是说他会造成一定的浪费,就直接的时间上浪费,还可以不需要去做这些操作。像存储 IO 的话,主要就是像比如说一个 IO 请求来了,来了之后,然后他进行一个 IO 队列,然后队列里面的然后进行一个磁盘一个或者说值班的一个数据办不限。然后 L 的那个系统已经做好了,有比如说我想提高更好的,还有的一个硬硬件等等,有一些其他的优化的对。网络的话其实也是一样,比如说像收发包讲完了把那个头发包的大小的一个白框。看这边的话,数据包这两个指标的话表示数据标,数据包已经进入了 rain buffer 的状态,但是由于内存不够,导致了最终被丢弃,所以说这种情况的话就是会有一定的影响。下面的话主要是一些系统配置的一些介入。然后因为在那个服务器上它有很多配置,比如说像网网口网口的那个中队列的一个中断版绑定这边。然后这边的话主要是 ssmeo 的 smmeo 的一个。然后这边是那个大数据的一个发行的策略。这边可以大家去看一下他的实际的一个数据流转的一个这些配置的话,对,就是可以做一个比较优的结果。比如说像大数据的一个发行策略,他如果说在也比较频繁的时候,就是那种 IO 操作比较频繁的时候,他大数据写的话可能时间可以设置成多少秒,最佳编了一个优化的策略。下面的话主要是一个进线程的一个技能分析。进线程的话它包含了一个方面,比如说一个进程的 CPU 的一个在一段时间内的一个消耗的情况。这边比如说像他的一个折线图,就是在这段时间内比如说像 CPU 的使用率,她的 iowait 她的一个情况。然后这边的话主要是内存的一个资源的消耗情况,就是通过对进线层的一个 CPU 内存,还有分磁盘、分组等这些资源的一个消耗情况就获取到另一大段,比如说像使用率、错误、饱和度这些指标,然后通过这些指标去识别你这个地线层的一个瓶颈在哪。然后针对这个部分指标的一个分析,然后以及现在我们内部就是从经验上定的一些基准值,然后给出一些优化建议。说举个例子就是说比如说当一个定的他的那个 CPU 他的利用率他不一直是100%。然后你这个定的它的 BPO 消耗很高,是否可以?比如说采用多进程的方式去进行处理,这样的话就可以降低,可以提高你的这个进展的一个运行或者计算的一个。下面是进线程的一个分析原理,主要也是通过 CPU 内存和磁盘确定这些指标的话,在我们工具的里面进行介绍。下面的话是一个定成的,看一下我们电话能力进程的分析的解读,就是 3a 进程。当进程 1 在执行的时候,比如说他会有一些这种上下文的切换,上下文切换比如说定成 1 在执行的时候他定成1,就会比如说做了一些操作,然后完了之后他会进行一个上下文的保存保证之后,然后这个时候进程 2 他会就是加载进来,然后进行进程 2 的操作。就是比如说在一个 CPU 和上。他的话主要就是明亮被动任务上下次变换的一个次数。比如说在他的变化次数高的时候,或者说据说当某个进程了连片耗尽,最后被系统挂切换到其他正在 CPU 进行,相当于他的那个比如说发起之后他的那个那样就会被保存,然后另外正在等待的进程,它就会进入到 M 的一个加载当中。然后在这种情况下,就是比如说在一个进程他的那个切换次数就非常多,很容易造成有比如说 CPO 的时间的大量的消耗,中间这部分上下文切换这部分资源的一个浪费。这样的话就可以大大缩短,否则的话大量时间都会浪费在这个上下文切换上。下面一个的话主要是热点函数的一个分析质检函数的话,可以看一下看,这个主要是给出了首页上主要是给出出了一个 top 10 的一个热点数,然后并给出一些这种优化建议。比如说像 A 的那个加速,或者说比如说一个压缩的一个加速,就是会有一些加速库的一些计划,还给出了一些比如说像指令运行的一个因素,适中周期等等。然后通过这个热点函数,我们可以去关联这些热点函数的一个指令,还有它的源码还有一个代码框。然后可以看到如果说这个值这个函数它的那个源码源码块在这,然后可以比较详细地看到它热点代码代码,我并对这些热点代码进行一个分析。然后工具的话还可以从几个模块对那个热点做进行一个展示。比如说包含这种模块包模块的维度,还有 CPU 和的维度,然后对整个热点数进行一个展示,然后还得等着他具体的一个调用账号。这边的话主要是一个火焰图的话,可以看到这边他上面有很多这种这样的情况,这个 Y 轴的对 X 轴的话主要是这样是一个多样数,它其实是每一层都是一些函数。对,下面是一个。就是他如果越高有这个好运,越高,费用大越深。然后最顶部的话比如说就正在那个被执行的一个函数就最低最顶上的函数下方的话主要是它的一个负函数,就是调用它的一个。然后 Y 轴的话主要是表示那个如果说你那个 X 轴越宽,然后它表示被搜到的次数就越多,然后他的执行时间就越长。然后需要调优的情况主要是看他最顶层的一个函数,如果这个顶层的月框怎么说明他被执行的时间越长,那么他性能就越低,那么就可以对这个最顶的进行一个优化。.热点产出的分析原理的话主要是基于 CPU 的 cycle 事件的一个数据去进行分析的。热点函数的话和那个指令的话不是基于 CPU 的 cycle 事件中断的调用这样的一个信息的一个表示符。就是比如说在一段时间内五个函数的那个某个指令,被采集到的那个次数越多就说明他越热因为他可能就被调用的次数越多。然后然后调用到就用以调用这样的信息去反映他的调用关系。然后就像我们刚刚看到的那个火焰图,就是一层一层的去调用。然后还可以做到,就是比如说放汇编去解析它的那个目标文件,获取它的一个汇编指令,然后还可以通过符号表还有第八个信息去关联它的源码,然后通过这一块,然后可以看到他热点函数的源码,并对这个源码去进行一个详细的分析,然后去进行一个调优。举个例子就是比如说这边有一个函数,然后这个函数的话它是做一个个类似于谈判,矩阵的一个一个运算,一个巨大的运算。然后我们可以看到采集出来的数据的话,它它它是以这个函数,它是它的热点函数,multimultiplay的这个函数是它的那个热点函数。然后我们可以看到它对应的一个代码块,还有具体的一个源码源码情况,还有个会编码,然后通过这个热点梳理源码,我们可以发现就是他做了一个那个运算,然后他主要就是一个这个谈话。那么通过这种可以通过疯狂的那个没有指令去优化这个矩阵的计算,没有指令的话可以一次性就是计算扑克塔多条那个矩阵,矩阵的一个,就可以提高它的一个运算效率。可以看一下这边的一个结果,这个大家可以后面就是用这个例子去执行一下具体的一个情况。下面一个是微架构的分析。微架构的分析的话主要是基于那个公共处理器的一个 MU 事件去进行一个分析的。我们可以看到就是整体的话是从上到下分为底层就是一个 top one 的一个模型。然后主要就是获取指令在 CPU 的流水线上的一个运行情况,然后帮助用户定位你这个这个运行运行期间应用在 CPU 上的一个性能瓶颈。比如说像 returring 它其实就表示就是一个正常执行的指令的大比来看的话就是相当于是。然后去利用安全的一个 CPU 资源。好页面上的话会给出这些已有的一个采集到的一个上报下的一个发布单的一个数据,然后并给出一些优化建议。然后还可以通过其他几个维度,比如说进程、线程模块、CPO等几个维度去给出它具体的每一个的一个 top down 的一个持续的数据。同样的他还可以有每个比如说函数他都有详细的任务大。然后这个还可以通过其他的一个维度去进行一个展示。同样的他也支持光关联到一个指令热点指令和一个源码一个情况。微架构的主要原理的话就是利用的话就是一条指令他在那个流水线上运行的时候的一个,然后去查看他的那个。大家都知道就是 CQ 的话它是一个流水线对吧。然后欧盟的话主要那个比如说微架构的话,它其实分为第一层下面是第一层,它分为那四大块, recarrying retaling 的话其实它就表示正常执行的指令,这个越大的话就表示你的指令的执行的成功率或者说效率得越好。 get speciination 的话主要是那个预期是比如说好吧,分支预测主要是指分支预测,就是你的那个预测的预测的那个准确率,如果说错误越多的话,就说明他对干部就会越高。 Frank front 的话,这个这表示的一个前端的话,主要就是比如说像。第二层的话就是相当于这一些具体的一个错误的一些信息了。比如说像 back and 上面的 memory bound 就相当于是内存的一个。这些的话主要是就是相当于处理器在每个阶段都设定了一些 pmu 事件,然后我们通过获取到这些事件,然后结合一些算法,一些模型,然后得到这些指令在 CPU 的流水线上的一个运行情况,然后并对这些数据进行一个分析,去整理出一个 top down 的模型,一个整体的数据。下面是微架构的一个例子,大家可以看一下。这边的话主要是执行定义了一个那个一个宿主。然后下面的话主要是对那个这个这个数据里面的数据做了一些做了一些。如果说大于 128 的话,就进行了一个加。然后我们用微架构去进行一个分析,发现它消耗的时间会比较长。然后因为这个数组里面的数据它都是随机的,所以说在它是否大于128,在这个判断的时候,如果进行 CPU 的预测的时候,它就会存在大量的失败的情况。因为有可能第一个数是128,小于128,第二个数又大于,第三个数又小于。这样的话,CPU在预测的时候它的 value 就很高,那么它的那个 add speculation 它会就很可以看到这边。所以说我们可以对比如说这个速度进行一个排序,排序完之后,那么它就是一条比如说从小到大的数据的从小到大一个数据的排序。那么在做判断的时候 CPU 的预测它就会比较准。因为它前面比如说前面在 128 前的,它可能都是小于 128 的。当大当然有一个大于 88 的时候,那么他都是后面所有组都是大于 88 的,然后他的预测就 CPU 预测的话他就会准确率就很高。那么投之后它的性能就这样,它的那个运行时间就降低到了差不多 11 秒,是提升了大概百分之二百架构的一个例子。第三个的话是缓存分析。有鲲鹏处理器,鲲鹏处理器的话是一个牛马架构的做,就是分为存在的一个焊接开始机制, CPU 在和就是 CPU 在访问数据的时候就会通过比如说存在本地的跨片的发带的,然后和在内存里面的等等这种方法情况。然后他不同的路径的话,他的那个实验是很大的。我们可以看到是的,如果他从 L1 开始上去获取,那么他的那个实验就很短。如果是 L1 开 L1 下没有获取到,它会从 L2 去获取,L2获取的话就比 L1 获取它的名额时间要慢很多。然后如果他在都获取不到。比如说可能要从画片画带去获取数据,他的那个时长也会增加很多。如果说他这种要从内传里面获取,那么他的那个就会有更好的一个实验的情况。然后基于这种情况的话,就是工具的话就是有那个对这个缓存的一个过程就是进行一个分析,因为它可能会存在瓶颈。然后并对这些那个性就是可能造出的一些性能问题给出一些原因。还有优化建议。目前的话主要包含了一个三个功能,一个是那个发生记得一个分析,然后第二个是历史的事件,第三个是那个违控。对我们可以先对他缓存整体的一个缓存进行一个分析,然后分析完了之后,然后再针对某个方向去进行一个单独的分析,那也都是能精确到那个异常代码。缓存分析的话主要是那个。那个一个缓存统计,比如说像 L1 开始 L2 开始, L3 开始的一个命中率。如果他们的命中率都比较低的时候,那么 CPU 的话可能要花费更长的时间,比如说去内存中读取。那么。比如说像数据访问的一个复合开始的时间和空间局部性等等,这些就是我们可以去提高他的率,然后这样的话就可以提高我们这个整体程序的一个性能。然后这样的话是可以。如果比如说像出现很多那个跨带跨片的方案,这边也是可以在界面上进行一个展示的。还可以比如说像那个跨片跨贷房的一个次数如果他次数倍数很大,那么我们可以考虑就是对相关的进程进行一个绑的操作。缓存分析的一个原理的话,主要是基于处理器的一个 Oncall 的一个变统计,就 CPU 和访问 cache 和 DDR 的一个性能数据,就分析对缓存和内存的一个访问次数命中率,还有一个带宽的情况。这边的话主要是一些靠事件和 Oncall 事件的话在 CPU 上的一个分布的情况,大家可以去看一下。然后访谈分析的第二个是那个 MIS 事件。 MIS 事件的话主要是实现了一些那个写,比如说像 lsllc 的一个名词,然后 TLB 的一个名词等等,然后并关联它的,并关联到造成这些 miss 事件的一个源代码。要做的话就可以自己去选择性地修改自己的程序,然后降低这些降低他们的历史率,然后提高一个程序的运行的性能。可以这边可以看到,就是比如说对每种事件它其实都是可以关联到那个函数以及调用这样。然后还有从不同维度去查看,然后查看它的调查信息,查看它的源码信息,然后并对这个源码进行一个优化。这个事件的话主要是基于 arm 的 spe 的能力去实现的。 1 的话是针对指令进行一个材料,然后记录一些触发事件的一个信息,就是所向精确的 PC 的尺寸信息。然后利用这个的话它其实是可以用于对应用程序进行这些事件的一个分析,然后精确到一个事件的代码。就 SP 的话它比 PMO 事件要更加的一个精确,因为 PM 的事件他是没无法精确到一个比如说像 PC PC 的一个指针的问题。其实是可以看一下界面这边的话主要比如说我们可以可能发生为共享的一些点,然后通过发展为共享一些点,去关联到他的那个汇编和源码,然后并对这个汇编和源码进行一个修改,可以针对性的修改源码,降低那个为共享利率,然后提高它的性能。研发几十伪共享的话,我们可以看一下伪共享的一些性能伪共享哪些原理他其实没共享的话其实就是说就是说比如说像多个 CPU 其实是多个线程,比如说多个线程修改同一条凯西兰造成的一个影响这样虽然我们从表面上看,比如说他可能其实是两个不同的变量是完全不一样的。但其实他内部实际上存储在同一个材质栏里。所以说三线程1,线程 2 同时比如说线程 1 去修改这个配置单,然后线程 2 又去读取这个我就会造成这种开始一致性的一个问题。然后标准对,所以共享的一个划分。这边的话,然后下面举一个那个共享的一个例子,这边的话可以这边不是一个函数,它其实就一个一代码。然后这边定义了一个结构体,它一个里面有两个一个 int 的一个。然后线程 1 的话去读取,比如说 X 然后线程 2 的话去修改 Y 就从代码逻辑上看,他可能觉得就是一个是主选一次,一个是修改 why 它其实没有任何的那个影响。但是实际上它其实是会发生未共享的情况。这边可以看到他这边就是发生了伪共享的点。然后他具体的那个代码就是关联到了线程 1 的一个 X 和一个线程 2 的一个Y。这样干其实也不是很好,因为他可能会告诉一些因为你是强制一把他,那么他可能会造成一些资源的浪费等。下面是那个IO 。 IO 分析的话主要是对那个磁盘磁盘的一个 IO 的读写。分析,这边一个是那个磁盘 O 的一个。比如说 IO 的读写次数,数据大小等等,就是那个变了一些利用率等等。然后第二个的话是这边的话是主要是 IO 的一些 apis 就是关联 IO 的一些应用层的一些操作,比如说像GID 、说明等等。那么我们通过这个上下的一个时间轴的一个关系,我们就可以这个应用它跟它的那个设备去进行一个关比如说像 link demo 间断,他进行了多少次还有操作,我进行了多少次读写操作等等。这样的话就可以把两个是吧跟那个应用去进行一个关联。然后下面还给出了一些那个优化建议,比如说可以看到 L 数据的分布一个分布情况,看看它是随机读还是。是随机操作还是顺序操作?然后这样的话可以给出一些相应的优化建议。操作 L 分析的主要原理的话就是在那个设备层的话就是设备层的话,如果是通过那个 App base 去跟踪那个内核的一个 point 去获取到它对应的那个 IO 的全部信息。不知道,主要是这个系统调用公司系统调用这个情况。然后下面的话就是对,因为 case point 的话它其实只能反映那个内核的跟 L 这些处理,他们无法反映应用层的一些那个应用的那些处理。所以说还通过 IO 的那个,还通过去捕获那个 IO 的一些 API 的一些调用,就获取到应用层的一些那个一些处理。然后通过应用层跟内部层做映射,做一个映射。然后可以看到可以那个指导指导应用层的一些优化。比如说通过某个时间段数据快了一个分布是连续的还是随机的,然后优化应用参加一个 IO 请求。然后下面的话是支持那个自研盘的内部的一个新的数据的优化。这边的话还可以,比如说一个完整的还有操作,比如说从队列插入。还要分析的举例,就举个例子。然后下面是那个一个一个人物操作,可以看到就是我们这边就是 sda 的那个,它就一直很低就上不去说他们那个写数据就写写的 lps 就很低就上不去。然后我们通过工具发现就是这段时间内的那个详细的一个 L 情况,然后得到那个 L 的一个调用站的一个信息,然后可以看到最终这一块。然后的一个进程采用的是一个同步的操作。然后我们可以通过帮我们这些数据去比对我们的源码,发现代码中确实是。 ATS 确实是这样的一个调化,所以然后可以对这部分去进行一个相应的优化,就可以提升一个。资源调度的一个环节。资源调度的话看一下资源调度的话主要是一个比如说像金县城的调度的情况,进线程,比如说是否只有评判,看一下怎么变化 CPU 是否及时调度。然后还可以分析就 CPU 和在各个时间点的一个运营情况。比如说每个 CPU 和就比如说一个 CPU 和,然后这边有很多 CPU 和,然后每个 CPU 和它上面的具体的运行情况。然后比如说像绿色的,它是有进程就在 running 然后这种颜色的就是在 idol 的状态,有空闲的状态,可以看到进程的。然后下面的话就是可以看到每个进程,比如说拿到一个进程之后,还可以看到这个进程就是他的那个运行的一个情况。比如说这个进程它一直在运行这段时间内有的进程,它可能一直在那个 wait wait 的状态就一直在等待。那么我们可以通过这部分去优化那个定层的一些信息。电源调度的话,它的那个主要原理的话就是相当于采集 CPU 的那个那个调度事件,CPU的调度事件。然后从进程和线程的角度就是排序,把各个事件进行一个排序,然后计算出每个事件的一个时间差,然后去标记出来。比如说一个进程,它的那个 wait time 的一个时间。然后这样的话就。可以看出整个 CPO 上。比如说像下面举个你的资源调度的例子,比如说这边可以看到就是进程 1 他占用了很长的时间的一个一个,然后定场号的话他就一直在相当于一直在等待的情况,然后又执行了一点透明进程,这其实是同同一个进程同一个进程。比如说就是说他有两个GID ,现在就是这个他是两两个两个进程对两个两个一个一个 GID 然后两个 GID 两个线程可以看到线程 1 的话它就是它占用的时间是绿色的,这部分他一直在执行,然后变成 2 的话他就在一直在等待的状态,然后又执行了一点说明,线程 1 跟线程 2 之间有一个一直在等待的过程,然后我们就可以通过一些辅助的功能,比如说工具中的所有等待的功能,可以找到这个函数的一个情况。我们可以发现他把那个 sleep sleep 10 秒携带了这个锁。这个锁的一个情况就是加了日报锁之后,然后他没有释放,他没有释放,然后在里面就是那怎么样停了几秒之后,然后然后这个现场就会一直正在执行优化的时候。比如说优化成把这个 leap 10 秒加到这个锁的外面,那么他的运行情况就是这个样子的,就是两边都是一个正常的执行状况。所以说这样优化的时候,每个线程它都能够以最大的程度的运行记录,这样的话就是一个比较好的一个资源调度的一个情况。下面就是讲迫于等待的一个功能,可以看到这边,所以等待的那个这边是所有等待的一个函数的调用信息。然后这边是所以所等待的一个函数调用点,然后也可以看到它对应的一个源码还一个汇编指令的一个信息。所以的那个话它其实主要也是采集那个热点函数,然后并将这些热点函数进行分析,它分析它的那个 glibus glib C 的一个锁和与等待的一些函数。比如说像 sleep 加没有 tax 等等,这也是我的一个情况。然后通过这些情况关联到它对应的一个进程和调用点的一个情况。然后并通过我们就是一些专家或者一些经验,然后就给出一些优化建议。下面是那个 HPC 的一个场景。 HPC 的话就相当于目前的话是那个 open MP 和 MPI 分析,就是我们是通过那个采集系统的那个 pmu 事件,然后并配合采集面向比如说 open MP 和 MPI 的应用的关键指标,然后去帮助用户获取获取这个 region 已经以及这个 barary to to bear barrier 的那个唤醒疾病的一些时间,还有一些微架构的一些指标。下面的话是主要是一些那个一些指标信息,比如说像 open MP 的那个运行时指标,然后计划指标,还有 MPI 的运行时指标,还有 top down 的一些信息等等。这边后面这种比如说涉及到这种 HPC 的这种高性能计算的场景,大家可以去使用一下这个对。下面是那个内存诊断,对,这边主要是诊断方面的一个那个功能,上面主要是系统性能方面的一些方案。诊断功能的话就比如说像包括那个内存诊断的话主要是包括内存泄露、内存异常访问、内存异常访问,然后内存越界还有 OM 等的一些内存的跟踪。像内存泄露的话我们可以看看到就是内存泄露的点会给出内存泄露的具体到哪一行的具体泄露,然后只需要去改修改这一行就可以了。然后内内存异常释放,他其实也一样,就是说他也可以看到内存异常示范的点代码,具体去修改这个代码即可。然后内存越界内存越界的话,比如说像内存的一个正常的使用情况,比如说同步包括他的那个心理认证里面的等等。对它的一个内存的使用情况。然后那个越界的话也包括比如说像堆,然后对于堆的对 use after three 的那然后还有 star 吊着还有 dark 等等。然后 OM 的话主要就是比如说内存发生,如果内存泄露它导致的比如说一些进程的一些被被杀掉,获得被杀掉之后他的那个一些调用单的一些情况,还有被杀掉的时间定的是哪些等等。内存诊断的原理。主要就是采用那个 hook 的那个技术获取内存的一个申请和释放,然后并通过那个内存地址的一个匹配,就匹配到了申请点和示范点,判断是否有内存那个泄露还有异常示范的产品。然后我们拉起那个进程的产品,主要是用 pre load 那个技术加载用于 hook 的函数的动态库。然后针对这种产品,然后可以用那个 case 去加载用的 hook 函数的方法,然后计划那个计划原有的动态度,然后是他用到工具的那个 so 然后去获取到他对应的内存。乐见的话主要集成的是谷歌的这个工具,然后就是在编译的时候进行插桩,然后对每块内存的空间增加一个 memory 然后去检查上面是那个诊断。诊断的话当然还有比如说像包括网络诊断那个 IO 诊断,这里就不影响了大家再可以去费用的时候可以用是吧。下面是那个调和助手,调助手的话主要就是采集数据那个覆盖 OS 应用硬件等各个课程的一个系统配置性能指标。你看一下,跳出去,他从应用层,然后从那个硬件层,硬件又包括CPU ,那什么还有网络等等都会有设计,然后通过这些软件的一个结合,然后分析它的一个性能以及性能消耗。性能。就是性能的一些瓶颈,像资源消耗以及他们今天我们。然后助手的话主要就是它包含几大块一块是系统配置,主要是从配置方面给出的一些优化建议。这边主要是那个优化的一个,然后可以通过这些优化的那个优化建议,然后以及具体的一个说明和优化建议去调整对应的那个定了一些配置。然后第二个是热点函数,热点函数的话主要是包括一些比如说像加速库的函数,然后一些一些已知的经验的函数去进行一个计划,比如说像 delete delete 的那个加速。第三个的话主要是从系统性能方面,比如说从 CPU 的一个任务情况,然后去去发散到你的比如说各项的一个优化的可优化的一些点,然后通过这些点一个数据的分析,然后去进行一个数据的调整,对,找到他的性能瓶颈,然后根据优化建议去优化即可。第四个的话主要是进程线程性的,就是你的进线程是否存在瓶颈,然后可以通过它对应的优化建议去优化。这边主要是调助手,然后调助手的话主要系统配置上还给出了那个具体的优化建议的一个详细数据。下面是系统性鲲鹏分析工具的最后一项 Java 性能分析工具。这边 Java 性能分析工具的话主要是针对 Java 程序的一个性能分析,就是能图形化形式 Java 的一个堆线程数,垃圾回收等信息,然后去收集它的热点函数,定位那个程序的一个平均点,帮助用户进行一个性能调优。它主要分为两大部分,一部分是那个发力的就是在线分析,然后一部分是那个 sampling 就是采样分析,这边都是基于不同的技术去进行分析的。在线分析的话主要基于 attach 的技术,有实现 Java 程序的那个内部数据的一个动态采集,然后还可以动态的获取到实时的数据,然后发现这些实时数据的一个那个云点,比如说像 tip 还有那个 GC 活动等这些。然后采样采样分析的话主要就是一段时间内去采集收集那个这边的一个内部的一些活动和性能事件,然后通过那个一些录制回放的方式,然后进行一些分析,然后去发现他的一个性能力。然后这部分的话主要是针对那个 Java 的一个 Java 应用的一个分析。然后性能分析工具的话大致介绍就到这里。他其实没有一个因为性能分析可能会比较难,就是说没有一个就是逻辑不到位的情况。所以说需要根据比如说像昆仑性能分析工具各个各个一个一各个模块各个应用去同时对这个一个服务器或者一个应用去进行一个整体的情况的一个分析,最终得出一些比较好的一些优化建议,然后或者对代码进行一些修复,然后这样才能达到一些效果。
  • [技术干货] 《锁长期等待》项目大作业解析读书笔记
    《锁长期等待》项目大作业 一、调试前的准备工作1.从网址上下载pthread_mutex_long.c测试程序上传到服务器上,通过性能分析工具找出程序中加锁范围不合理的地方并解决。2.授权文件:chmod 777 pthread_mutex_long.c 3.编译程序:gcc -g pthread_mutex_long.c -o pthread_mutex_long -lpthread -lm && chmod 777 pthread_mutex_long 4.绑核启动:taskset -c 0-1 ./pthread_mutex_long二、优化前的性能分析通过邮件里的信息,可以登录性能分析工具 Web,通过性能分析工具发现环境中有性能问题的目标程序。发现可调优的目标程序后,修改代码,重新使用性能分析工具进行分析,相关指标有变化说明调优成功。1.全景分析任务结果-性能tab 在程序运行的过程当中,我们利用鲲鹏性能分析工具创建系统全景分析任务,分析当前程序。任务名称:pthread_mutex_long配置参数如下:      分析对象:系统      分析类型:全景分析      采样时长:60 秒      采样间隔:1 秒如上图:“总览”页签上方会显示“检测到 CPU 利用率高”的优化建议。2.进程/线程性能分析任务结果-总览任务名称:pthread_mutex_tesst2配置参数如下:      分析对象:系统      分析类型:进程/线程性能分析      采样时长:60 秒      采样类型:全部勾选如上图所示,可以看到 pthread_mutex_l 程序在消耗大量的 CPU,这个也是我们启动的测试程序。3.资源调度分析任务结果-总览下面来创建资源调度分析任务,并启动分析。具体配置如下所示任务名称:pthread_mutex_tesst3配置参数如下:      分析对象:系统      分析类型:资源调度分析      采样时长:120 秒      采样文件大小(MB):256 秒过滤出 pthread_mutex_l 进程,如下图所示:4.资源分析任务结果-进程/线程调度如上图:发现在采样期间两个线程之间的调度没有交集,没有平衡的相互交替运行,我们可以推断两个线程可能在抢占某个资源,最有可能就是有锁等互斥量操作。其中一个线程长期占用锁,导致另一个线程无法得到锁而长期等待。5.热点函数分析任务结果-总览任务名称:pthread_mutex_test4配置参数如下:      分析对象:系统      分析类型:热点函数分析      采样时长:30 秒如上图所示:发现测试程序的热点函数是 Func6.热点函数分析任务结果-热点函数源码通过对热点函数的源码分析,发现其中有一段业务逻辑并不涉及并发的场景,并且这块业务逻辑是Func函数中热度最高的,完全可以移到锁的范围之外去。所以我们应该将对应的代码块进行修改。7.代码优化前后代码优化前代码优化后优化后重新编译:编译命令:gcc -g pthread_mutex_long.c -o pthread_mutex_long -lpthread -lm && chmod 777 pthread_mutex_long三、优化后的性能分析代码优化后重新启动,资源分析任务结果-进程/线程调度绑核启动:taskset -c 0-1 ./pthread_mutex_longpthread_mutex_test5配置参数如下:      分析对象:系统      分析类型:资源调度分析      采样时长:120 秒      采样文件大小(MB):256 秒从上图可以看出:两个线程可以并发运行不需要互斥操作的计算,大幅度提升了计算性能。资源分析任务结果-进程/线程调度,测试程序的两个线程会平稳的进行,不会存在长时间的等待。
  • [技术干货] 第十讲:《锁长期等待》项目大作业解析
      鲲鹏性能分析工具是一个工具集,包含调优助手、系统性能分析工具、Java性能分析工具、系统诊断工具。业界已经有非常丰富的系统性能分析工具,而鲲鹏性能分析工具的目标是在提供业界工具的能力之外,还实现几点独特功能:基于鲲鹏处理器,提供软硬件结合分析能力,采集和分析硬件性能指标,以及硬件性能在软件层的表现,让用户更加全面的了解整个系统的运行状况。系统化的分析建立各层指标之间的关联关系、并以用户视角呈现这些指标和关系,方便用户更易于发现问题。结合华为在鲲鹏处理器上的性能优化经验,给出优化思路和建议,帮忙用户快速定位和修复问题。实现分析过程管理,例如:优化结果对比、优化效果标记、优化过程记录等。  工具从软件逻辑上分成Analysis Server与Agent两部分,其中Analysis Server的主要作用是实现性能数据分析及分析结果呈现,Agent的主要作用是实现性能数据采集。  系统性能分析工具具有以下分析类型:通用分析:全景分析(包括对CPU重点指标的解读、内存重点指标解读、存储IO重点指标解读、网络IO重点指标解读)、进程/线程性能分析(对图像和重点指标的分析解读,例如Nvcswch/s、Cswch/s,系统调用信息)、热点函数分析(包括热火焰图,冷火焰图等)系统部件分析:微架构分析(Tips给出优化建议,详细的调用栈信息,支持以函数、模块、线程、进程等多维度展示)、访存分析(Miss事件分析、伪共享分析)、IO分析(以块设备为分析对象)专项分析:资源调度分析(进程/线程调度信息,识别线程是否频繁上下文切换,CPU是否能及时调度。)、锁与等待分析。  以下是通过性能分析工具找出程序中加锁范围不合理的地方并解决的过程1.做好前期准备工作2.创建工程3.创建采集数据任务,任务名qjfx_1,采样时长60s,采集间隔1s性能tab 显示的数据,切换到表格模式并对用户态cpu利用率进行排序,看出cpu0的使用率最高,达到99.44%,与优化建议相符。磁盘使用率最高为1.44%,每个磁盘平均转换时间较高的会在cpu利用率上体现出来。4.创建cpu进程/线程分析任务,查看结果总览总览页签下查看各进程的CPU使用情况,发现启动命令为 ./pthread_mutex_long 的进程的用户态使用率高,这个正好也是我们启动的测试程序。检测到如下线程没有设置线程亲和性{Tid_3095367_pthread_mutex_l}优化建议:建议减少跨NUMA访问内存。不同NUMA内的CPU core访问同一个位置的内存,性能不同。内存访问延时从高到低为:跨CPU > 跨NUMA不跨CPU >NUMA内。修改方法:1) 通过numactl启动程序,如下面的启动命令表示启动test程序,只能在CPU core28到core31运行(-C控制)。numactl -C 28-31 ./test2) 在C/C++代码中通过sched_setaffinity函数来设置线程亲和性。3) 很多开源软件已经支持在自带的配置文件中修改线程的亲和性,例如nginx可以修改nginx.conf文件中的worker_cpu_affinity参数来设置nginx线程亲和性。 检测到CPU利用率不均衡,线程Tid_3095367:pthread_mutex_l的CPU占用率过高。优化建议:建议优化线程Tid_3095367:pthread_mutex_l的实现。修改方法:将线程的计算任务分解到其它线程或增加此类线程的线程数。5.资源调度分析任务结果-总览,新建资源调度分析任务命名为zydd_1,时长设为100s两个运行线程的TID为2915211,2915212在进程/线程调度中可以看到在采样期间两个线程之间的调度没有交集,没有平衡的相互交替运行,我们可以推断两个线程可能在抢占某个资源6.热点函数分析任务结果-总览测试程序的热点函数是 Func热火焰图如上所示,看出Func函数热度最高从代码流图中可以看出Basic Block7模块有问题,点击后跳转到出问题的语句与汇编代码,46-49行进行了一个局部操作导致锁等待耗时很长,而加锁中的业务逻辑应尽可能简单,即满足其中所有操作都具有全局并发的场景,分析可知46-49行代码应移出锁范围之外。7.代码优化前后8.代码优化后重新启动,资源分析任务结果-进程/线程调度可以看到在进行了代码优化后测试程序的两个线程会平稳的进行,不会存在长时间的等待。
  • [技术干货] 第十讲:《锁长期等待》项目大作业解析
      鲲鹏性能分析工具是一个工具集,包含调优助手、系统性能分析工具、Java性能分析工具、系统诊断工具。业界已经有非常丰富的系统性能分析工具,而鲲鹏性能分析工具的目标是在提供业界工具的能力之外,还实现几点独特功能:基于鲲鹏处理器,提供软硬件结合分析能力,采集和分析硬件性能指标,以及硬件性能在软件层的表现,让用户更加全面的了解整个系统的运行状况。系统化的分析建立各层指标之间的关联关系、并以用户视角呈现这些指标和关系,方便用户更易于发现问题。结合华为在鲲鹏处理器上的性能优化经验,给出优化思路和建议,帮忙用户快速定位和修复问题。实现分析过程管理,例如:优化结果对比、优化效果标记、优化过程记录等。  工具从软件逻辑上分成Analysis Server与Agent两部分,其中Analysis Server的主要作用是实现性能数据分析及分析结果呈现,Agent的主要作用是实现性能数据采集。  系统性能分析工具具有以下分析类型:通用分析:全景分析(包括对CPU重点指标的解读、内存重点指标解读、存储IO重点指标解读、网络IO重点指标解读)、进程/线程性能分析(对图像和重点指标的分析解读,例如Nvcswch/s、Cswch/s,系统调用信息)、热点函数分析(包括热火焰图,冷火焰图等)系统部件分析:微架构分析(Tips给出优化建议,详细的调用栈信息,支持以函数、模块、线程、进程等多维度展示)、访存分析(Miss事件分析、伪共享分析)、IO分析(以块设备为分析对象)专项分析:资源调度分析(进程/线程调度信息,识别线程是否频繁上下文切换,CPU是否能及时调度。)、锁与等待分析。  以下是通过性能分析工具找出程序中加锁范围不合理的地方并解决的过程1.做好前期准备工作2.创建工程3.创建采集数据任务,任务名qjfx_1,采样时长60s,采集间隔1s性能tab 显示的数据,切换到表格模式并对用户态cpu利用率进行排序,看出cpu0的使用率最高,达到99.44%,与优化建议相符。磁盘使用率最高为1.44%,每个磁盘平均转换时间较高的会在cpu利用率上体现出来。4.创建cpu进程/线程分析任务,查看结果总览总览页签下查看各进程的CPU使用情况,发现启动命令为 ./pthread_mutex_long 的进程的用户态使用率高,这个正好也是我们启动的测试程序。检测到如下线程没有设置线程亲和性{Tid_3095367_pthread_mutex_l}优化建议:建议减少跨NUMA访问内存。不同NUMA内的CPU core访问同一个位置的内存,性能不同。内存访问延时从高到低为:跨CPU > 跨NUMA不跨CPU >NUMA内。修改方法:1) 通过numactl启动程序,如下面的启动命令表示启动test程序,只能在CPU core28到core31运行(-C控制)。numactl -C 28-31 ./test2) 在C/C++代码中通过sched_setaffinity函数来设置线程亲和性。3) 很多开源软件已经支持在自带的配置文件中修改线程的亲和性,例如nginx可以修改nginx.conf文件中的worker_cpu_affinity参数来设置nginx线程亲和性。 检测到CPU利用率不均衡,线程Tid_3095367:pthread_mutex_l的CPU占用率过高。优化建议:建议优化线程Tid_3095367:pthread_mutex_l的实现。修改方法:将线程的计算任务分解到其它线程或增加此类线程的线程数。5.资源调度分析任务结果-总览,新建资源调度分析任务命名为zydd_1,时长设为100s两个运行线程的TID为2915211,2915212在进程/线程调度中可以看到在采样期间两个线程之间的调度没有交集,没有平衡的相互交替运行,我们可以推断两个线程可能在抢占某个资源6.热点函数分析任务结果-总览测试程序的热点函数是 Func热火焰图如上所示,看出Func函数热度最高从代码流图中可以看出Basic Block7模块有问题,点击后跳转到出问题的语句与汇编代码,46-49行进行了一个局部操作导致锁等待耗时很长,而加锁中的业务逻辑应尽可能简单,即满足其中所有操作都具有全局并发的场景,分析可知46-49行代码应移出锁范围之外。7.代码优化前后8.代码优化后重新启动,资源分析任务结果-进程/线程调度可以看到在进行了代码优化后测试程序的两个线程会平稳的进行,不会存在长时间的等待。
  • [技术干货] 第八讲:鲲鹏性能分析工具基础知识读书笔记--性能分析工具
    鲲鹏性能分析工具是一个工具集,其中包括四大工具:调优助手、系统性能分析工具、Java性能分析工具和系统诊断工具。 相较业界传统性能分析工具,DevKit在提供通用的功能之外还提供了以下能力: 1. 基于鲲鹏处理器,采集分析软硬件结合的性能指标。 2. 系统化建立各层指标之间的关系,并以用户视角呈现。 3. 结合华为性能优化经验,给出优化思路和建议。 4. 实现分析过程管理。 ## 性能分析工具功能介绍: #### 一、全景分析: 获取服务器运行时的各类信息。综合分析系统性能和系统配置情况,给出检测到的性能瓶颈点。 本部分中,提到了三个系统性能指标:使用率、饱和度、错误。 * 使用率:资源忙不忙 * 饱和度:资源不能服务的情况多不多 * 错误:错误事件个数 #### 二、热点函数分析 可以知道耗时比较多、调用比较多的函数的情况。可以通过火焰图看到函数的调用栈和调用时间。 #### 三、微架构分析 获得指令在CPU流水线上的情况。可以帮助用户快速定位CPU性能瓶颈。 #### 四、访存分析 分析程序访问缓存、内存的情况,分析这一过程中可能产生的性能瓶颈。主要包括三个功能:(1)访存统计分析;(2)Miss事件分析;(3)伪共享分析 #### 五、IO分析 分析IO过程的各类相关信息、分析IO调用过程中相关的线程及调用栈的信息。 #### 六、资源调度分析 给出进程线程的调度情况。分析CPU是否能合理的去调度各个进程/线程、能图形化的展示各个进程/线程之间的运行切换情况。
  • [技术干货] 第八讲:鲲鹏性能工具基础知识读书笔记--初识性能分析工具
    鲲鹏性能分析工具性能分析工具支持鲲鹏平台上的系统性能分析、Java性能分析和系统诊断,提供系统全景及常见应用场景下的性能采集和分析功能,并基于调优专家系统给出优化建议。同时提供调优助手,指导用户快速调优系统性能。性能分析工具包含以下四个子工具:调优助手,系统性能分析,Java性能分析,系统诊断。一、工具的功能 用户可通过优化建议拓扑树图查看CPU密集型、网络IO密集型和存储IO密集型等业务类型优化建议中的相关配置、指标说明、优化建议及优化指导,也可查看CPU、内存、存储、网络和OS等系统配置的详细数据。通过采集系统的PMU事件并结合面向OpenMP和MPI应用的关键指标,帮助用户精准获得Parallel region及Barrier-to-Barrier的串/并行时间、多维度的MPI_wait指标、热点函数、L3利用率等信息,按性能分析逻辑维度呈现。二、软件的架构工具从软件逻辑上分成Analysis Server和Agent两大部分: 1.  Analysis Server:主要作用是实现性能数据分析及分析结果呈现。2.  Agent:主要作用是实现性能数据采集。三、功能原理1、全景分析:基于USE (utilization、saturation、errors)性能分析方法,获得系统资源(CPu、内存、l/o等)的使用率、饱和度、错误等指标,以此识别系统瓶颈。展示服务器系统及其各个子系统的TOPO结构及其配置。方便用户快速了解系统配置,及是否存在配置不合理的点,例如内存条配置位置。基于USE性能分析方法,针对系统CPU、内存、磁盘lIo、网络lo资源的运行情况,获得它们的使用率、饱和度、错误等指标,以此识别系统瓶颈。针对大数据、数据库、分布式存储场景的硬件配置、系统配置和组件配置进行检直并显示不是最优的配置项,同时分析给出典型硬件配置及软件版本信息。综合分析系统性能和系统配置情况,给出检测到的性能瓶颈点,并给出优化建议和修改方法。2、进程/线程性能分析采集进程/线程对CPu、内存、存储IO等资源的消耗情况,获得对应的使用率、饱和度、错误等指标,以此识别进程/线程性能瓶颈。针对部分指标项,根据当前已有的基准值和优化经验提供优化建议。分析进程/线程的系统调用情况、上下文切换。3、热点函数分析Top 10热点函数,支持给出优化建议,包括提示采用加速库优化。以函数/模块/线程/CPu核等维度展示热点函数。函数调用栈信息。基于各函数的调用栈信息绘制的火焰图。4、微架构分析基于鲲鹏处理器PMU (Performance Monitor Unit)事件,建立鲲鹏处理器的Top-Down模型,获得指令在CPu流水线上的运行情况,可以帮助用户快速定位当前应用在cPu上的性能瓶颈,用户可以有针对性地修改自己的程序,以充分利用当前的硬件资源。一条指令在流水线上运行时,需要经过取指、译码、执行、回写等多个阶段,处理器在每个阶段都设定了一些PMu统计事件,通过采集这些事件,并结合一定模型,就可以表示出指令在CPu流水线上的运行情况。5、访存分析工具基于CPU访问缓存和内存的事件,分析访存过程中可能的性能瓶颈,给出造成这些性能问题的可能原因及优化建议,具体包括三个功能:访存统计分析、Miss事件分析、伪共亨分析。6、lO分析以块设备为分析对象,分析得出I/o操作次数、I/O数据大小、I/o队列深度、I/O吞吐率、I/o操作时延等信息。支持关联I/o操作时的进程/线程及调用栈信息,包括时间、时间、块数、CPU核、进程ID.进程名称、调用栈等信息。支持关联I/O操作时应用层的I/O APIs,包括进程PID、函数名、调用次数、平均执行时间.总执行时间、执行时间占比等信息。7、资源调度分析进程/线程调度信息,识别线程是否频繁上下文切换;CPU是否能及时调度。分析进程/线程在NUMA节点的切换情况,对于频繁切换,给出绑核优化建议。分析CPu核在各个时间点的运行状态,如:idle、running等。如果是running状态,能关联在CPU核上运行的进程/线程信息。分析进程/线程在各个时间点的运行状态,如: wait_blocked、wait_for_cpu和running,能方便识别频繁上下文切换的线程。
  • [问题求助] 【mindx】【推理功能】多线程推理,执行一段时间后报Failed to create Stream, ret=6001
    【功能模块】使用mindx多线程推理,执行一段时间后报Failed to create Stream, ret=6001,流程如下:初始化sdk-CreateMultipleStreams-推理-stopstream-CreateMultipleStreams-推理-stopstream如上反复执行【日志信息】(可选,上传日志内容或者附件)pipline:{    "detection":{        "stream_config":{            "deviceId":"0"        },        "mxpi_rtspsrc0":{            "props":{                "rtspUrl":"rtsp://192.168.10.110:554/07675260004380860101?DstCode=01&ServiceType=4&ClientType=1&StreamID=1&SrcTP=2&DstTP=2&SrcPP=0&DstPP=1&MediaTransMode=0&BroadcastType=0&SV=1&TimeSpan=20220718T022000Z-20220718T022500Z&Token=PkYM/bysWO7cnGQirlX88u24zS2P/ApZ6h/2J0QG7v0=&Multiplex=Kir8/SRUiW7dvKP3MLhAB8lGsqzVvnn95l2CCqWlfXU=&"            },            "factory":"mxpi_rtspsrc",            "next":"mxpi_videodecoder0"        },        "mxpi_videodecoder0":{            "props":{                "deviceId":"0",                "inputVideoFormat":"H264",                "outputImageFormat":"YUV420SP_NV12"            },            "factory":"mxpi_videodecoder",            "next":"mxpi_skipframe0"        },        "mxpi_skipframe0":{            "factory":"mxpi_skipframe",            "next":"mxpi_imageresize0",            "props":{                "frameNum":"10"            }        },        "mxpi_imageresize0":{            "props":{                "dataSource":"mxpi_videodecoder0",                "resizeHeight":"640",                "resizeWidth":"640",                "resizeType":"Resizer_KeepAspectRatio_Fit"            },            "factory":"mxpi_imageresize",            "next":"mxpi_modelinfer0"        },        "mxpi_modelinfer0":{            "props":{                "modelPath":"x.om",                "postProcessConfigPath":"x.cfg",                "labelPath":"x.name",                "postProcessLibPath":"/opt/mxVision/lib/libMpYOLOv5PostProcessor.so"            },            "factory":"mxpi_modelinfer",            "next":"mxpi_dataserialize0"        },        "mxpi_dataserialize0":{            "props":{                "outputDataKeys":"mxpi_modelinfer0,ReservedFrameInfo"            },            "factory":"mxpi_dataserialize",            "next":"appsink0"        },        "appsink0":{            "props":{                "blocksize":"4096000"            },            "factory":"appsink"        }    }}
  • [其他] 简记:Gauss与Citus使用体验对比
    一、测试环境1.硬件环境1台   coordinator3台   worker/datanodeOS :     Centos 7.4CPU: 4U内存: 32G2.软件环境Gauss:版本:8.0集群规模:3节点*2进程  配置高可用存储类型:行存表Citus:版本:PG 14.1 + Citus 10.2.3集群规模:3节点*2进程  未配置高可用存储类型:行存表二、测试方法(TPCH标准测试集兼容性和性能测试)1.构造数据cd /srv/BigData/tar -zxvf TPCH.tar.gzcd TPCHtar -zxvf dbgen.tar.gzchmod 777 -R /srv/BigData/TPCHchown -R omm:wheel /srv/BigData/TPCHcd /srv/BigData/TPCH/dbgen./dbgen -s 102.建表Gauss外表:gsql -d postgres -p 25308 -f TPCH_CREATE_FOREIGN_TABLE_001.sql示例:drop FOREIGN table if exists orders_load;CREATE FOREIGN TABLE ORDERS_load(    O_ORDERKEY       BIGINT  , O_CUSTKEY        BIGINT  , O_ORDERSTATUS    CHAR(1)  , O_TOTALPRICE     DECIMAL(15,2)  , O_ORDERDATE      DATE  , O_ORDERPRIORITY  CHAR(15)  , O_CLERK          CHAR(15)  , O_SHIPPRIORITY   BIGINT  , O_COMMENT        VARCHAR(79))SERVER gsmpp_serverOPTIONS(location 'gsfs://xx.xx.xx.xx:xx/orders*',format 'text',delimiter '|',encoding 'utf8',mode 'Normal');分区表gsql -d postgres -p 25308 -f TPCH_CREATE_PARTITION_TABLE_001.sqldrop table if exists orders;CREATE TABLE ORDERS(    O_ORDERKEY       BIGINT NOT NULL  , O_CUSTKEY        BIGINT NOT NULL  , O_ORDERSTATUS    CHAR(1) NOT NULL  , O_TOTALPRICE     DECIMAL(15,2) NOT NULL  , O_ORDERDATE      DATE NOT NULL  , O_ORDERPRIORITY  CHAR(15) NOT NULL  , O_CLERK          CHAR(15) NOT NULL  , O_SHIPPRIORITY   BIGINT NOT NULL  , O_COMMENT        VARCHAR(79) NOT NULL  --, primary key (O_ORDERKEY))--with (orientation = column)distribute by hash(O_ORDERKEY)PARTITION BY RANGE(O_ORDERDATE)(    PARTITION O_ORDERDATE_1 VALUES LESS THAN('1993-01-01 00:00:00'),    PARTITION O_ORDERDATE_2 VALUES LESS THAN('1994-01-01 00:00:00'),    PARTITION O_ORDERDATE_3 VALUES LESS THAN('1995-01-01 00:00:00'),    PARTITION O_ORDERDATE_4 VALUES LESS THAN('1996-01-01 00:00:00'),    PARTITION O_ORDERDATE_5 VALUES LESS THAN('1997-01-01 00:00:00'),    PARTITION O_ORDERDATE_6 VALUES LESS THAN('1998-01-01 00:00:00'),    PARTITION O_ORDERDATE_7 VALUES LESS THAN('1999-01-01 00:00:00'));Citus分区表建表语句示例drop table if exists orders;CREATE TABLE ORDERS(    O_ORDERKEY       BIGINT NOT NULL  , O_CUSTKEY        BIGINT NOT NULL  , O_ORDERSTATUS    CHAR(1) NOT NULL  , O_TOTALPRICE     DECIMAL(15,2) NOT NULL  , O_ORDERDATE      DATE NOT NULL  , O_ORDERPRIORITY  CHAR(15) NOT NULL  , O_CLERK          CHAR(15) NOT NULL  , O_SHIPPRIORITY   BIGINT NOT NULL  , O_COMMENT        VARCHAR(79) NOT NULL  --, primary key (O_ORDERKEY))PARTITION BY RANGE(O_ORDERDATE);CREATE TABLE ORDERS_1   PARTITION OF ORDERS    FOR VALUES from ('1992-01-01 00:00:00') to ('1993-01-01 00:00:00' );CREATE TABLE ORDERS_2   PARTITION OF ORDERS    FOR VALUES from ('1993-01-01 00:00:00') to ('1994-01-01 00:00:00' );CREATE TABLE ORDERS_3   PARTITION OF ORDERS    FOR VALUES from ('1994-01-01 00:00:00') to ('1995-01-01 00:00:00' );CREATE TABLE ORDERS_4   PARTITION OF ORDERS    FOR VALUES from ('1995-01-01 00:00:00') to ('1996-01-01 00:00:00' );CREATE TABLE ORDERS_5   PARTITION OF ORDERS    FOR VALUES from ('1996-01-01 00:00:00') to ('1997-01-01 00:00:00' );CREATE TABLE ORDERS_6   PARTITION OF ORDERS    FOR VALUES from ('1997-01-01 00:00:00') to ('1998-01-01 00:00:00' );CREATE TABLE ORDERS_7   PARTITION OF ORDERS    FOR VALUES from ('1998-01-01 00:00:00') to ('1999-01-01 00:00:00' );3.导入数据Gauss:通过gds导入(类似pg_fdw)gds -d /srv/BigData/TPCH/dbgen -p xx.xx.xx.xx:5001 -H 0.0.0.0/0 -l /srv/BigData/TPCH/gds_log.txt -D入库语句示例:insert into  orders   select * from  orders_load;analyze orders;--分区表:orders--外表:orders_loadCitus:通过copy语句导入copy orders from '/srv/BigData/TPCH/dbgen/orders.tbl' with (format 'text',delimiter '|',encoding 'utf8');SELECT create_distributed_table('orders', 'o_orderkey');analyze orders;4.进行测试测试案例选择22个场景,以下提供3个作为参考测试语句示例1:select        l_returnflag,        l_linestatus,        sum(l_quantity) as sum_qty,        sum(l_extendedprice) as sum_base_price,        sum(l_extendedprice * (1 - l_discount)) as sum_disc_price,        sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)/1000) as sum_charge, --add /1000        avg(l_quantity) as avg_qty,        avg(l_extendedprice) as avg_price,        avg(l_discount) as avg_disc,        count(*) as count_orderfrom        lineitemwhere        l_shipdate <= date '1998-12-01' - interval '3 day'group by        l_returnflag,        l_linestatusorder by        l_returnflag,        l_linestatus;测试语句示例2:select        s_acctbal,        s_name,        n_name,        p_partkey,        p_mfgr,        s_address,        s_phone,        s_commentfrom        part,        supplier,        partsupp,        nation,        region,        (                select                        ps_partkey as temp_ps_partkey, min(ps_supplycost) as temp_min_ps_supplycost                from                        partsupp,                        supplier,                        nation,                        region                where                        s_suppkey = ps_suppkey                        and s_nationkey = n_nationkey                        and n_regionkey = r_regionkey                        and r_name = 'EUROPE'            group by ps_partkey        ) as tempwhere    p_partkey = temp_ps_partkey        and p_partkey = ps_partkey        and s_suppkey = ps_suppkey        and p_size = 15        and p_type like '%BRASS'        and s_nationkey = n_nationkey        and n_regionkey = r_regionkey        and r_name = 'EUROPE'        and ps_supplycost = temp_min_ps_supplycostorder by        s_acctbal desc,        n_name,        s_name,        p_partkeylimit 100;测试语句示例3:select        sum(l_extendedprice) / 7.0 as avg_yearlyfrom        lineitem,        part,        (select            l_partkey as temp_l_partkey, 0.2 * avg(l_quantity) as temp_avg         from                lineitem         group by l_partkey        ) as tempwhere        p_partkey = l_partkey        and p_brand = 'Brand#23'        and p_container = 'MED BOX'        and l_quantity < temp_avg        and p_partkey = temp_l_partkey;三、测试结果1.空间利用率比较GaussCitus2.语法兼容性比较gauss支持声明式创建分区表,建表指定分布列,自动发送数据到datanodeCREATE TABLE ORDERS(    O_ORDERKEY       BIGINT NOT NULL  , O_CUSTKEY        BIGINT NOT NULL …)distribute by hash(O_ORDERKEY)PARTITION BY RANGE(O_ORDERDATE)(    PARTITION O_ORDERDATE_1 VALUES LESS THAN('1993-01-01 00:00:00'),    PARTITION O_ORDERDATE_2 VALUES LESS THAN('1994-01-01 00:00:00'),...);citus支持声明式创建分区表,但建表与建分区需独立执行,需手动分发数据CREATE TABLE ORDERS(    O_ORDERKEY       BIGINT NOT NULL  , O_CUSTKEY        BIGINT NOT NULL …)PARTITION BY RANGE(O_ORDERDATE);CREATE TABLE ORDERS_1   PARTITION OF ORDERS    FOR VALUES from ('1992-01-01 00:00:00') to ('1993-01-01 00:00:00' );CREATE TABLE ORDERS_2   PARTITION OF ORDERS    FOR VALUES from ('1993-01-01 00:00:00') to ('1994-01-01 00:00:00' );SELECT create_distributed_table('ORDERS', 'o_orderkey');3.join支持Gauss:支持(1)分布列进行join(2)一个分布列,一个不是分布列join(3)两张表都不是分布列joinCitus:支持(1)分布列进行join(2)一个分布列,一个不是分布列join,需要Set citus.enable_repartition_joins to on,否则报错(3)两张表都不是分布列join,不支持外连接,报错如下4.运维管理Gauss有cm(cluster manager)对进程故障检测,进程自拉起,主备高可用,集群拓扑等功能提供命令与视图Citus对worker管理提供了sql支持,高可用依赖postgres实现,有待进一步了解5.性能比较关于性能差异的猜想:a. gauss配置了主备同步,可能会牺牲部分性能保障数据安全b. citus除了实例级别的数据切分,还有数据切片机制,能更好地并行计算四、总结citus作为pg的一个插件,跟随pg主线演进,可以结合社区新特性,性能不俗,能够弹性扩展;但是作为插件也天然存在局限性,例如对join的支持,与pg运维管理相关功能的配合,产品对使用者的能力要求比较高。gauss集群建设更加完善,提供了cm(cluster manager),om(operation manager)以及更上层的维护功能,提供集群高可用,在分布式场景能够完全兼容单机sql语法,对使用者更加友好。五、遗留了解Citus分片切分的粒度与机制,了解分片是否会产生大量小文件问题了解citus分布式计划实现原理,了解citus对需要先redistribute进行join限制的原因
  • [技术干货] 第五讲:鲲鹏性能分析工具基础知识读书笔记-Devkit调优
    1、DevKit系统性能分析工具DevKit性能分析工具正是为了满足上述需求而设计,DevKit性能分析工具是一个工具集,包含:系统性能分析工具、JAVA性能分析工具、系统诊断工具,本文将重点介绍系统性能分析工具。系统性能分析工具主要针对服务器系统(包括:硬件、OS、应用软件等)进行性能分析,能收集服务器硬件、操作系统、进程/线程、函数等各层次的性能数据,分析得出系统性能指标,定位到系统瓶颈点及热点函数,给出优化建议,辅助用户快速定位和处理软件性能问题。相对业界性能分析工具,该工具主要增强实现如下能力:· 提供软硬件结合分析能力,采集和分析硬件性能指标,以及硬件性能在软件层的表现,让用户更加全面的了解整个系统的运行状况。· 系统化的分析建立各层指标之间的关联关系、并以用户视角呈现这些指标和关系,方便用户更易于发现问题。· 结合华为在鲲鹏处理器上的性能优化经验,给出优化思路和建议,帮忙用户快速定位和修复问题。2、DevKit系统性能分析工具的功能?系统性能分析工具提供3大模块\9大主要功能:(1)通用分析: 通用分析:采集和分析整个系统的软硬件配置信息、识别性能瓶颈,针对异常指标项提供优化建议(包含全景、进程/线程和热点函数分析);(2)系统部件分析· 系统部件分析:针对系统主要部件(如:处理器、内存、存储等)暴露的硬件指标,结合系统软件运行指标,分析各部件的性能瓶颈(包含微架构、访存和I/O分析);(3)专项分析· 专项分析:针对特定性能问题进行专题分析(包含资源调度、锁与等待和HPC分析)。
  • [其他干货] CUDA编程(六)存储单元
    CUDA的存储单元包含以下类型:如下表所示:名称位置用途使用方法限制备注Register寄存器GPU的SM上存储局部变量每个SM上有成千上万个一个线程最大数量为256个需要省着用线程私有,最快线程退出则失效Shared memoryGPU芯片上实现Block内的线程通信,目前最快的多Thread沟通的地方__shared__修饰符需要__syncThreads()同步分为32个banks需要省着用,会影响活动warp数量可被1个block所有thread访问,次快高带宽,低延迟Local memory存放单线程的大型数组和变量(Register不够时用它)没有特定的存储单元线程私有,速度较慢,速度与Global memory接近Constant memory常量内存驻留在device memory中用于同一warp的所有thread同时访问同样的常量数据,比如光线追踪__constant__修饰符必须在host端使用 cudaMemcpyToSymbol初始化没有特定的存储单元,但是有单独的缓存只读,全局Global memory等同于GPU显存驻留在device memory中输入数据,写入结果全局,速度较慢Texture memory纹理内存用于加速局部性访问,比如热传导模型只读,全局,速度次于Shared Memory(延迟比Shared Memory高,带宽比hared Memory小)Host memory:可分页内存主机端内存使用malloc访问使用free释放不可以使用DMA访问内存页可以置换到磁盘中另一种Host memory:又称:Page-locked Memory,Zero-Copy Memory主机端内存使用cudaMallocHost访问使用cudaFreeHost释放属于另一种Global memory如何使用Shared Memory优化CUDA应用呢?Shared Memory的特点是快的时候特别快,慢的时候特别慢。什么时候快?同一warp中所有线程访问不同的banks或者 同一warp中所有线程读取同一地址(通过广播)什么时候慢?同一warp中多个线程访问同一个bank的不同地址(此时将产生 bank conflict)串行访问请注意:bank conflict发生的原因就是 warp的分配和bank的分配重叠了:如何避免bank conflict,简单的方法是Padding法(好像叫做补边):通过增加一个空列,让bank强行错位,使得每段连续的数据被分配到不同的bank中。具体做法很简单:就是在设置Shared Memory的时候,不设置成 方阵BLOCK_SIZE X BLOCK_SIZE,而设置成 BLOCK_SIZE X (BLOCK_SIZE+1).最后,我们可以使用Shared Memory优化mXn, nXk的矩阵乘 的代码,提高访存的效率。具体方法如下:申请两块 Shared Memory,都是BLOCK_SIZE X BLOCK_SIZE 大小。一个沿着矩阵mXn滑动,一个沿着矩阵 nXk滑动。将 子集的结果累加到 目的矩阵中:具体的代码如下:__global__ void gpu_matrix_mult_shared(int *d_a, int *d_b, int *d_result, int m, int n, int k) { __shared__ int tile_a[BLOCK_SIZE][BLOCK_SIZE]; __shared__ int tile_b[BLOCK_SIZE][BLOCK_SIZE]; int row = blockIdx.y * BLOCK_SIZE + threadIdx.y; int col = blockIdx.x * BLOCK_SIZE + threadIdx.x; int tmp = 0; int idx; for (int sub = 0; sub < gridDim.x; ++sub) { idx = row * n + sub * BLOCK_SIZE + threadIdx.x; tile_a[threadIdx.y][threadIdx.x] = row<n && (sub * BLOCK_SIZE + threadIdx.x)<n? d_a[idx]:0; idx = (sub * BLOCK_SIZE + threadIdx.y) * n + col; tile_b[threadIdx.y][threadIdx.x] = col<n && (sub * BLOCK_SIZE + threadIdx.y)<n? d_b[idx]:0; __syncthreads(); for (int k = 0; k < BLOCK_SIZE; ++k) { tmp += tile_a[threadIdx.y][k] * tile_b[k][threadIdx.x]; } __syncthreads(); } if(row < n && col < n) { d_result[row * n + col] = tmp; } }并将前面 代码中调用矩阵乘的地方:gpu_matrix_mult<<<dimGrid, dimBlock>>>(d_a, d_b, d_c, m, n, k);  改为 gpu_matrix_mult_shared<<<dimGrid, dimBlock>>>(d_a, d_b, d_c, m, n, k); 其余不变。编译,执行:修改blocksize,将其分别改为 16,8,4,再进行统计汇总:矩阵MXN(m)矩阵NXK(n)矩阵NXK(k)blocksizestop-start(ms)100100100321.83286100100100161.2736510010010081.2329210010010043.528651001001006(补测)2.199910010010012(补测)1.34755从上面的结果来看,blocksize为8,16,32时好像差异不大,但是blocksize为4的时候速度降得比较厉害。blocksize为4时,其实并没有发生bank conflict!而只是因为4X4,只有16个线程,而一个warp需要32个线程,所以相当于计算时,有一半算力被浪费掉了,进而速度慢了一倍。专家建议,至少应该NXN>32比较好。将 矩阵从100改为1000试试,但是发现一旦改为1000后,CPU计算可能算不过来了,需要将CPU那部分代码和后面比较的代码屏蔽掉。再重新统计:矩阵MXN(m)矩阵NXK(n)矩阵NXK(k)blocksizestop-start(ms)10001000100032265.10610001000100016228.091000100010008202.3821000100010004518.3151000100010006(补测)386.17110001000100012(补测)246.29
  • [其他干货] CUDA编程(四)Global Memory
    在GPU上,on-board memory包含以下类型:local memory 每个thread一个。线程私有。global memory 每个grid一个。每个thread都可以读。constant memory 每个grid一个。只读。每个thread都可以读。texture memory 每个grid一个。只读。每个thread都可以读。on-chip memory包含以下类型:registers 每个thread一个。线程私有。shared memory 每个block一个,一个block下所有线程都可以访问。HOST内存函数malloc 申请memset 初始化free 释放DEVICE内存函数cudaMalloc 申请cudaMemset 初始化cudaFree 释放请注意,这里函数只返回状态。所以分配的内存地址作为函数参数。HOST《-》DEVICE互相拷贝cudaMemcpy( 目的内存地址,源内存地址,内存大小,cudaMemcpyHostToDevice/cudaMemcpyDeviceToHost/cudaMemcpyDeviceToDevice/cudaMemcpyHostToHost)以矩阵乘为例:CPU的做法是嵌套循环,如上图所示。GPU的做法应该是使用 index( blockIdx和 threadIdx的组合公式)替换原来的下标i,j。这也是一般CUDA程序的套路——把for loop展开成每个线程处理其中的一步。那么,如何使用CUDA将坐标拆开呢?将二维坐标(矩阵)改为 在全局中的索引:需要找到每个线程需要处理元素的位置。ty=线程在y方向的坐标tx=线程在x方向的坐标ty=blockIdx.y*blockDim.y + threadIdx.ytx=blockIdx.x*blockDim.x + threadIdx.xnx=x方向有多少数据。index = ty * nx + tx目的是将高维降为低维。矩阵乘的每个核函数的算法如下:典型的核函数算法代码如下:需要注意:矩阵乘 矩阵M是 mXn,矩阵N是 nXk,这里面需要 矩阵M和矩阵N都有n。否则无法相乘。上代码:matrix_mul.cu#include <stdio.h> #include <math.h> #define BLOCK_SIZE 16 //使用GPU进行矩阵计算 __global__ void gpu_matrix_mult(int *a,int *b, int *c, int m, int n, int k) { int row = blockIdx.y * blockDim.y + threadIdx.y; int col = blockIdx.x * blockDim.x + threadIdx.x; int sum = 0; if( col < k && row < m) { for(int i = 0; i < n; i++) { sum += a[row * n + i] * b[i * k + col]; } c[row * k + col] = sum; } } //使用CPU进行矩阵计算 void cpu_matrix_mult(int *h_a, int *h_b, int *h_result, int m, int n, int k) { for (int i = 0; i < m; ++i) { for (int j = 0; j < k; ++j) { int tmp = 0.0; for (int h = 0; h < n; ++h) { tmp += h_a[i * n + h] * h_b[h * k + j]; } h_result[i * k + j] = tmp; } } } int main(int argc, char const *argv[]) { /* 矩阵A mXn,矩阵B nXk --》矩阵乘计算的结果是 mXk */ int m=3; int n=4; int k=5; int *h_a, *h_b, *h_c, *h_cc; //分配原矩阵的内存 h是host memory cudaMallocHost((void **) &h_a, sizeof(int)*m*n); cudaMallocHost((void **) &h_b, sizeof(int)*n*k); //分配 CPU结果内存 cudaMallocHost((void **) &h_c, sizeof(int)*m*k); //分配 GPU结果内存 cudaMallocHost((void **) &h_cc, sizeof(int)*m*k); //初始化矩阵A(mxn) srand(time(0)); printf("---------------h_a------------------\n"); for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { h_a[i * n + j] = rand() % 1024; printf("%d", h_a[i * n + j] ); printf(" "); } printf("\n"); } //初始化矩阵B(nxk) printf("---------------h_b------------------\n"); for (int i = 0; i < n; ++i) { for (int j = 0; j < k; ++j) { h_b[i * k + j] = rand() % 1024; printf("%d", h_b[i * k + j] ); printf(" "); } printf("\n"); } int *d_a, *d_b, *d_c; //分配 原矩阵的GPU内存 d是device memory cudaMalloc((void **) &d_a, sizeof(int)*m*n); cudaMalloc((void **) &d_b, sizeof(int)*n*k); //分配 目的矩阵的GPU内存 cudaMalloc((void **) &d_c, sizeof(int)*m*k); // copy matrix A and B from host to device memory cudaMemcpy(d_a, h_a, sizeof(int)*m*n, cudaMemcpyHostToDevice); cudaMemcpy(d_b, h_b, sizeof(int)*n*k, cudaMemcpyHostToDevice); unsigned int grid_rows = (m + BLOCK_SIZE - 1) / BLOCK_SIZE; unsigned int grid_cols = (k + BLOCK_SIZE - 1) / BLOCK_SIZE; dim3 dimGrid(grid_cols, grid_rows); dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE); //GPU计算,结果放入h_c gpu_matrix_mult<<<dimGrid, dimBlock>>>(d_a, d_b, d_c, m, n, k); cudaMemcpy(h_c, d_c, sizeof(int)*m*k, cudaMemcpyDeviceToHost); //cudaThreadSynchronize(); //CPU计算,结果直接放入h_cc cpu_matrix_mult(h_a, h_b, h_cc, m, n, k); int ok = 1; for (int i = 0; i < m; ++i) { for (int j = 0; j < k; ++j) { // 比较大小的时候使用 a-b<0.0000000001 if(fabs(h_cc[i*k + j] - h_c[i*k + j])>(1.0e-10)) { ok = 0; } } } printf("---------------h_c cpu result------------------\n"); for(int i=0;i<m;i++) { for(int j=0;j<k;j++) { //矩阵小的时候还可以打印,大的时候就别打了 printf("%d",h_c[i*k + j] ); printf(" "); } printf("\n"); } printf("---------------h_cc gpu result----------------\n"); for(int i=0;i<m;i++) { for(int j=0;j<k;j++) { //矩阵小的时候还可以打印,大的时候就别打了 printf("%d",h_cc[i*k + j] ); printf(" "); } printf("\n"); } if(ok) { printf("Pass!!!\n"); } else { printf("Error!!!\n"); } // free memory cudaFree(d_a); cudaFree(d_b); cudaFree(d_c); cudaFreeHost(h_a); cudaFreeHost(h_b); cudaFreeHost(h_c); return 0; }代码中张小白加上了注释,已经介绍得比较清楚了。我们执行下看看:代码以 3X4和4X5的矩阵相乘,得到了3X5的矩阵结果。这个结果跟CPU计算的结果做了对比。显示Pass表示结果是一致的(其实张小白把两个结果都打印的出来,当然也是一致的)这里面有个小TIPS,就是在调用rand()生成随机数的时候,可以使用srand(time(0)) 做随机数种子,这样下次调用的时候跟这次生成的内容就会不一样。如果去掉这句话,每次执行的结果都是一样的。当然,如果在同一秒同时执行,srand(time(0)) 也会导致同时生成的随机数是一样的。这点需要注意。
  • [其他干货] CUDA编程(三)线程层次
    线程层次的概念:简单说,就是一个grid有多个block,一个block有多个thread.grid有多大,用gridDim表示它有多少个block,具体分为gridDim.x, gridDim.y,gridDim.z。block有多大,用blockDim表示它有多少个thread,具体分为blockDim.x,blockDim.y,blockDim.z。怎么表示thread在block中的相对位置呢?用 threadIdx.x,threadIdx.y,threadIdx.z表示。怎么表示block在grid中的相对位置呢?用blockIdx.x,blockIdx.y,blockIdx.z表示。顺便解释下 https://bbs.huaweicloud.com/forum/thread-194449-1-1.html 中hello_from_gpu<<<x,y>>>(); 中的x和y是什么意思?它们分别表示 gridDim和blockDim。对于下面这个函数:表示gridDim是1,表示grid有1个block,blockDim是4。表示block有4个thread。所以对于上面的核函数,相当于有4个thread分别执行了 c[n]=a[n]+b[n]的操作,n=threadIdx.x在调用的时候,所有的CUDA核都是执行同一个函数。这与CPU多线程可能会执行不同的任务不同。如上图所示,Thread在CUDA core中执行,Block在 SM中执行,Grid在Device中执行。那么,CUDA是如何执行的呢?看下面这张图:如果没有block的概念,要同时进行同步、通信、协作时,整体的核心都要产生等待的行为,如要进行扩展时,扩展的越多等待也越多。所以性能会受影响。 但是有block的概念后,可以实现可扩展性。用block或warp就可以很容易实现扩展了。 如何找到线程该处理的数据在哪里呢?这就要提到线程索引的概念。 以上:假定每8个thread时一个block。具体的公式如下:具体的索引位置 index = blockDim.x * blockIdx.x + threadIdx.x那么一个CUDA程序到底应该怎么写呢?以将一个CPU实现的代码转换为GPU为例: CPU的实现过程大致如下:(1)主程序main:先分配 源地址空间a,b,目的地址空间c,并生成a,b的随机数。然后调用 一维矩阵加的CPU函数。(2)一维矩阵加的CPU函数:遍历a,b地址空间,分别将 a[i] 与 b[i]相加,写入 c[i]地址。这个时候,请注意是要显式地进行for循环遍历。那么,GPU该如何实现呢?(1)主程序main:因为GPU存在Host和Device内存,所以先申请host内存h_a,h_b,存放a,b的一维矩阵的内容(也可以生成随机数),并申请host内存h_c存放c的计算结果。然后申请device内存,这个时候,需要申请 d_a,d_b两个源device内存(cudaMalloc),以及d_c这个目的device内存(cudaMalloc)。将h_a和h_b的内容拷贝到d_a和d_b (显然需要使用 cudaMemcpyHostToDevice);然后调用核函数完成GPU的并行计算,结果写入h_c;最后将d_c的device内存写回到h_c(cudaMemcpyDeviceToHost),并释放所有的host内存(使用free)和device内存(使用cudaFree)。(2)核函数这里就是重点了。核函数只需要去掉最外层的循环,并且根据前面 的index写法,将i替换成index的写法即可。如何设置Gridsize和blocksize呢?对于一维的情况:block_size=128;grid_size = (N+ block_size-1)/block_size;(没有设成什么值是最好的)每个block可以申请多少个线程呢?总数也是1024。如(1024,1,1)或者(512,2,1)grid大小没有限制。底层是以warp为单位申请。 如果blockDim为160,则正好申请5个warp。如果blockDim为161,则不得不申请6个warp。如果数据过大,线程不够用怎么办?这样子,每个线程需要处理多个数据。比如对于上图,线程0,需要处理 0,8,16,24 四个数据。核函数需要将每一个大块都跑一遍。代码如下:这里引入了一个stride的概念,它的大小为blockDim.x X gridDim.x 。核函数需要完成每个满足 index = index + stride * count对应的相关地址的计算。范例1:体验indexIndex_of_thread.cu#include <stdio.h> __global__ void hello_from_gpu() { //仅仅是在原先代码的基础上打印 blockIdx.x 和 threadIdx.x const int bid = blockIdx.x; const int tid = threadIdx.x; printf("Hello World from block %d and thread %d!\n", bid, tid); } int main(void) { hello_from_gpu<<<5, 5>>>(); //记得加上同步,不然结果会出不来。 cudaDeviceSynchronize(); return 0; }Makefile:TEST_SOURCE = Index_of_thread.cu TARGETBIN := ./Index_of_thread CC = /usr/local/cuda/bin/nvcc $(TARGETBIN):$(TEST_SOURCE) $(CC) $(TEST_SOURCE) -o $(TARGETBIN) .PHONY:clean clean: -rm -rf $(TARGETBIN)编译并执行:范例2:完成一维向量计算:addvectorAdd.cu#include <math.h> #include <stdio.h> void __global__ add(const double *x, const double *y, double *z, int count) { const int n = blockDim.x * blockIdx.x + threadIdx.x; //这里判断是防止溢出 if( n < count) { z[n] = x[n] + y[n]; } } void check(const double *z, const int N) { bool error = false; for (int n = 0; n < N; ++n) { //检查两个值是否相等,如不等则error=true. if (fabs(z[n] - 3) > (1.0e-10)) { error = true; } } printf("%s\n", error ? "Errors" : "Pass"); } int main(void) { const int N = 1000; const int M = sizeof(double) * N; //分配host内存 double *h_x = (double*) malloc(M); double *h_y = (double*) malloc(M); double *h_z = (double*) malloc(M); //初始化一维向量的值 for (int n = 0; n < N; ++n) { h_x[n] = 1; h_y[n] = 2; } double *d_x, *d_y, *d_z; //分配device内存 cudaMalloc((void **)&d_x, M); cudaMalloc((void **)&d_y, M); cudaMalloc((void **)&d_z, M); //host->device cudaMemcpy(d_x, h_x, M, cudaMemcpyHostToDevice); cudaMemcpy(d_y, h_y, M, cudaMemcpyHostToDevice); //这个是公式。记住就可以了。 const int block_size = 128; const int grid_size = (N + block_size - 1) / block_size; //核函数计算 add<<<grid_size, block_size>>>(d_x, d_y, d_z, N); //device->host cudaMemcpy(h_z, d_z, M, cudaMemcpyDeviceToHost); //检查结果 check(h_z, N); //释放host内存 free(h_x); free(h_y); free(h_z); //释放device内存 cudaFree(d_x); cudaFree(d_y); cudaFree(d_z); return 0; }Makefile-addTEST_SOURCE = vectorAdd.cu TARGETBIN := ./vectorAdd CC = /usr/local/cuda/bin/nvcc $(TARGETBIN):$(TEST_SOURCE) $(CC) $(TEST_SOURCE) -o $(TARGETBIN) .PHONY:clean clean: -rm -rf $(TARGETBIN)编译后执行: