那一年,让我整个人升华的 C++ BERT 项目

Posted CSDN

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了那一年,让我整个人升华的 C++ BERT 项目相关的知识,希望对你有一定的参考价值。

作者 | rumor       责编 | 八宝粥
来源 | 李rumor(ID:leerumorr)

大家好,我是rumor。

最近知乎有个很火的问题:「你的编程能力从什么时候开始突飞猛进?」,每次信息流刷到的的时候都会拨动我记忆的缓存,让我想起那段大起大落的时光。
19年夏的某一天,在各大公司陆续推出自己预训练的BERT,并开始用它们做离线任务时,leader把我叫进会议室,说老大想上线BERT,需要尽快写一个C++版本的BERT服务。
不是那种用tfserving或者libtorch哦,是直接用C++写。
我面无波动地答应了下来,实则内心已经裂开了。
那一年,让我整个人升华的 C++ BERT 项目

自己不是CS科班出身,压根没写过完整的C++项目,虽然有些Java基础,但到底几斤几两我还是蛮清楚的。

打比方的话,就是让第一次玩塞尔达、刚开完四个神庙、出了新手村的我直接去打盖农,悲壮之感难以言表。

那一年,让我整个人升华的 C++ BERT 项目

整个人都不好了


但我作为新时代的自强女性,还是开启了冲向海拉尔中心的旅程。

第一步,就是搜集可以参考的开源项目。

Fortunately,我找到了知乎开源的cuBERT项目,作者写得特别清楚,并且同时提供了GPU和CPU版本,但老板看了速度之后仍不太满意,最终我靠着极强的搜索技术又找到了英伟达刚开源的fastertransformer,用Cuda C++直接实现了transformer底层运算,速度秒杀其他方案。

在经历了各种make、install的折磨之后,我终于摸清了如何在服务器配置相关lib并运行c++代码,也顺利跑通了官方demo,性能完全符合预期。但不能高兴得太早,因为这个库只有transformer层的实现,前面的tokenization、embedding、pooling都没有写。。所以,这意味着我要读懂源码,然后自己把剩下的补全。

那一年,让我整个人升华的 C++ BERT 项目

第二步,我开始读源码改项目。

这里有碰到了第一个难点,就是跑官方给的模型没问题,但运行我们自己的模型后就出现了 nan 这个恐怖的结果。于是我开始使用 print 大法,但惊讶地发现 cuda 底层全是并行的,一打日志都是乱的。。。于是我学会了 synchronize,在每个 operation 之后同步再打印结果,最终花了两天时间定位了问题:原来是Softmax没加溢出保护。立刻给作者提issue,不过在等待作者回复的过程中我居然自己给改好了,还默默学会了 Parallel Reduction 算法。

期间我还会卡在各种各样的事情上,经常会卡上一两天,陷入自我放弃的漩涡。

最终还是搞懂了源码,搞懂了cuda运算,并加上输入输出层搞出了完整的C++ BERT。

但仅仅有个程序还是不够,服务接口在哪里呢?

第三步,整一个服务。

于是我又搜啊搜,找到了一个宝藏:TensorRT Inference Server。当时的版本提供以下超赞的功能:

  1. 支持单GPU上的多模型&单模型多实例
  2. 支持多种backends框架(TensorRT、Tensorflow)
  3. 动态Batch增加吞吐
  4. 提供负载均衡及状态监测

所以又花了几天把C++ BERT适配TensorRT框架,成功变成了服务。

变成服务之后又有问题,就是每次换机器都要重新配置环境并部署,于是我又学会了docker,减轻运维负担。


那一年,让我整个人升华的 C++ BERT 项目

通关了!


整个改造差不多耗时小两个月,也是我工作至今记忆最深的一段时刻。

我永远忘不了,那种看着看着代码就想站起来掀桌子的感觉。可以一起体会下:

template <typename T>
  __inline__ __device__
T blockReduceMax(T val)
{
  static __shared__ T shared[32]; 
  int lane = threadIdx.x & 0x1f// in-warp idx
  int wid = threadIdx.x >> 5;  // warp idx

  val = warpReduceMax(val); // get maxx in each warp

  if(lane == 0// record in-warp maxx by warp Idx
    shared[wid] = val;

  __syncthreads();

  val = (threadIdx.x < (blockDim.x >> 5 )) ? shared[lane] : -1e20f;
  val = warpReduceMax(val);

  return val;
}

当然也忘不了身边同事牺牲自己时间给我的帮助,还有lead和我一起翻了半天C++ Premier才解决问题的欣喜。

故事的后来很圆满,压测效果满意,成功服务了团队的BERT上线。包括后来我在20年初和其他两个大厂团队的人交流,都没有听到过更快的速度,甚至有同学直接质疑了我,因为他们的时延是我们的两倍。。。


那一年,让我整个人升华的 C++ BERT 项目

去挑战一座山吧


现在回想起来,这段经历真的太宝贵了。虽然不想再经历一次,但自那以后我再也没怕过任何代码。

做算法也有了底气,我可是搞过CUDA C++的女人,别叫我调包侠。

如果想快速提升,那就去挑战一座山吧,找一个高质量的项目,读懂并进行修改,在一次次自我放弃中成长。

就像走过高考、走过考研、走过校招那样,过去那道坎就没什么了。

「调包的日子,单纯快乐」

那一年,让我整个人升华的 C++ BERT 项目
那一年,让我整个人升华的 C++ BERT 项目
    
      
      
    

“面向对象就是一个错误!”

以上是关于那一年,让我整个人升华的 C++ BERT 项目的主要内容,如果未能解决你的问题,请参考以下文章

那一年高考时

那一年风华正茂,书生意气,年方二十,小鲜肉一枚。大学时期日志

回顾自己毕业的那一年,感恩的四年,成长的四年!

致命错误 LNK1104 C++

C++小项目:directx11图形程序:particleSysclass

火车上的随笔——给今年做个总结