本文为HLS的下半部分。
在本文当中,将涉及如下部分的内容:
- 1.性能分析报告的读取和分析
- 2.接口类型以及优化的方法
编译报告的分析
每一个HLS的代码编译之后,都会生成编译报告,而编译报告主要提供了如下的一些信息:
- 1.编译的指令
- 2.对应的模块的资源消耗
- 3.性能瓶颈
而优化的策略,也主要根据上面的进行。
编译报告读取
所有的编译报告在生成的xx.prj/reports当中,使用浏览器打开报告,可以看到如下的内容:
我们所需要的,就是分析报告的各个部分,并根据其提示的信息进行代码的优化。
01 报告汇总
在报告汇总当中,主要包含了几项:
- 1.编译的指令
- 2.使用的器件(芯片)
- 3.资源的消耗
从这几个报告当中,可以简单的得知当前的模块是否可以在选定的fpga芯片上运行。
02 循环分析
大部分的算法当中,循环是不可避免的一部分,而循环实现的方式方法,会影响到FPGA芯片运行的效率和资源占用。因此,也需要对循环的进行分析
上图是一个典型的循环分析的结果。在循环分析的时候,主要有几个指标需要注意:
- 1.Pipelined:表示循环是否被流水线化,即循环不是串行的执行的。一般情况下,Yes会比No好。
- 2.II:表示时间延迟的度量,通常的说,<=1,或者n/a为最好,<=10为次好,其余的数据表示的结果,就表示延迟非常大了,执行效率和并行度不高。
- 3.Bottleneck:表示性能瓶颈,通常说来,n/a表示不存在性能瓶颈,如果后面出现II,表示对应的部分就是性能瓶颈,如果需要高性能,则需要对对应的代码进行优化了。而,对应的代码,可以在右方直接查看。
而我们针对HLS的优化,主要的依据也是这部分,特别是针对存在循环的算法。
03 资源使用分配
FPGA也存在自己的资源,而一个应用程序要在FPGA芯片上运行,首要的就是必须满足资源使用量在芯片提供的范围内,不能超过。如果使用的资源超过了,则对应的FPGA算法无法在芯片上运行。
资源的使用主要在于HLS当中的变量,变量类型是浮点类型,整数类型,还是其他类型。不同的类型会占用不同的资源,而不同的运算,也会占用不同的资源。总体上来说:
1.整数类型<单精度浮点类型<双精度浮点类型,8位 < 16位 < 32位
2.加法操作 < 移位 <= 乘法 < 减法 < 除法
3.直线运行 < if-else
所以,在优化的时候,基本上选择低精度的数据,代替高精度的数据,将减法和除法移除算法,或者少用减法与除法,尽可能的减少if-else结构的使用。
04 图示表达
除此之外,还存在其他的一些性能影响的因素,比如接口类型。而这些其他因素的影响,最能直观表达的就是图示。
比较好的性能,通常图上的红色区域比较少,或者没有。
而图上红色区域非常多,或者非常密集,就说明对应的算法模块性能延迟比较严重,也需要进行不断的优化。
而HLS算法模块的性能优化,就是根据上述的几个方面,采取不同的措施,进行对应的优化。
HLS优化与接口
HLS的优化原则:不影响算法的实际功能,在FPGA芯片许可的资源范围内,尽可能的将算法进行并行化,减少时间延迟;但是优化并不是必须的,有的时候,甚至是不能使用优化策略的,必须具体问题具体分析。
而优化的策略和手段,也不外乎上边提到的几点:接口、循环优化和合适的数据类型选择。
01 接口选择
所谓的接口实际上就是一系列HLS函数(C函数)的参数。因为HLS是用C/C++实现FPGA的IP Core,因此函数参数实际上是负责CPU与FPGA芯片之间的数据搬运的。而这个数据搬运的过程,则会直接影响到算法的性能。
1.1 默认接口
上述的代码当中,接口便是指a和b。在默认的情况下,标量型数据,即数值类型,通常对应的是FPGA的默认start/busy接口;而指针类型的参数,则默认对应的是Intel FPGA的Avalon总线接口,Avalon Memory Mapped。只不过,标量型接口,其对应的是32位的地址总线,而指针类型,无论什么类型,都是对应的64位的地址总线。这个地址总线不是在CPU端的,而是在FPGA端的。
一般情况下,默认接口总是能够满足我们的需求的(功能上)。除了默认的接口之外,还可以显示的使用Avalon Memory Mapped(Avalon MM)接口。
1.2 Avalon MM接口
Avalon MM接口分为3种类型:master模式,slave模式和stream模式。每种模式有不同的使用场景,以及效果。另外,Avalon MM接口通常用于处理指针、数组和应用类型,其他数据类型,一般都是按照标量方式进行处理,并没有采用Avalon MM接口。
1.2.1 master模式接口
显式的使用master模式的接口,需要使用ihc::mm_master关键字进行标记,如下:
其中,ihc::mm_master标记一个指针或者应用类型的数据,表示显式的使用Avalon MM接口,并且使用master模式。ihc::mm_master的使用方式如下:
其中,mm_master可选的常用参数如下:
接口定义,编译完成之后,即可提供给FPGA使用。如果为了在main函数当中进行功能测试,可以如下进行。
需要注意的是,master接口通常适用于比较小数据量的传输,因为master是完全同步的,数据量一旦太大,相应的,延迟也会增大。
1.2.2 stream模式接口
流式接口实际上是master模式接口的一种变种,其主要思想就是将CPU和FPGA之间的连接做成流水线,数据源源不断的进行传递。
其主要的形式如下:
简单来说,流式接口,stream_in专门负责接收输入数据,stream_out专门接收输出数据;这类接口只能使用引用数据类型。
在上边的代码当中,a和b就是流式接口,read进行数据的读取,write进行数据的回写。
如果是需要测试函数的功能,可以在main函数当中进行如下的使用和操作:
1.2.3 slave模式接口
与之前的master和stream接口不一样,slave接口主要是针对大容量的数据或者数组为提供的;但是也提供了针对较小数据量的支持,比如:slave的寄存器。
其中,b就是放在寄存器当中,而不是放在内存,因此,从性能上说,速度会非常快;而真正大显身手的,则是slave接口在大数据量以及超大数据量下的表现。
其中,b是一个包含了5000个int元素的数组。这样的数组,如果使用master,会导致FPGA芯片反复的读取接口;如果使用stream接口,也会导致FPGA芯片对接口的反复读取。二者的结果是,读取5000个元素,至少需要5000个时钟周期。
但是,如果使用slave的模式,由于slave的内存非常大,因此,可以将这5000个元素一次性的读入到FPGA当中,效率非常高。
我们可以简单看看一个实际的对比结果:
使用master接口的编译结果,可以看到延迟非常大。
而修改为slave之后,可以看到,几乎没有任何的延迟,瞬间就把执行效率提高了。可以说,slave接口本身就是针对大容量的数据设计的。
从上面的结果,可以看到,接口的选择对性能的影响非常大。但是,并不是什么时候都应该使用slave接口。master、stream和slave,其生成的电路结构是完全不一样的,因此,应当根据具体的需求进行接口的选择。
2.1
其他的优化措施
除了这些接口(电路结构)的优化之外,剩下的无非就是算法的优化,少用循环,少用if-else等比较耗费资源的语法结构。
没有回复内容