做过一些计算加速的工作,个人感觉要入手先要想好几个问题: 要加速的是什么应用,应用的瓶颈是什么,再针对这个瓶颈,参考前人工作选择合适的方案。
过早地执着于fpga的技术细节(用hdl还是hls,用啥芯片,用啥接口)容易只见树木不见森林。现在software define network/flash/xxx,已然大势所趋。之前开组会时跟同志们聊过,算法是纲,纲举目张;软件是妈,软件是爹,软件比基金委都亲。所以推荐先把cnn的算法看一下,拿一些开源代码跑一下经典的例子(lenet, alexnet, etc)看好输入输出,摸清算法。
<img src="https://pic4.zhimg.com/50/v2-d56c256b53f9a6948743ad15370e3d57_hd.png" data-rawwidth="600" data-rawheight="226" class="origin_image zh-lightbox-thumb" width="600" data-original="https://pic4.zhimg.com/v2-d56c256b53f9a6948743ad15370e3d57_r.png">
比如以下是一个lenet的cpp和opencl的实现
nachiket/papaa-opencl
以下图片源自Yufei Ma的Slide
<img src="https://pic2.zhimg.com/50/v2-36c7d33b8cbf02699f45af1487002319_hd.png" data-rawwidth="600" data-rawheight="456" class="origin_image zh-lightbox-thumb" width="600" data-original="https://pic2.zhimg.com/v2-36c7d33b8cbf02699f45af1487002319_r.png">
可以看到cnn算法主要由conv ,pooling,norm等几个部分组成。工作时将image跟weight灌进去,最终得到预测结果。
接下来拿profiler(比如perf)去分析下软件算法,找找热点和性能瓶颈。在cnn里面主要耗时的就是conv二维卷积了。性能瓶颈也主要在于卷积时需要大量乘加运算,参与计算的大量weight参数会带来的很多访存请求。
接下来考察下前人的工作和当前的灌水热点。按理说这种大量的乘加运算用dsp应该不错,但是在cnn中大家并不需要这么大的位宽,有时候8位就够了。dsp动辄32/64位的乘加器实在是浪费。于是乎大家就开始减位宽,多堆几个运算单元。面对大量的访存请求,大家就开始设计各种tricky的缓存了。
以下是大家的一些灌水方向
<img src="https://pic1.zhimg.com/50/v2-09196400e0ed3fa04d62900fce972ff8_hd.png" data-rawwidth="600" data-rawheight="432" class="origin_image zh-lightbox-thumb" width="600" data-original="https://pic1.zhimg.com/v2-09196400e0ed3fa04d62900fce972ff8_r.png">
<img src="https://pic2.zhimg.com/50/v2-43538fad18114810a846a02ee64cea61_hd.png" data-rawwidth="600" data-rawheight="449" class="origin_image zh-lightbox-thumb" width="600" data-original="https://pic2.zhimg.com/v2-43538fad18114810a846a02ee64cea61_r.png">
于是就有了以下各路硬件设计
<img src="https://pic3.zhimg.com/50/v2-a9fad6b6ff48cfa3fdc806cd798c0a9e_hd.png" data-rawwidth="600" data-rawheight="448" class="origin_image zh-lightbox-thumb" width="600" data-original="https://pic3.zhimg.com/v2-a9fad6b6ff48cfa3fdc806cd798c0a9e_r.png">
<img src="https://pic3.zhimg.com/50/v2-1191985b7be33b5a26560ef8c027a8c6_hd.png" data-rawwidth="600" data-rawheight="441" class="origin_image zh-lightbox-thumb" width="600" data-original="https://pic3.zhimg.com/v2-1191985b7be33b5a26560ef8c027a8c6_r.png">
有人照着dsp风格去设计加速器
<img src="https://pic4.zhimg.com/50/v2-6986c5225c4ea5efeaee0c1591851083_hd.png" data-rawwidth="685" data-rawheight="573" class="origin_image zh-lightbox-thumb" width="685" data-original="https://pic4.zhimg.com/v2-6986c5225c4ea5efeaee0c1591851083_r.png">
ceva也出了一系列面向CNN的IP
<img src="https://pic1.zhimg.com/50/v2-486f5c2d265ce3da1d9178ac6b668b10_hd.png" data-rawwidth="600" data-rawheight="267" class="origin_image zh-lightbox-thumb" width="600" data-original="https://pic1.zhimg.com/v2-486f5c2d265ce3da1d9178ac6b668b10_r.png">
<img src="https://pic3.zhimg.com/50/v2-e0580dff7dc6fe91c47c4ff3665767ae_hd.png" data-rawwidth="600" data-rawheight="300" class="origin_image zh-lightbox-thumb" width="600" data-original="https://pic3.zhimg.com/v2-e0580dff7dc6fe91c47c4ff3665767ae_r.png">
有人用了脉动阵列或者Dataflow的风格
<img src="https://pic2.zhimg.com/50/v2-a662f91d6112f176202a85c040cedc61_hd.png" data-rawwidth="644" data-rawheight="606" class="origin_image zh-lightbox-thumb" width="644" data-original="https://pic2.zhimg.com/v2-a662f91d6112f176202a85c040cedc61_r.png">
有人设计了专用的芯片比如计算所的Cambricon
<img src="https://pic3.zhimg.com/50/v2-201b88545f9f16f79998e46bc799dbce_hd.png" data-rawwidth="671" data-rawheight="382" class="origin_image zh-lightbox-thumb" width="671" data-original="https://pic3.zhimg.com/v2-201b88545f9f16f79998e46bc799dbce_r.png">
还有的就是你提到的fpga
所有的事情到了硬件层面实际上能用的手段也就有限了。不外乎堆资源和切流水两招。再不然就是做一些bit level的小技巧,比如乘法器变查表之类的,这些技巧在很多二十年前的dsp教材里面都描述得很细致了,拿来用就好。比如这本书亲测有效
VLSI Digital Signal Processing System–Design and Implementation
by Keshab
典型的fpga实现可以参考Yufei Ma的文章,不论是conv,还是pooling,依葫芦画瓢设计data path,切好流水,再想好状态机加上控制信 。这些就看大家撸rtl的基本功了。
比如Conv模块如下图,主要拿一堆乘法器以及加法器树搭好data path,切好流水,接着加上控制信
<img src="https://pic2.zhimg.com/50/v2-6b3985b59e32ecdd69e4353c4cbdc1c9_hd.png" data-rawwidth="600" data-rawheight="230" class="origin_image zh-lightbox-thumb" width="600" data-original="https://pic2.zhimg.com/v2-6b3985b59e32ecdd69e4353c4cbdc1c9_r.png">
Pooling也是大同小异
<img src="https://pic2.zhimg.com/50/v2-2e90e3c2e8d9322f92289e6b14e4e601_hd.png" data-rawwidth="600" data-rawheight="168" class="origin_image zh-lightbox-thumb" width="600" data-original="https://pic2.zhimg.com/v2-2e90e3c2e8d9322f92289e6b14e4e601_r.png">
还有Norm
<img src="https://pic3.zhimg.com/50/v2-45c9dea746c4890787deb29454369202_hd.png" data-rawwidth="600" data-rawheight="182" class="origin_image zh-lightbox-thumb" width="600" data-original="https://pic3.zhimg.com/v2-45c9dea746c4890787deb29454369202_r.png">
最后把这些模块通过router连接,外面再套一层控制模块,封成ip就好了。
<img src="https://pic2.zhimg.com/50/v2-8181c345725124399edbfdd7d38da329_hd.png" data-rawwidth="600" data-rawheight="390" class="origin_image zh-lightbox-thumb" width="600" data-original="https://pic2.zhimg.com/v2-8181c345725124399edbfdd7d38da329_r.png">
剩下的就是集成进你的系统(microblaze, nios还是arm,配好dma,写好灌数据的驱动,这些就是各有各的道儿了)。推荐动手码rtl前先写好文档,约定好端口,寄存器和软件api,否则边写边改容易乱。
整体来说,cnn这种应用流水线控制相对cpu简单,没有写cpu的那一堆hazard让人烦心,也不用写汇编器啥的。太大的cnn放在fpga里挺费劲,做出创新很难,但是fpga上写个能用的lenet这种级别的cnn还是挺容易的。最后还可以依照惯例跟cpu比性能,跟gpu比功耗。现在人工智能辣么热门,小朋友们找工作可以拿写过cnn来忽悠hr。
最后祝大家发出足够的paper,找到理想的工作,拿到想要的funding。fpga这个圈子已经那么小众了,还是不要把新人吓跑了,其实fpga这东西也不难,数字电路跟微机原理用心学一学,别太沉浸于工具软件的用法(xilinx的工具真鸡儿烂),上手也挺快的,而且还挺好玩的。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!