ISC 19 回顾

上周,我作为超算队成员去德国法兰克福参与了 ISC 19(全名叫 ISC-HPCAIAC Student Cluster Competition)。很不幸的是,这次我们又得了第二名,赢家是南非的国家超算中心(CHPC)。现在正在回家的火车上无事可做,想到比完发了条票圈说考完要写小作文,那就兑现一下承诺吧。

赛题

ASC 19 结束以后,我们就开始着手准备 ISC 19。这次的题目有:

机器配置

这次的机器配置由浪潮赞助,还是 ASC 用的机型。由于 SWIFT 需要 MPI Rank 数量为 2 的幂,而 CP2K 需要进程数量为 $ab^2$ 的格式,因此我们的选择并不多。综合 benchmark 的需要,我们最终选择了 4 机 16 卡的配置。我们给所有机器都换上了 Intel Xeon Platinum 8270 的 CPU(这里要感谢 Intel 和浪潮),并插满了 768 GB 的内存,也上了双 FDR 的 IB。由于显卡和 IB 卡用完了所有的 PCI-E Slot,并且这次的应用并不比 IO,我们就没有用 4T 的 SSD。我从浪潮寄往现场的 12 台机器拔下来一堆盘,全部插在 i1 上做了软 RAID0(事实上 SATA 背板只有 8 个接口,除去系统盘就剩 7 块),实测读取性能能到 3000MB/s 左右,当作一个快速的共享存储。

在装机器的过程中,照例会出不少锅。与 ASC 时相同,我们又发现了一个坏掉的 PCI-E raiser。而 i3 插满内存以后,反复起不来,更换内存也不能解决,最终只能换一台机器。由于我们事实上带了 12 块 8270,外加 8 张 16G 和 8 张 32 G 的 V100,而在现场又从 NVIDIA 得到了 8 张 32G。因此,我们配置了 6 台可用的机器(最后两台上 16G 的卡)以作出现突发情况时的备用。还好,我们并没有遇到这样的情况。

比赛过程

性能测试

ISC 的性能测试比较神奇,用上了 HPCC 这一个包罗万象的测试,里面有 HPL、DGEMM、PTRANS、随机访问、网络通信等一系列综合的测试,最后组委会会挑选一些项目进行加权评分(而我们并不知道是哪些)。往年我们对其中的一些部分做了优化(比如 DGEMM 移植到 CUDA),但没有取得最好的成绩。今年,hkz 和郑总接手了 HPCC,进行了大刀阔斧的改革。虽然今年看起来规则改了,我们一直不知道我们的更改(比如上卡)是不是合法,而屡次向组委会询问都未果。在比赛开始前一天,我们直接去询问组委会,也被咕咕咕了好几次。其中还发生了几次转折,最后我们得到的答复是可以。

虽然前一天已经花了将近一个小时完整测试过一遍,但跑 HPCC 的时候还是出现了大锅。第一次正式跑了二十多分钟的时候,在某个测试开始的时候功耗瞬间飙升到了 3500 W,浪费了一次免死金牌(第一天可以超两次而不扣分)。而第二次跑的时候,我的一个脚本出现了大问题:它从 PDU 持续读取功耗,如果大于一个很大的值,比如 3500W ,就立刻压下功耗并打开风扇,以防集群因为某些异常状态过热或者超过功耗太多而被断电;但是我们万万没想到(可能是我访问 PDU 过于频繁),PDU 会返回一个错误值,导致脚本在功耗正常的时候被触发。于时跑着跑着突然风扇开始狂转,大家都吓坏了,立刻掐了程序。这时候已经过去半小时了,当我明白发生了什么的时候立刻停掉了脚本,接下来我们才完整地跑了一遍下来(其中的 HPL 功耗被控制得非常死)。当我们在当天最后打算再跑一遍的时候,发现某个测试会随机崩溃,我们第二次花费了四十分钟得到了无效的结果。而它恰好在第一次没有崩溃,我们就提交了第一次的结果。虽然惊险很多,但是我们还是在这一项得到了第一的好成绩。

HPCG 是非常传统的测试,至少要跑半小时,功耗也非常稳定,压到 2950W 左右跑就可以了,它的成绩也基本和卡的数量是成线性关系的。最终,我们用 16 卡跑出了第一的成绩。虽然 NTU 等学校卡都并不比我们少,但可能由于机器特性(他们是 2 * 8 + 2 * 0)和额外功耗开销的问题,最终成绩并没有我们高。

虽然 HPL 不算分,但是我们还是跑了几发,一部分原因是因为 HPCC 中的 HPL 跑得实在是太菜了(个位数 TFLOPS),另一方面也是傲傲不想前一天白调了。不过因为不想浪费免死金牌,其实跑得也还是很保守,因此成绩并不好。不过比完之后傲傲写了一个根据每两次输出之间的功耗自动调整 CPU 功耗的脚本,来做 HPL 自动调参,据说还可以当他的毕设。由于时间不够,我们并没有跑多久,不过看起来效果还不错。

