您的位置:首页 > 其它

FPGA计算性能优化-数据运行优化2

2017-12-19 10:56 225 查看
 data access patters/数据访问模式(关键词:函数之间,函数内的数据访问模式对性能的影响)

由于FPGA的优越性能,FPGA被选中来实现C代码。一个FPGA的大规模并行架构允许它执行的操作比一个CPU处理器的固有的顺序操作的速度更快。用户通常都希望利用这一性能。

这里的重点理解是理解C代码中固有的访问模式对结果可能产生的影响。虽然最受关注的是那些进入和推出硬件功能的访问模式,但在硬件功能中的任何瓶颈都会对函数的访问速率产生负面影响,因此考虑函数内的访问模式也是值得考虑的。

为了突出强调某些数据访问模式如何对性能产生负面影响,并演示如何使用其他模式来完全支持FPGA的并行性和高性能功能,本节将通过一种图像卷积算法来阐述

1.algorithm with poor data accesee patters/数据访问模式差的算法

这里使用一个标准的卷积函数来演示C代码是如何对FPGA的性能产生负面影响的。这个例子中,对数据执行一个水平的,然后是垂直的卷积,由于图像边缘位于卷积窗口之外,最后一部是处理边界周围的数据。

算法的结构可以概括为:

1.水平卷积

2垂直卷积

3.对边界像素操作

水平和垂直卷积都是用一个固定长度的模板进行卷积,这样会导致边界像素不能够通过卷积模板后得到结果,优化手册中是直接使用最靠近边界区域的有效填充



//代码理解见P22

static void convolution_orig(int width,int height ,const T *src,T *dst,const T *hcoeff,const T *vcoeff)

{

T local [MAX_IMG_ROWS*MAX_IMG_COLS];//图像面积

//水平卷积

HconvH:for(int col=0;col<height;col++)//从图像的第0行到最后一行

 {

   HconvW:for(int row=border_width;row<width-border_width;row++)从图像的卷积滤波中心到(列操作)

{Hconv:for(int i =-border_width;i<=border_width;i++)

{

}

}

//垂直卷积

HconvH:for(int col=border_width;col<height-border_width;col++)

{

    HconvW:for(int row=0;row<width;row++)

{

Hconv:for(int i =-border_width;i<=border_width;i++)

{

}

}
//对边界元素操作

}

这样的C代码直观易懂,但是C代码中的一些问题会对硬件结果的质量产生负面结果。问题具体表现在:

(1)编译需要大量的存储:

第一个问题是C编译过程中需要大量的存储,算法的中间结果存错在一个内部本地数组中。这需要一个高度*宽度的数组,比如说对于1920*1080的图像,它将容纳2073600个值。

对于针对ZYNQ和ultrascale MPSOC以及许多主机的的交叉编译器,这个本地存储的数据量将会导致在运行的时候堆栈溢出。

当一个函数只能够运行在硬件上,为了避免这种问题的一个有用的方法是使用_synthesis——宏,当硬件函数被集成到硬件时,系统编译器自动定义这个宏。上面的代码在C仿真过程中使用动态内存分配来避免编译问题。并且只在合成过程中使用静态存储。值得一提的是:

使用这个宏的缺点是C仿真验证的代码与实际合成的代码不一样。但是,在这种情况下,代码并不复杂,行为也将相同。

上面讨论硬件函数使用动态内存分配来避免需要大量的存储,但是在硬件喊函数上动态分配内存,消耗的将是BRAM,这对于FPGA设备将会有很大的要求。块RAM的使用可以通过使用数据流优化和数据流通过小型高效的FIFO最小化。但这将需要的数据被用于一个流的顺序,而目前还没有这样的要求。

(2)本地数组初始化:

这个问题与性能的表现有关,循环clear_local用于将阵列局部值重新设置为0.即使这个循环在硬件中以高性能的方式执行,这个操作任然会需要大约两百万个时钟周期(将存储一幅图像的数组都清零)。并且当内存初始化的时候,系统不能够执行任何图像处理(循环中是串行的)。解决思路是在次内存初始化时,使用在内循环Hconv的一个零时变量在写入数据数据前初始化。

最后,可以得出的一些结论是:

1)数据的吞吐量,系统的性能,从根本上受到数据访问模式的限制。

2)高效能的FPGA关键之一是最小化PS端的读写。

3)以前获取的每个数据访问都会对系统的性能产生负面影响。

4)FPGA能够执行许多并行计算并达到非常高的性能,但不能通过重新读取值来中断数据流。

5)为了最大限度提高性能,数据应只从PS端中访问一次,并且本地存储最小的数据单元,这些数据也应该考虑到复用。(不是很懂)

2.algorithm with optimal data access patterns

上面主要讨论实现卷积的关键是以最少的资源进行高性能设计,具体方式包括:

1)通过系统最大化数据流:避免使用任何阻止数据连续传输的编码技术和算法行为。

2)最大化数据的重用:使用本地缓存确保没有重新从外面读取数据的要求,并且传入的数据可以保持连续的流。

3)接受条件分支:这对于CPU和GPU是昂贵的开销,但是对于FPGA是最优的

为了使得算法具有最优的访问模式,我们首先讨论数据是如何通过系统在FPGA上流入和流出。

卷积算法是在图像上执行的,当来自图像的数据生成或被销毁时,它以标准的光栅扫描方式传输。图见P29

如果数据以流方式传输到FPGA上,FPGA应该以流的方式处理它,并且以这种方式将其从FPGA中返回。(这些以流方式访问的数据,最终实现中被优化为单寄存器)

(1)优化的行卷积:

这个算法必须使用K个以前的样本来计算卷积结果,因此将样本复制到零时缓存hwin中。使用本地存储意味着不需要从PS端重新读取值并中断数据流。其中,对于第一次计算,由于没有没有足够的值在hwin中去计算结果,故没有输出值被写入。

该算法一直读取输入的样本并将其保存刀hwin中。每次梅毒一个新的样本,它将不需要样本值移除hwin。在第K个输入数据已读后,可以将第一个输出结果写出(满足了第一次卷积条件,完成第一次卷积)。算法根据这种方式将数据沿着行进行读取,直到最后的样本被读取完毕。只有最后的样本存储在hwin中,就能够计算完卷积所需要的全部内容。

在整个过程中,SRC输入的样本都是以光栅流的方式处理,每个样本依次读取,任务的输出要么被使用,要么被丢弃。但是任务的不断进行计算,这与CPU中执行的代码不同。

(2)行和列卷积优化:

(3)边界卷积优化:

3.optimal data access patterns/数据访问类型优化

下面概述了如何确保数据访问模式在FPGA上达到最佳性能:

1)最小化输入数据读取:在数据读入BRAM后,它可以很容故意满足多并行路径,但是对硬件函数的输入可能是性能的瓶颈。因此,读取数据一次,如果数据需要重用,请使用本地缓存。

2)尽量减少多数组的访问,尤其是大数组:数组在BRAM中实现,像I/O口只有有限数量的端口,这可能会导致性格的瓶颈。因此可以将大数组划分成更小的数组,甚至单个寄存器,但将大数组分区将导致使用许多寄存器,建议使用小的本地化缓存来保存结果,如累积。然后将最终结果写入数组。

3)寻求在流水线任务中执行条件分支(而不是条件执行任务,甚至流水线任务)。条件语句在管道不同的路径实现(条件语句是顺序执行的),允许来自一个任务的数据流入下一个任务,在下一个任务中执行将导致更高性能的系统。(增加并行度?)

4)将写出最小化:这与读取的原因相同,即I/O端口有限导致系统性能的瓶颈。复制额外的访问只会加深系统的问题。

对于流方式处理数据的C代码,考虑采用一种只需读/写一一次的编码格式,用于确保函数在FPGA有效的实现。在C中设计一个高性能的FPGA实现比调试更有效,这就是问什么FPGA不能在性能debug模式下运行的原因。(翻译的不对?)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: