万万没想到,我的炼丹炉玩坏了
前记
众所周知,夕小瑶是个做的小可爱。
虽然懂点DL框架层知识,懂点CUDA和底层,但是我是做算法的哎,平时debug很少会遇到深度学习框架层的bug(上一次还是三年前被pytorch坑),更从没遇到过CUDA层甚至硬件层的bug。直到有一天....
这个bug彻底颠覆了我的debug思路,从这个bug开始,我再也不认为做算法的就做好算法就好了。
事故现场还原
当时的背景是这样的,我平时在一台8卡的GPU服务器上debug代码,这台服务器很少会把8卡全用满,毕竟这台机器只是用来debug代码而已,要跑那种好几天的8卡训练任务的话肯定就提交集群了,否则你如果占了全部8张卡,那自己和别人都没法debug了。
当时恰逢deadline扎堆,0-5卡都被其他蹭机器的小伙伴占了,只有6、7卡能用。于是我就用这两张卡来debug代码,训练和预测都是多卡并行,于是debug的时候这两张是同时用的。
结果把新的idea实现后,跑了一下,发现一个step都没有跑完就挂掉了,而且全部的报错信息就两个单词:
$ bash run.shsegmentation fault
???我写的都是python代码呀,我不是在做NLP么,怎么跑出来 段错误segmentation fault 了??众所周知,段错误是写C++代码时非常让人头疼的错误
随手一搜
果然,自己摊上大麻烦了╮(╯▽╰)╭
虽然,连挂掉时的代码出错位置都没有打印;虽然,炼丹以来第一次遇到这个报错。但!是!依然难不倒见过大风大浪的本宝宝的!
进一步加关键词限制来强行Google了一下,发现可能的错误原因依然太多了,各种类型的代码里都可能遇到。算了算了,这次就不Google oriented debug了,是时候展示真正的技术了!
首先,先看看是计算图 compiled之前 还是 过程中 还是 runtime 挂的。
【此处省略一顿怒插数十断点的操作】
于是一路debug发现计算图可以顺利建立,但是在深度学习框架完成计算图编译,开始runtime计算的时候,就挂了。emmmm,这说明。。。
果然是最糟糕的情况哇! !
基于静态图的DL框架就怕在runtime出问题,很多小伙伴可能不清楚runtime该怎么debug,于是纷纷觉得pytorch真香。其实对于绝大部分错误,静态图还是蛮好debug的,秘诀也很简单,那就是往图里插入debug op。
像tensorflow、paddlepaddle这种支持静态图的深度学习框架,都是有大量debug op的,最常用的就是 print op (tensorflow中的tf.Print/tf.print,paddle中的layers.Print),它可以在计算图的任意位置打印出运行到此位置时的任意Variable/Tensor的runtime值,当然也可以搭配shape op(TF中的tf.shape,paddle中的layers.shape)打印出Tensor的runtime形状等信息。除了print op外,还有 assert op 、 is op 等保证运行正确性、辅助debug的op,也就是说,python中常用来debug的一些关键字和函数调用,其实成熟的静态图框架里基本都能找到对应的op。熟悉了映射关系后,静态图debug起来也不会太费力。
但!是!有两种情况依然比较头疼,一种是计算图已经完全建立了,但是第一个op还没有跑到的时候就挂了(这时候插入的debug op完全没被run到);还有一种是前向正确的跑完了,但是计算梯度的时候挂掉了(这时候错误可能发生在整张图的任意位置,甚至不在图里)。当它们再叠加上多机多卡问题时,就是最惨的情况。
不幸的是,我遇到了(。 ́︿ ̀。)
首先,如前所述,本宝宝很淡定的在计算图里插入了一堆Print结点,然后发现前向计算结果貌似非常正确,轻松的定位到错误发生在runtime计算反向梯度的时候,也就是
optimizer.optimize()
算了算了,也不是第一次挂在这儿,报错信息似乎也提供不了太多指导(报错信息里有一些路径类信息,比较敏感就不贴出来了)。再多插入几个debug op来验证前向路径的正确性
【此处省略另一顿插op的操作】
结果,万万没想到,插入debug op之后,竟然第一个step跑完了,这次挂在了第二个step!
? ? ?
debug op还有强行续命的功效???
我!不!信!于是把debug op又都注释掉了,重新run!
结果,
又挂在了第一个step!!!
我当时狠掐了自己一把,这特喵的一定是在做一个很荒唐的梦!
令我没想到的是,现实果然比梦境更荒唐。我又重复的run了好多好多次,发现没有插入debug op的时候确实永远是第一个step就会挂掉。于是我把这个震惊的现象告诉了大家,然后果然,大家都觉得我疯了。
小夕: " 你们都过来!我来亲自演示一遍!"
小夕: " 你们看!没有插入debug op的时候,第一个step就报了segment fault的错误叭~"
大家: " 哦"
小夕: " 然后看好哦,我把这里的debug op都插上,然后,run!"
诶诶诶???怎么还是第一个step就挂了,被疯狂打脸( ´Д` )
大家: 小夕你要是累了就先去睡会儿,别太累了,大家散了散了╮(╯▽╰)╭
嘤嘤嘤怎么会(。 ́︿ ̀。)
算了算了,不纠结这个了。回到debug本身,emmm,话说前向能正常跑完,永远都是挂在反向阶段,那么说明要么前向哪里埋了一个坑暂时被强行计算了,但是这个节点处的梯度其实是无法计算的;要么可能是优化器本身就有bug。
于是先扫了一遍代码,确实没有使用也没有cast很奇怪的数据类型,也没有使用一些很奇怪的op,基本可以排除前一种情况。那应该就是后一种了!而优化器用的其实就是adam,没什么改动,那问题就肯定出在自己新加的 多卡梯度合并 这块了!
于是果断的在6卡上跑了一下单卡的代码,果然正常训练! 我真是福尔摩夕呀,马上就要揪出bug真凶了好激动。
继续!回去check了一下多卡逻辑,燃鹅似乎。。没问题鸭?天呐,难不成这回要去框架源码里插断点了?
看来,我只能祭出我的杀手锏了,那就是
滚! 去! 写! demo!
由于代码逻辑有点复杂,并且会在多卡梯度合并的时候要进行一些额外的处理,当时觉得应该还是代码哪里写的有问题,于是决定先开ipython写个多卡计算的小demo。
【此处省略一个demo】
嗯,前向计算顺利通过,符合预期。好,加大难度,引入梯度合并。
结果,
又又又segmentation fault 了!!!我还没引入我的idea呢,直接就挂了。
不对哇!!这不就是多卡训练的标准玩法么,难道最新版的框架有bug???天呐我发现了这么大的一个bug!于是卸掉重装了一个旧版的非常稳定的包,重新跑这个demo代码
结果,又又又又 segmentation fault 了!!!
不会吧。。。又check了一下环境,确保CPU、内存、磁盘、GPU及其显存都是够用的,CUDA、CUDNN、 NCCL 也没问题(自带的tool都能check过)。难不成是python的锅?不至于吧,我也太多疑了。。。
这时我出现了一个大胆的想法。
难不成这两张显卡里,有一张是坏的??
前面测试CUDA时刚在GPU6上跑了一把,发现好好的。那么。。。GPU7!狼人肯定是你了!!
于是又去GPU7上跑,满心等着它挂掉,结果。。
训练一切正常
强迫自己冷静了一下。我是不是太多疑了,竟然都怀疑到硬件身上了。但是,基本的运行环境都check过了鸭,如果不是硬件,也不是python和框架的问题,那难不成。。。是中间的glibc的问题???联想到segment fault这个报错信息,更加确信了!glibc就是狼人,这次跑不掉了!
然而众所周知,glibc这种级别的库,是非常底层的,一旦改坏了,会导致ls、cd这种级别的命令都挂掉。我看了一眼GPU0-5卡上跑的好好的别人的任务,再次陷入了迷茫。。。
【此处省略长达数十分钟的卡顿】
嗯,先低调一些,先把demo放到一个崭新的环境中跑下!于是把demo放到另一台机器上跑了一下,果然,多卡也是能正常跑的。莫非真是glibc?但是为什么别人的任务没问题呢。似乎一切现象都在导向最后一种可能
GPU6到GPU7的底层通信链路出故障了!
于是我耐心的等着其他小伙伴的任务跑完,重新测试!
GPU5+GPU6:训练 正常
GPU5+GPU7:训练 失败
GPU0+GPU1+GPU2+GPU3+GPU4+GPU5+GPU6:训练 正常
GPU0+GPU1+GPU2+GPU3+GPU4+GPU5+GPU6+GPU7:训练 失败
实锤!!!激动万分的跑去找管机器的小哥哥了,语无伦次的给解释了大半天。终于,小哥哥将信将疑的安排人去进一步测试了一下
焦急的等了若干天。。。
小哥哥: " 是的,GPU7坏掉了,无法与其他卡通信,更换GPU7后测试正常"
听到这个消息后,小夕非常激动的重新测了一遍各种跟GPU7的排列组合,成功!!
后记
这时候我突然想起来之前那个见鬼的插debug op会导致多跑一个step的现象,而且仅出现了一次,于是跑去问小哥哥,小哥哥悠悠的说
" 那大概就叫做回光返照吧,GPU7尽力了"
作者暂无likerid, 赞赏暂由本网站代持,当作者有likerid后会全部转账给作者(我们会尽力而为)。Tips: Until now, everytime you want to store your article, we will help you store it in Filecoin network. In the future, you can store it in Filecoin network using your own filecoin.
Support author:
Author's Filecoin address:
Or you can use Likecoin to support author: