本文翻译自Continuous Integration with Arm Forge
为了提高软件,特别是有多人参与的大型软件,的整合性和质量,持续集成(CI)在软件工程中广泛使用。随着代码的增多,优化的深入,高性能计算(HPC)应用也可以通过Jenkins之类的的CI框架来确保软件符合精度和性能的需求。
CI工具其实就是管理项目的在一堆代码和依赖资源上进行并行构建,测试的机器人。他们可以和版本控制 软件,构建系统或者单元测试框架进行对接,以在开发流程中实现更好的集成系统。最后它还可以收集并聚合测试数据来显示应用的健康程度。
我们将在本文中介绍如何将arm Forge集成到CI系统中,以提供更多的信息和更高的效率。
Jenkins持续集成系统
我们创建并配置了一个Jenkins项目用来构建我们开发中的应用,并进行非回归测试。这可以通过脚本实现,我们使用了一个叫做shell2unit的工具将Junit测试报告中的输出,这样这些信息就可以通过接口暴露出来。
这个项目执行应用输出的验证:
result=$(grep "Checking results" output.log | awk -F": " '{print $2}') juLog -name=check_results test "${result}" == "passed" perf=$(grep "Elapsed time" output.log | awk -F" " '{print $3}') slow=$(echo ${perf} '>52' | bc -l) juLog -name=check_performance test ${slow} -eq 0
项目配置成周期性地构建,Jenkins显示每次构建后运行失败的测试。这给出了项目的状态趋势:如果一次次构建后,越来越多的测试失败,乌云表示存在精度和性能问题!
我们需要修理这些检测到的问题,但目前的测试并不能告诉我们程序错在哪里。目前的代码顺序执行,当我们用OpenMP对他进行并行化的时候,我们会遇到更多的问题。我们能不能通过修理错误来提高项目质量?能不能不重载测试代码就可以定位错误?
增加测试覆盖
arm Forge中的arm DDT是一个强大的针对多线程,多进程应用的交互式调试器。它有GUI交互模式,也有基于命令行调试的离线模式。离线模式除了交互模式中的所有功能,还可以通过调试器传入参数采集需要的信息。调试的结果以文本或者HTML格式输出供后续处理。
调试器有许多主动侦测问题的功能,比如我们用内存调试器功能来探测内存泄漏问题。命令行如下:
ddt --offline --output=report.html --mem-debug ./mmult.exe
该命令以离线方式执行mmult.exe,同时打开了内存调试功能。运行完毕后生成report.html。我们可以使用hxselect之类的工具对文件进行分析,如果发现内存泄露就触发失败
for i in ${SRC_FILES}; do leak=$(cat report.html | /opt/utils/html-xml-utils-7.6/install/bin/hxselect textarea | grep $i | awk -F"," '{print $2}') if [ ! -z "${leak}" ]; then echo "Memory leak found in: " ${leak} " nb_leak=$(( ${nb_leak} + 1 )) fi done juLog -name=check_leaks test ${nb_leak} -eq 0
性能回归测试
Arm Froge里还有一个针对并行程序的轻量级交互式分析器,这样很容易通过扩展CI测试集来覆盖性能下降问题,通过arm MAP,我们的应用可以在离线模式下进行分析:
map --profile --output=mmult.map ./mmult.exe
输出可以后面在GUI中打开,命令如下
map mmult.map
结果也可以导出为JSON格式,供Jenkins之类的第三方工具进行后处理:
map --export=mmult.json mmult.map
现在,我们可以编辑我们的工程来处理生成的JSON文件。因为我们准备利用OpenMP对应用进行并行化,所以我们想知道多线程的性能。我们可以在Jenkins扩展中使用Python对JSON格式信息进行处理和并将结果存储为CSV文件,后面进行图形化展示。
data = json.load(open('mmult.json')) mylist.append( [ "Serial code", "OpenMP code" ]) mylist.append( [ sum(data['samples']['activity']['openmp']['normal_compute'])/float(num_samples), sum(data['samples']['activity']['openmp']['openmp'])/float(num_samples) ] ) with open('results.csv', 'wb') as myfile: wr = csv.writer(myfile, quoting=csv.QUOTE_ALL) wr.writerow(mylist[0]) wr.writerow(mylist[1])
提高生产率
现在我们的测试框架就位,我们来看看arm Forge提供了哪些信息。对于我们的第一版基于OpenMP的应用,我们发现有四个测试中的两个测试(红色)失败了。
如果我们点击链接获取更多信息,Jenkins会报告arm DDT报告了一个内存泄露问题。我们可以通过分析调试报告将测试结果可视化:
Memory leak found in: "main (mmult.c:37)"
我们也可以在报告中看到
这个信息对于定位和修理程序问题非常有用。经过几次代码提交,内存泄露测试失败的问题修复了。
现在我们来看看第二个测试失败。我们看到并行化代码让程序运行的更慢。我们可以通过arm MAP的性能图表理解为何变慢。
分析器需要收集数据,我们看到“OpenMP overhead in region”占据了30%的时间,只有65%的时间用在OpenMP代码的运算。我们这里有负载不均衡的问题吗?是不是我们的并行粒度过细?打开arm MAP分析器,我们可以了解到其中的性能问题:
GUI界面上用红色标注出最新提交的代码,我们了解到内循环里OpenMP的reduction操作效率很低,右边的时间分解窗口显示100%的时间都花在内存访问上!我们将外循环并行化,提交修改代码
所以测试通过:项目页面又恢复阳光!多亏arm MAP提供的建议,我们得以解决由于应用中因为串行化代码带来得性能问题。
更进一步
本文简要介绍了arm Froge和CI流程进行整合得功能,你也可以通过监控更多的因子(文件系统,GPU,功耗)和众多Jenkins扩展来提高产品开发的效率。
章政同学写的好,赞