前期准备

寒假开始自己学了一些基础知识,包括 CUDA 编程,MPI 编程,OpenMP 和 OpenCL 的基本用法等等,其间完成了 Coursera 上的 Heterogenous Parallel Programming 课程。由于之前完全没有并行计算的背景知识,所以学得还是比较痛苦的。

大概在一月份的时候 PyFR 和其他两个应用放出,不过一开始由于不熟悉训练集群环境,卡到了 Python 部署上,所以寒假在 PyFR 上几乎什么都没干。

三月份开学后,突然间进入高速备战的状态,在第一次组会前终于把环境配好了,然后基本每周一次组会,还是非常累人的。话题从环境部署到 Tuning 到 Performance Characteristics 到 Optimization。中间清明节假期把训练集群的 CentOS 换成了 Gentoo,然后话题又重新回到环境配置了。

作为系统管理(酱油)之一,从摸索以前留下的 CentOS 环境,学找到 Intel, NVIDIA 的库的位置,到自己动手装软件、配环境,再到在集群上装 Gentoo 等,我感觉还是学到了很多的。

应用部署

我搞的这个应用应该是比较“奇葩”的一类,首先是架构奇特:Python 写的 Controller, 读取用户的配置文件和输入数据,然后调用用 mako 模板语言写好的 solvers,然后做一些翻译后 feed 给后端接口(ctype, PyCUDA, PyOpenCL),后端接口再做一次翻译,给 C Compiler,或者直接调用 Python 的 API,从而调用后端 (CPU, GPU) 干活。

部署上的经验,就是一定要重视 local installation 和 system-wide installation 的区别。我在配 CIL 时也被这个坑了。对于集群来说,每个节点之间都是相对独立的,share 的部分很有限(NFS)。

为何不共享同一个系统镜像呢?想象一下,假设几台物理上隔离的机器用的是一套系统,那么问题可能是:

  • 灾害不好隔离
  • 权限不好控制
  • 配置不够灵活(假设我只有4块 GPU 卡,但是有8个节点)

所以,要是想让一个软件无差别地运行在多个环境里,软件自身首先要有好的架构,尽量减少对具体环境的依赖(PyFR 用了很多上游的 binding 库,并提供对多种运算环境的支持)。

继续说安装,怎么做 Python 本地安装呢?有一种叫做 virtualenv 的东西,但是真的是很难用,至少在当时的训练集群环境上是这样的;其次 pip 也是一个很麻烦的东西,由于难以在集群上科学上网,所以下包很困难,但是假如我想要用一个 pypi mirror,配置怎么做又是一个问题。其次,多节点同步的话越简单、越直接越好,虚拟化的层次多了后权限和 PATH 就会成问题,然后 mpi 就会挂等。

其实,手动编译的话,就可以减少很多自动化带来的麻烦。首先是 Python 环境编译。Python 本质上就是 binaries,libraries 和一些管理工具。编译好 Python,就可以用编译好的 Python 去运行 pip 的安装脚本(不过 Python3 貌似是绑定的?),装好 pip,再用这个 pip 程序去安装包。你再手动编译安装其他的 Python 包,也要用这个本地 python

然后,假如说你没有其他需求,其实就可以把这个本地安装的 Python 里面的某些程序链接到 /usr/bin 里面。

应用调试

细节视应用而定,千差万别。不过基本的概念,大概可以如此总结:首先什么叫调试?

用惯通用系统的我们,可能完全感受不到这个需要。事实上,面向大众的软件,也需要在 target machine 上做调试,以期达到最优效果。然后在把基本上调至最优的软件直接卖给客户。OS X 为啥可以做到很多非常难以置信的事情,我觉得一个固定的 target machine 就是很关键的一点。

从源代码到和系统、库完美合作的科学应用,往往需要考虑三方面的因素:

  • 编译器(e.g. GNU, Intel, PGI, etc),负责生成高效的代码。
  • 数学库(e.g. Intel MKL, OpenBLAS, ATLAS etc.),负责提供已经最优化的最基本的数学函数,例如矩阵运算等等。
  • 并行方法(e.g. OpenMP, OpenCL,CUDA etc.),也可以分为 CPU, GPU 和 MIC。
  • MPI 实现 (e.g. OpenMPI, Intel MPI, pmpi etc),主要涉及到多节点通信的效率。

所以,一个 naive 的方案,就是排列组合不同选项,当然,其实根据硬件环境也可以做一些预判。

除了大方面之外,还有细节,比如编译优化程度,rank allocation 等等。

不过,以上还都属于基本调试,如果是高级调试,在我看来,应该是能做到针对不同的硬件条件(节点数, vendor, bandwidth, energe threshold etc),预测性地给出相对不错的配置,同时也有能力在最短时间内根据实际硬件摸索出最好的配置。

Culture Shock 集合

搞超算确实给了我很多 culture shock:

  1. Intel 其实写了很多不错的软件(编译器、通信库、数值计算库等)
  2. 超算用的也是普通 Linux 发行版(这个并不是很准确,只是我们这种小型集群可以直接用 server 版的发行版;稍大一些的商业集群很多方面都要定制,尤其是很多用户的那种,这类系统环境和『正常』Linux 已经有很多不同了;最后,那些顶尖超级计算机,一般都会自己定制操作系统,内核上甚至都会有不少修改)
  3. 一个机器的内存可以比硬盘都大
  4. 计算机间通信这种事情可以以百G每秒算(高速互联)
  5. 低年级本科生可以随便玩很贵的东西
  6. 程序不仅要写得好,还要跑得好(其实体现了科学计算程序还不够成熟的一面,如果对给定问题 $P$, 有 $C_1, …, C_n$ 种配置都可以得到正确结果,那么程序应该自动选择(近似)最佳配置而不是让用户手动调配。不过其实已经有很多参数内化了。)
  7. NVIDIA 不仅仅是和 EA 勾搭的奸商
  8. 超算机器重启的方式和普通机器并没有区别(也是针对我们的小型集群而言的,大型机群重启应该是一个比较复杂的流程,可能要更多考虑供电、备份、网络通信恢复等问题)。

前往法兰克福

比赛是在德国法兰克福(Frankfurt am Main)举行的。这是一个位于西南部的城市,但是其气候仍然非常干燥、寒冷,白昼很长(当时接近夏至)。从下机的一瞬间,就感受到欧洲国家的那种安静的氛围。德国的大多数人都非常礼貌,也比较忙碌。然而也有一些比较有『特色』的地方,譬如很多烟头、酒瓶在地上扔着(这让我感受到工薪阶级的真是存在,并不是每个人都可以生活地『文明』),譬如很多人都骑车出行,其中不乏西装革履者,基本上除了自行车,就是非常夸张的摩托车,亦或是(本土产品)奔驰、宝马、奥迪等。法兰克福这边晚上治安也很不错,很少有那种据说在美国很常见的『街头混混』。这个城市总体感觉还是非常好客的,食物的种类很多而且有很多语言的菜单,时常见到土耳其人,韩国人,也有中国人,还有黑肤色的、棕肤色的人,大家似乎生活还是比较和谐的。

语言方面,本地人的 hallo 应该是为数不多可以一下子反映过来的了,剩下的英语交流因为我和他(她)的英文都比较渣而显得非常尴尬。与会的也有俄罗斯籍、印度籍,以及英国(比如 PyFR 的两位作者,纯英伦腔并不易理解)等其他欧洲国家。不过似乎技术胜于言辞,大家沟通只要大概明白意思并不会太过细究。 总而言之,德国的工业、现代化和古典建筑似乎很和谐地放到了一起,西装革履和街头涂鸦、滑板、摇滚、刺青也似乎很和谐地放到了一起。然而我在这里所住时间太短,并不能探清其中细节。另,德国有很多非常漂亮的妹子(然而很多都比我高)。

PyFR (pronounced as “Pi far”)

我现在印象最为深刻的一瞬间就是 PyFR 两位作者(主要作者 Freddie Witherden 和实验室老板 Peter Vincent)来到我们的 booth 问候我们应用跑得怎么样,我们的老板叫我们一起拍照,然而 Freddie 一直在看我们贴出的有关 PyFR 前期研究情况的 poster,下面有一小部分被我们包挡住了,他非常急切地自己在那里把我们的包放到一边去看那几行非常惨淡的 top3 crucial optimization. 然后我问他 “Anything wrong here?”,他只是笑了笑 “Very good” 而没有任何问题,我在其中感受到的是他的失望。

他身材瘦小,但是着装正式,看起来很腼腆,但是假如你问他问题就会非常热情地解答。我在问他一个极其愚蠢的问题后,他还热心解答问题中并帮我拓展了知识(standard output is buffered, but standard error is not buffered),还演示了如何重定向到文件(虽然事实上是 instructions 略有误导之嫌,但是终究还是我没有思考有关 IO 流的事情,之前也基本很少用到标准输出重定向之类的)。

从今年 2 月份开始我就在 watch PyFR 的 GitHub repo,可谓旁观了从 0.2.6 到 1.0.0 的整个开发过程。然而终究只是旁观,我甚至都没有写一个 issue !

虽然我听人们说搞物理等学科的人代码写的稀烂,但是从 PyFR 这个项目而言,我看到的是一个有着良好代码结构和风格、注重工程技术的开源项目。PyFR 基于 Python,强调 portability 和 compact code,接口 MPI, OpenMP, CUDA, OpenCL 等等多种成熟的技术。要引用这么多 packages 并且封装成一个统一的 API 和数据结构来保证 heterogeneous and scalable 绝非稀烂的代码可以胜任。Mako 模板语言、plugin 架构、configurable input 都是对于这个框架的可拓展性的极好说明。虽然 PyFR 解决的是一类特定的问题(advection/diffusion on unstructured grids),但是代码结构却没有限于要解决的问题。

另一方面,PyFR 的 idea 来自于 2007 年 Huynh 介绍的 Flux Reconstruction 方法(然而当时我的物理很渣,也没怎么学过 numerical methods,基本看不懂),但是可以看出的是,Huynh 发表文章时并未想过 FR 方法可以被如此成功地实现在 GPU 平台上。设想,当时 Vincent Lab 的人看到这篇 FR 的论文,如何分析出 element locality 的特性,如何将一种计算方法映射为 kernels,并且分为两部分(Matrix computation kernels & point-wise operations kernels),其中矩阵运算做优化和分解,利用高效 BLAS 库和 Mako 的模板语言 + 代码生成技术解决。从 idea, formulas 到 implementations, benchmarking, profiling, engineering,可见需要敏锐的洞察,也需要良好的工程能力。而且,最新版已经可以 pip install pyfr 了,可见作者用心。

Competition

Setup

话题回到赛场上,第一天 setup 的主要问题是花了很久等华为的机器被运到 booth 前,然后安节点、布线、组网、配置显示器,最后上电。第一天基本上重启很多次,Infiniband 处于半死不活的状态。第二天 setup 基本上都在修 IB 网络,各种 errors,latency 大的不行,IPoIB 基本上废掉,然后怀疑是硬件问题,于是从 Mellanox 展台搞了另一台 EDR 交换机上去终于恢复了一些,但是还是不稳定,latency 高。这直接把 HPCAC 的 Pak Lui 和 David Cuo 惊动了,赶紧过来帮我们一起修。后来各种刷交换机 OS,升级网卡固件,然而并没有什么用。最后重新拔插所有节点的网卡,LAMMPS 性能才恢复到原来的测试水平。最后 HPCC 和 PyFR 也是草草测了一遍就算完成 setup 了。此时悲剧的是,octopus 突然发现需要新版,于是现场配,结果 ld 总是失败,终于把链接问题解决后,编译出来的程序一跑就各种段错误之类的。事实上,这个问题直到 day2 announcement 一小时前才解决,原因是 MPI 库的版本乱掉了。

关于部署问题的经验教训

首先,nightly build 和 updating 非常重要。尤其纯靠 Makefile 而没有语言级别的 module 系统支持的程序,要是一直用原先 build 出来的 binary 而不经常 build 的话,很可能现场捉急。

其次,集群环境要小心维护。包括 MPI 库、数学库等其他库的一致性,尤其在 CentOS 这种源里基本没有几个 HPC 相关库的环境下,都要手动维护库。可能要考虑到的问题包括:

  • 有几个版本(包括多种实现,如 OpenMPI 和 Intel MPI,也包括某个实现的不同 release)?哪个是默认版本?版本之间的文件会不会交叉?
  • 通过什么方式指定路径?PATH/LD_LIBRARY_PATH 还是 symbolic link 还是编译器参数 -I
  • 库放在什么地方?是集群公用还是节点私有?有没有路径的 convention?
  • 库是不是给全了?有没有什么需要手动编译的动态文件/静态文件?

最后,队伍里不能有完全只负责应用而不参与系统维护的人,万一出了紧急事件不好处理。

Day One

上午一方面在远程主机上尝试编译 Octopus,一方面 HPCC 测试。下午 3 点 kick-off,上来就是从整个会场的 booth 里的隐藏地点找分散的六件队服。找齐了队服就可以领取 U 盘,里面有 HPCC 和 HPL(即 Linpack)的说明,以及一个待填的 hardware config 表。

事实上硬件配置不允许改的逻辑是:day1 提交的 hardware config 里面写的东西是根本依据,然后 day2, day3 组委会会来人突袭检查硬件和拍照,防止作弊。但是这个明显不够严格,我 day1 的时候跑 HPL 事实上可以把所有 CPU 节点都下掉,然后用 GPU 来跑,然而我再改硬件 config,day2, day3 都用改过的。不过,硬件配置还是能说明一定问题的,至少改动太大可以看出来。这里可以看出主办方对于参赛队伍的品质还是比较信任的。

不过 HPL 确实是跪了,毕竟 2 个 k80 节点还是很难跑出好看成绩的;HPCC 也不理想,主要是集群基础功耗高的不行,啥都不干集群放在那里 8 个节点就要 1400 W,直接限制了提高 CPU 频率以及用的核心数的程度。

事实上 k80 的零功耗选项是可以通过对 BIOS 做二次开发实现的,然而华为方面的联系人实在是有些敷衍。k80 的每个核心闲置功耗要 25W,每个节点就是100W,仍然是比较可观的。除此之外,遗憾之处还在于液冷系统没有配上,就 PyFR 为例,风扇系统在 GPU 跑到一定程度后就会飚起来,一下子上去一两百瓦的功耗,然而液冷系统的功耗就要小很多,而且声音也没有那么夸张。会场上还见到把整个 SoC 跑到非导液体里的。

Day1 晚上比较开心的是有个乐队在巡场演奏,非常带感;以及 HPCC 交了后我没事干跑去和那些5点过后着急回家的展台人员瞎聊,有个做 Percona 开源集群解决方案的,那个人解答我的问题时手舞足蹈,非常有趣,我当时还问他了 “How GPL would affect your consulting business?”,他说 “Just let customers download our open-source softwares, and then we will come at service. We don’t sell the products, we sell services”。

Day Two

Day2 就比较跌宕了。PyFR 发来下两个一共接近 2G 的压缩包算例,我差点吓坏,打开一看原来是他们已经算了一部分结果让我们接着继续算。

然而事实上训练过程我从来没有用过 pyfr restart 命令。除此之外,还有对 post-processing (用 paraview 的 pvpython 工具运行 vis.py,通过调用 API 的可编程方式产生最后的图片)的强调,训练也基本没有太多管这个部分。事实上,个别队伍没有成功完成最后处理的部分。我们也是用了两台 PC 和集群来生成,然而我的 Macbook Pro 差点就挂机了。PyFR 的任务还有一个问题是 runtime 输出,PyFR 的进度条是 stderr, 然而我之前并没有关注这一点,所以试算时并没有重定向到文件,导致白跑。Octopus 和神秘应用 Graph500 都不理想。T 大的 Graph500 比我们好 50 倍,不过 Octopus 终于在最后关头解决问题,算例运行还算可以。

Day Three

Day3 第一个任务是 LAMMPS,rzy 同学采取了非常稳妥的战略,然而 GPU 版本编译不动。不过,事后发现 LAMMPS 还是有些链接上的问题,不过很多队伍都栽到这个问题上了。

然而下面的 Secret Mission 是 PyFR,形式和前一天类似,都是把一个跑了大部分的算例 restart,然后做 post-processing。然而这个算例的特点是 IO 密集,500 MB/s 的 disk write 频繁出现。Secret mission 的要求是在 45 min 内用最低 peak power 跑完,可以随便搞硬件配置。硬件配置的想法并没有问题,直接单节点单卡 k80 跑,尽管计时开始前2分钟才把下了 K80 的节点抬上去,还有两个螺丝没有拧上去,机器指示灯还闪烁着红光,非常吓人。然而由于去年 USTC 因为配置压缩太过激进导致没有在规定时间跑完,这次我被吓的失去了理智,用了最保守的方案,没有调整 GPU 的功率,也没有把 CPU 功率压倒最低,也没有把风扇控制到最低,就怕机器出问题。然而 peak power 在 k80 温度触顶后由于风扇加快立马冲到 600 W,在各个队伍里变成中流水平。

然后就是 interview,我也被 Vincent 诘问到了,到底还是对于 PyFR 的思考不深刻,露馅了。整个比赛结束后确实有些士气低落,感觉要倒数了。

比赛结束了,事实证明,我们确实很水,但是终究拿到名次也算是幸运。我们应用第三,CHPC 第二,T 大第一,ray 同学非常激动。Linpack 是一个印度队伍,4 个节点,三台 k80,Linpack 10.X TFLOPS。Tartu 貌似是最受欢迎队伍。