顺便一提,应大家傲傲的要求,我用 React + Django 写了一个音台式的控制台,可以用 iPad 实时调整集群的风扇速度、 CPU 功耗、GPU 功耗、GPU 频率等,效果拔群。不过它也只是个娱乐性质的项目,因为存在延时和误触的可能,比赛中也没有用上。

传统 HPC 题

OpenFOAM

OpenFOAM 之前在 ISC 出现过一次,而前一个优化 OpenFOAM 的学长甚至因为做得太好得到了 HPCAIAC 设置的一个特别奖。这次 OpenFOAM 由立立和张晨年级第一 负责,后者甚至打印了一整本几百页的用户手册没事的时候带着看!

刚拿到题目,组委会就说不让上 GPU,后来赛前也说不让进行手动划分,因此实际上能做的优化不是很多,更多的是各种编译选项、库、参数的组合等。当然,我并不了解,就不妄加评论了。真正跑的时候,有一个比较长的单线程预处理时间用来划分网格(不由得让我想起了 ShengBTE),真正运行才几分钟。郑总看起来非常认真,跑了至少有四五次。最后,我们在这一题上拿了第二。

SWIFT

SWIFT 是一个宇宙流体力学学模拟软件,用来模拟各个星体之间的相互作用等。它改进了空间划分的方式和相互作用的计算方法,号称自己比之前的 state-of-the-art 应用快数十倍。刚开始看介绍的时候我对它做的事情非常感兴趣,因此就选了它做优化。zcg 和 gyx 一开始也在,但是后来 gyx 因为忙就没有再参与了。

SWIFT 依赖很多:HDF5、ParMETIS/METIS、GSL、FFTW,它本身也推荐使用 jemalloc/tbbmalloc/tcmalloc 来优化内存分配的性能,因此我把绝大多数时间花在了尝试这些库的组合上。HDF5 没有什么好说的,而我始终用不起 ParMETIS(报的错误是所有的划分比例加起来不等于 1,让我非常费解,搜遍了也没找到解决方案),于是就没有再管它们了。GSL 没有什么可以替代的,FFTW 除了原本的实现,还有 cuFFTW 和 MKL 的 drop-in replacement,可以直接替换 so 来用。此外,我们还可以控制 SIMD 指令集,包括 SSE/AVX/AVX2/AVX512 (自从 CESM 测出 AVX512 并没有优势以后,我就对此非常敏感。这次 NTU Swift 跑得比较慢,也是因为直接上了 AVX512)。最后,还需要控制每台机器的 Rank 和每 Rank 的线程数量(2 * 26 或 4 * 13)。这么多变量,手工测试非常麻烦,于是我写了一个脚本来控制这些因素,自动测试、输出结果。经过很长时间的测试,最终得出的最优组合是 tbbmalloc + FFTW + AVX + 4 Ranks * 13 Threads。其中前两者的替换影响都不大(cuFFTW 会变慢,或许是因为内存拷贝的开销,并且还会增大 GPU 功耗),而后两者能产生比较大的影响(至少 $10\%$)。

除了这些代码无关的优化之外,在最后几周的时候,我和 zcg 都对 SWIFT 跑了 vtune。他尝试过把其中的数学运算换成比较快的近似版本,但是并没有改善热点(指数运算)。而我在 perf MPI 版本的时候发现了一个比较奇怪的地方:SWIFT 在每几次迭代完后就会强制划分整个空间(以减少各个 Rank 之间计算量的不平衡),并重新分发到所有 Rank。这是一个单线程的过程,计算和通信都比较慢。而我尝试注释掉这些步骤以后,其实在比较少的步骤内每一步的迭代时间并没有变慢(甚至反而变快了),并且最终结果也没有发生大的变化(可能有一些浮点误差),但减少的 engine_rebuild 过程至少能带来 $20\%$ 的性能提升!这一表现在组委会提供的测例上更为明显,去掉这一过程后我得到了大于 1.5 倍的加速。我们完整的运行(包括IO与真正计时的迭代过程)只需要 4 分钟左右,而其他队伍普遍需要 6 到 7 分钟。虽然如此,稳妥起见我还是同时提交了优化前后的两个版本。事实证明,最后组委会或许没有接受我的优化,因为我们在这一题上只排了第三。

CP2K

CP2K 是一个原子模拟软件,由 yjp 和 tsz 负责,tsz 还找了一个化学系的同学一起来帮忙。我对 CP2K 了解也不多,只知道一开始他们在编译和运行上遇到了相当多的问题(比如 MPI 的选择和运行方式等)。它同样也有很多运行参数,以至于 tsz 特别制作了一个 Excel 用来拼接运行参数和记录、对比结果。最神奇的是它要求线程数是 $ab^2$ 的样子,因此进程和线程的划分并不像其他题一样是跑满就行,而是需要更精细的调节。

tsz 对 CP2K 做了不少的优化,最后在比赛时都没怎么用上(听起来很像 WTDBG)。我们没有尝试过的一个库在比赛的测例上能让它变快,但是最终我们也没有用上(组委会要求同时提交用和不用的结果,意味着可能用了以后跑出来的是无效的)。CP2K 在运行时的功耗特征比较迷,有很多的尖峰,所以我们也进行了大量的尝试和控制。最终我们的结果是第二,很不错。

神秘应用

神秘应用和 SWIFT、OpenFOAM 同在第二天,名叫 Pennant,是一个有限元计算软件,傲傲和 hkz 接下它。在编译和运行上我们完全没有遇到问题。初步尝试以后,hkz 发现适当减少进程数可以获得至少 1.5 倍的加速比。此后,无论怎么尝试,都没有变得更快了。

经过尝试,我们发现这是因为这一题事实上访存密集,因此进程数太多明显是不利的。我们的集群原本内存频率是 2933 MHz,但是因为插满以后降到了 2666 MHz。对比没有动过机器的 SYSU,我们就明显有了劣势。最终我们也没能扭转这个劣势,跑出的成绩并不在榜上。

AI Task

这次的 AI 题是根据一些气象数据推断天气,数据来源于 CAM5(没错,它是 CESM 的一部分,这都让我有一些 PTSD 了)。傲傲和 hkz 负责这一题,他们找到了两个 state-of-the-art 的网络,并且把它们 ensemble 了起来,在我们不跑其他应用的时候见缝插针训练,看起来得到了比论文中更好的结果。

这一切都很好,直到比赛第三天中午还有一个小时 ddl 的时候,有人来问我们:你们知不知道组委会把答案也发下来了?

?????还有这种事?然后我们发现的确是这样的,并且这些答案其实也来自于公开的数据集(我们并没有见过)。于是大家去问组委会要不要重新发,他们思考了一阵子,表示不了。于是我们也没有做任何改动(毕竟不能造假),就交了。

交完以后还发生了一点小插曲,因为我们 inference 的时候用的 batch 比较大,所以为了填满 batch 就 duplicate 了一些测试样本,然后去重,这导致组委会的评分脚本不知为何不工作了。好在一番解释以后这个问题解决了。

最终我们的准确率大概在 $80\%$ 不到的样子,但是 ETHZ 的人来和我们交流的时候说他们就是对着答案那个数据集训练的,准确率将近 $100\%$……这还玩什么,认输认输。最后这一题我们也没有上榜,而 ETHZ 成功得到了第一名。

面试

ISC 的面试环节比较神奇,是每个评委下来转一圈分别问问题,所以我们可能要讲一个内容好多遍。第一天评委来的时候,我们甚至连 PPT 都没做好,我一边口胡 yjp 一边做,最终两边进度终于会合了,非常刺激。后来熟悉以后,就讲得顺很多了。不过并不是所有评委的风格都一样,有些人倾向于问技术,有些人就看重别的。我印象最深的是有一位美国女评委问我们为啥没有女生,说世界上很多超算(比如 Summit)都是有女性参与领导的,我们也应该有女性成员。Dan 在进行了两次采访之后摇身一变也变成了评委,还对我们的成果(比如 1.5x 加速比)表示不满,说清华应该至少能做到20倍(??)。

颁奖与结束

最后一天下午就是颁奖环节。不像 ASC,ISC 的颁奖很简洁,Dan 念一遍每个队伍的名字和简介,然后就是最佳人气奖(成功大学)、最高 HPL(ETHZ)和前三名了。第三名是 ETHZ,他们第一年参加,就拿到了这么高的名次,以后肯定也会成为有力的对手。第二名就是我们,说实话我的心情和 ASC 的时候一样,既希望又不希望是我们,但是还是欣然地接受了。第一名是比赛的时候坐在我们隔壁的 CHPC,南非国家队。他们是从全国比赛选拔的队员,还到 Intel 参与专门训练,实力了得,往届也经常和我们分庭抗礼。

颁奖结束以后,听了一个 2019 年 HPC 的发展报告,没听完就被叫去拆机器了。由于我们对机器的改动比较大,恢复和检查也用了不短的时间。最后的半小时我们一直在反复检查已经恢复完毕的机器,因为有一台机器的内存不够了。后来 yjp 喝水的时候才发现那些内存被放在了桌上,虚惊一场。收拾完就已经八点多了,由于 Mellanox 的人请我们吃了一顿德国肘子(真的好吃!),原本的 Student Gala 就被咕了。

吃完肘子回来,傲傲约了南洋的人去 yjp 房间喝酒,谁知道他们已经自己喝过了,就没怎么再喝。我原本计划第二天和 yjp 和 tsz 去纽伦堡,但是因为当时太晚了(十二点多),yjp 表示自己睡不够,就咕了。

关于吃的

我们每天早餐都是在酒店吃的自助,标价 22 欧/人,比较丰盛。我印象深刻的是没有味道的炒蛋和水煮香肠(每天必有);此外炸鱼和鱼蓉做的小点心非常好吃,面包也烤得很香(但是真的好!硬!);还有自助的水果酸奶和燕麦牛奶,我也每天必选。

比赛的时候会场也提供餐饮,都是比较欧式的风味。大多是肉、土豆、豆子之类的煮熟以后淋上酱汁,有时候味道不错,大多都很咸,偶尔也会有一些美式汉堡、意大利面之类的。

我们还有几顿饭是在酒店边上的 SKyline Plaza 吃的,第一天我们吃了印度菜,后来也吃过金拱门(果然全世界一个味道)和炸鱼+土豆,翟老师也给我们买过越南菜和必胜客(也是一个味道)当人肉外卖。总体来说法兰克福的吃的我还是比较能够接受的。

摸鱼时光

法兰克福散步

到的第一天晚上,吃完饭我和傲傲沿着街往南走了一些。我们先去中央火车站(Hauptbanof)逛了一逛,我惊讶于进门就直接能上站台的快捷和方便,还看到了几种不同的车型。然后继续往南,过了美因河之后沿着河走了一段,就回了宾馆。

比赛第二天晚上,六点就要从会场离开。吃完饭还很早(十点才天黑),我和傲傲又出门散步了。这次朝北走,一路到了法兰克福棕榈园,可惜已经关门了。而后我们去了法兰克福大学转了一圈,骑着当地1欧元/20分钟的共享单车回了宾馆。我的车意外显示开锁失败但是事实上成功了,我就在不知情的情况下白嫖了一路,到了以后才发现没扣钱。

纽伦堡之旅

比完赛后一天是空闲的,虽然 yjp 和 tsz 咕了,但是第二天我醒过来的时候才八点多!到纽伦堡的车九点半才出发,于是我马上就买了票(quer-durchs-land-ticket,铁路通票,可以坐所有 RE 和以下等级的车,但是不包括城郊线 S-Bahn 和地铁 U-Bahn),一天一个人 44 欧元(人越多均价越低,奈何没人陪我)。

因为是区域车,我在 Wuerzburg Hbf 换乘,需要吐槽的是地下换乘通道很小很挤,还漏水。我坐的车舒适度挺高的,但是摆式列车给我一种奇怪的感觉。一路上因为手机信号实在不好,我就一直在看 Kindle。RE 等级的车基本是站站乐,总共两个多小时后终于到了纽伦堡,这时候已经中午了。我在火车站买了个 Subway (又是全世界一个味道)当午饭,出门就见到一辆救护车飞驰而来,原来是有人晕倒在了路中央的人行道上。

纽伦堡是一个旅游城市,因此虽然当天是德国的公共假日(圣体日),大多数公共场所都还开着。整个内城不大,半小时就能走一圈(虽然我一度因为没有零钱上厕所而比较懵),我爬到了城堡顶上看了看整个城市的全貌。下午突然开始下雨,还好是小雨,一阵一阵的,但也让没带伞的我猝不及防。逛的途中买了一支甜筒,感觉颇为不错。下午的剩余时间我在日耳曼国家博物馆度过。这个博物馆名头很大,的确也很厉害,有很多著名的藏品,门票8欧元。从博物馆出来已经五点多了,变成了中雨,我就决定走了。

在纽伦堡火车站买了一个小 pizza 以后,我又踏上了回程的车。中间遇到了一个(并不有趣的)小插曲:在 Wuerzburg 转车的时候,官方火车 App 上标的站台和实际站台不一样!还好我发现不对立刻找了一圈,否则误了车就麻烦了。回来的车上看到了雨后的彩虹,很漂亮。到法兰克福已经九点多了,我回去又吃了一点东西然后就休息了。

事实上当天其他人也都出去玩了,yjp 和 tsz 去了法兰克福市中心散步;傲傲骑着租的巨重的公主车上了法兰克福城郊的山(太强了);立立和 hkz 坐火车去了亚琛玩,晚上十二点多才回来(hkz 还把手机丢在了火车上)。

总结

想好不写流水账的,最终还是没能逃过。写到这里,反而不知道应该说些什么了。迷信一点看,要尽人事听天命;这次我们已经做得很好了,或许还差那么一些运气吧。

还是一个老套的结尾:SC 19,加油!