T 大很厉害,Xeon 2699 完爆 2690 还带着 k20 备用。据说还有液冷。况且 Ray 同学非常厉害,加之 ASC 上他们夺冠后士气很高,经验丰富,而且不少大四学生可以突击训练而不必顾忌考试周的问题。其策略从最后 PyFR Secret Mission 可以看出来是“要么 0 分,要么第一”。没有丰富经验、冷静判断和胆识显然是不可能沉住气的。

其实,最后的 Interview 也算是帮我们对整个赛程做了一个回顾。包括:

  1. team management
  2. the difficulties you have met
  3. the biggest achievements
  4. what would you do if you come back next year
  5. what do you want to say to the next year’s team crew

最后总结一下三个方面:

  1. 我学到了什么
  2. 遗憾是什么
  3. 参与这类比赛的关键是什么

我学到了什么

  1. 并行计算基础知识,包括 CUDA, MPI, OpenCL, OpenMP 等的计算模型和编程基础,其中 CUDA 因为参与了 Coursera 课程而相对好一些;MPI 也跟着一个博客研究过教学型代码而学得好一些,其他就大多是走马观花。
  2. 高性能计算基础知识,包括 benchmarking, profiling, performance analysis, 至少对于集群、节点、Infiniband 通讯、memory-bound, computation-bound, accelerator 等概念有了基本认识
  3. Linux 系统 bootstraping & 运维:这点要特别感谢 rzy 同学,教了我不少。先是重装训练集群,然后还接触了科大超算系统(batch processing,和我们的训练集群非常不一样);最惨烈的是在深圳先研院装系统,从 0 搭建系统,发现各种硬件、尤其是网络部分,都是有问题的,绕了不少远路,Linpack 跑起来风扇噪音之大吓得旁边的办公室都关门了。后来到了德国 setup 和保证系统稳定性也是学习了不少东西。Linux User 和 Linux Sysadmin 的视角是完全不一样的(当然,和 Linux Hacker 的视角也是不一样的)。
  4. 配置环境、编译软件:Python 环境的部署并非易事。各种动态库要找齐也是不容易的。一般情况下,package manager 可以搞定问题,但是小众的软件或者是有非常灵活的软件配置(即,你可以换数学库,换 MPI 等)却并没有那么简单。然而绝大多数的高性能应用,都具有这个特点,加之非常特殊的硬件环境,所以部署起来还是比较复杂的。

遗憾是什么

  1. 没能取得更好的名次。虽然从实力上看,输得并没有问题,得了第三也应该侥幸,但是从比赛过程上讲,至少我个人还是有很多愚蠢的举动和决定,导致了不利的局面。
  2. 没能有更好的团队。领队老师确实非常认真,对比赛也投入了很多心血。然而过多的指导并不一定是好事,有时候会分散比赛时队员的注意力,训练周期也被拉的太长导致后期其实大家都有些疲惫了。另一方面,这类的比赛应该更多由学生主导,虽然按照我们团队的情况其实并没有一个特别具有领导力的同学。
  3. 硬件不够理想。虽然华为提供的已经是生产线上最好的产品之一了,但是毕竟是产品,而且时间仓促,液冷和 K80 的零功耗都没有做到。高密度的设计导致散热成为一大问题(比如 GPU 两块 K80 一个靠近风扇一个远离,整个性能构成短板效应),2690 也并非市面上最好的 CPU 解决方案。GPU 服务器也并不兼容最新的 Infiniband EDR 网卡,导致非对称的网络结构。那个所谓的高密线接口也很容易损耗,导致监控有时比较蛋疼。
  4. 我自己也没有更多的『有效投入』。准备 PyFR 时太多时间费在 benchmark 上了,其实不是参赛环境的话,benchmark 说明的问题很有限。而且当时也并没有什么方向,就是纯粹看数字。假如我再搞一次的话,可能会先期投入更多时间研究代码、和作者多沟通多问问题、多研究这个应用的背景和意义、多理解相关的知识比如 numerics, compute-kernel, CFD,多套一些算例等等。拿到系统后再着重研究最佳跑法,研究硬件软件的适配等等。

参与超算竞赛的基本素质

  • 硬件优良。最好是在产品线基础上进行定制,注重稳定性测试,注重硬件的新旧和质量。
  • 每个应用主要负责人都有系统管理经验,参与系统构建和日常运维,对于系统管理有内部的标准。
  • 对于突发情况有应变能力,对于外界干扰有免疫力
  • 对于科学计算应用要有多方面认知,且要懂得如何控制性能。