HLS介绍 - 03 - 以计算为中心的算法

简介: HLS介绍 - 03 - 以计算为中心的算法

写在前面


本文参考UG998的第五章,介绍了以计算为中心的算法。它还包括示例和最佳实践建议,以最大限度地提高HLS生成的实现的性能。

概念


以计算为中心的算法是每个任务配置一次的算法,并且在任务期间不能更改其行为。硬件中的任务与C/C++程序中的函数调用相同。任务的大小由HLS用户控制。

image.png

图中显示了Sobel边缘检测操作的代码。这是一个以计算为中心的算法的示例,它可以划分为不同大小的任务。该算法是一种二维滤波操作,通过计算x和y方向上每个像素的梯度来计算图像中区域的边缘。正如目前编写的,此代码可以由HLS编译成FPGA实现。

要正确优化此算法,设计者必须首先确定任务的大小。任务的大小决定了生成的硬件模块需要配置的频率以及需要接收新批数据的频率。

任务设定方案一:

image.png

任务设定方案二:

image.png

上面的两个图显示了Sobel边缘检测代码的两种可能的任务定义。另一种选择(任务设定方案三)是将Sobel边缘检测代码整个定义为一个任务。


任务设定方案一,创建一个仅用于梯度计算的硬件模块。梯度计算适用于 3x3 像素窗口,不支持线或图像帧的概念。 这个选择的问题是这个选择执行的工作量与算法的自然工作分配之间的不匹配。Sobel 边缘检测适用于完整图像的范围。 这意味着对于任务大小的这种选择,设计人员必须确定如何将图像划分为使用 HLS 构建的任务处理器所需的 3x3 像素切片。 需要处理器或附加硬件模块来完成算法的功能。

任务设定方案二,处理每个任务的完整像素线。 这是对任务设定方案一的改进,因为它需要更少的附加模块来实现算法的完整功能。 这种方法还将与控制处理器的交互减少到每行一次。 调整任务大小以一次处理一行的问题在于底层操作需要多行来计算结果。 有了这个选择,可能需要一个复杂的控制机制来将图像行排序到 HLS 生成的硬件模块中。

任务设定方案三是此算法的最佳选择,因为它匹配代码中表示的每个函数调用的完整图像。 这个选择是以计算为中心的任务,因为生成的 FPGA 实现的配置在图像帧的持续时间内是固定的。 处理后的图像的大小可以在帧之间更改,但不能在任务开始后更改。

在确定任务的适当大小后,用户必须使用 HLS 编译器选项优化算法实现。 对于图中的代码,FPGA 实现的目标图像大小为 1080 像素,每秒 60 帧。 这转化为一个硬件模块,能够以 150 MHz 的时钟频率处理 1920 x 1080 像素,每个时钟周期的输入数据速率为 1 个像素。

数据速率优化


在 HLS 编译器中,代码优化从基线编译开始。 基线编译的目的是确定实现瓶颈的位置,并为衡量不同优化的效果设置一个参考点。 基线编译使用尽可能少的 FPGA 资源和最低的输入数据速率构建算法实现。 在示例中,基线编译每 40 个时钟周期产生 1 个像素的传入数据速率。

使用 HLS 编译器时,流水线优化是提高生成实现中的输入数据速率和并行度的方法。流水线将大型计算划分为可以并发执行的较小阶段。 当应用于循环时,流水线设置循环的启动间隔 (II)。

循环 II 通过影响开始 i+1 迭代所需的时钟周期数来控制循环的输入数据速率。 设计人员可以选择在算法代码中应用流水线优化的位置。

image.png

上图显示了流水线操作指令在图示代码的应用。pragma 直接应用于算法源的流水线优化。在代码的这一层,流水线编译指令的作用是在每个时钟周期计算 3x3 滤波器窗口中的一个字段。 因此,需要 9 个时钟周期来计算 3x3 窗口中的乘法运算以及一个额外的时钟周期来生成结果像素。 在应用层面,这意味着输入采样率每 10 个时钟周期变为 1 个像素,不足以满足应用需求。

image.png

上图中流水线操作指令在 j 循环中的应用,该循环跨越图像的列。 通过在该循环上应用流水线编译指令,HLS 实现可以实现每时钟周期 1 个像素的输入数据速率。 为了实现这个新的输入数据速率,编译器首先完全展开窗口计算循环,以便所有梯度乘法可以并行发生。 展开过程会例化额外的硬件并将内存带宽要求增加到每个时钟周期对输入图像进行九次内存操作。

尽管 HLS 编译器可以检测是否需要比算法中表达的更高的内存带宽,但编译器无法自动引入影响算法正确性的任何更改。 在此示例中,超出 HLS 生成模块边界的内存无法满足流水线优化所需的 9 次并发内存访问。

无论外部存储器上的端口数量如何,HLS 生成的模块只能连接到每个时钟周期能够处理一个事务的单个端口。 因此,必须修改算法以将内存带宽要求从模块输入端口移到由 HLS 编译器生成的内存。 该内部存储器类似于处理器中的缓存。 对于像 Sobel 边缘检测这样的图像处理算法,这个本地内存被称为行缓冲区。

行缓冲器是一个多组内部存储器,它为生成的实现提供每个时钟周期从三个不同行的像素并发访问。

在任何计算开始之前,实现行缓冲区的算法必须分配时间用足够的数据填充结构以满足计算的要求。这意味着要满足每个计算结果 9 次访问的内存要求,算法必须考虑数据通过行缓冲区的移动以及算法更改产生的额外带宽。

image.png

上图显示了图像像素通过行缓冲区的移动。

浅灰色框表示此内存结构当前存储的像素。 此块的目的是仅存储功能正确性所需的最少像素数,而不是存储整个图像。 如前所述,这种存储器结构的添加会在输入像素采样和输出像素计算之间引入延迟。 对于 3x3 窗口操作,例如在 j 循环中的应用的代码中,行缓冲区必须存储两个完整的图像行和第三行的前三个像素,然后才能计算出第一个输出像素。 深灰色和黑色框表示此延迟。

黑框突出显示来自源图像的下一个输入像素的写入位置。 深灰色框显示当前计算像素在输出图像中的位置。

HLS 使用来自 FPGA 架构的 BRAM 资源实现行缓冲器。 这些双端口存储器元件以组排列,其中一组对应一条线。因此,可用于算法计算的内存带宽从原来的每时钟周期 1 个像素增加到每时钟周期 3 个像素。 这仍然不足以满足每个时钟周期 9 个像素的要求。

为了满足每时钟周期 9 个像素的要求,除了行缓冲区之外,设计人员还必须向算法源代码添加一个存储器窗口。 存储器窗口是使用来自 FPGA 架构的 FF 资源实现的存储元素。 该存储器中的每个寄存器都可以独立于所有其他寄存器同时访问。 从逻辑上讲,由 FF 元素组成的内存可以采用最适合 C/C++ 中算法描述的任何形状。

下图显示了 Sobel 边缘检测算法的内存窗口。

image.png

灰色的中心像素突出显示了计算梯度的像素。 黑色列代表行缓冲区提供的 3 个像素。 在每个时钟周期,窗口的内容左移,为行缓冲区中的新列腾出空间。 窗口内存的数据重用和分布式实现提供了算法所需的九种内存操作。 该存储器不会在设计中引入额外的延迟。 窗口数据移动操作与行缓冲区的移动操作同时发生。

下图显示了通过分层内存架构从输入到计算的整体数据移动。

image.png

下图显示了实现分层存储器架构所需的算法代码更改。

image.png

这种分层架构允许 HLS 生成的IP实现每时钟周期 1 个像素的输入数据速率。 在此代码示例中,算法的计算内核位于 sobel_operator 函数中。 这段代码的主要变化是行和列循环各扩展一次迭代。 此扩展说明了行缓冲区引入的额外任务执行时间。 此外,对行缓冲区的写入操作由基于原始图像边界的 if 条件保护。 算法输出写操作基于输出图像定位,与原始图像偏移1行1列。

以计算为中心的应用程序可以嵌入 for 循环、if-else 语句等形式的控制语句。 这种算法的关键特征是它的功能和行为在任务的持续时间内是固定的。HLS 生成的模块根据给定的配置处理一批数据。 配置可以在每个任务之间更改,但不能在任务期间更改。

reference


  1. UG998
目录
相关文章
|
2月前
|
存储 分布式计算 算法
大数据-106 Spark Graph X 计算学习 案例:1图的基本计算、2连通图算法、3寻找相同的用户
大数据-106 Spark Graph X 计算学习 案例:1图的基本计算、2连通图算法、3寻找相同的用户
73 0
|
2月前
|
JSON 算法 数据可视化
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
这篇文章是关于如何通过算法接口返回的目标检测结果来计算性能指标的笔记。它涵盖了任务描述、指标分析(包括TP、FP、FN、TN、精准率和召回率),接口处理,数据集处理,以及如何使用实用工具进行文件操作和数据可视化。文章还提供了一些Python代码示例,用于处理图像文件、转换数据格式以及计算目标检测的性能指标。
80 0
测试专项笔记(一): 通过算法能力接口返回的检测结果完成相关指标的计算(目标检测)
|
3月前
|
算法 数据可视化 数据安全/隐私保护
基于LK光流提取算法的图像序列晃动程度计算matlab仿真
该算法基于Lucas-Kanade光流方法,用于计算图像序列的晃动程度。通过计算相邻帧间的光流场并定义晃动程度指标(如RMS),可量化图像晃动。此版本适用于Matlab 2022a,提供详细中文注释与操作视频。完整代码无水印。
|
3月前
|
算法 C++
如何精确计算出一个算法的CPU运行时间?
如何精确计算出一个算法的CPU运行时间?
|
4月前
|
算法 Go Python
[算法]计算斐波拉契数列
[算法]计算斐波拉契数列
|
4月前
|
算法
计算空间物体包围球的两种算法实现
计算空间物体包围球的两种算法实现
58 0
|
6月前
|
机器学习/深度学习 算法
**反向传播算法**在多层神经网络训练中至关重要,它包括**前向传播**、**计算损失**、**反向传播误差**和**权重更新**。
【6月更文挑战第28天】**反向传播算法**在多层神经网络训练中至关重要,它包括**前向传播**、**计算损失**、**反向传播误差**和**权重更新**。数据从输入层流经隐藏层到输出层,计算预测值。接着,比较预测与真实值计算损失。然后,从输出层开始,利用链式法则反向计算误差和梯度,更新权重以减小损失。此过程迭代进行,直到损失收敛或达到训练次数,优化模型性能。反向传播实现了自动微分,使模型能适应训练数据并泛化到新数据。
76 2
|
6月前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-2
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题
|
6月前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-1
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题
|
6月前
|
机器学习/深度学习 自然语言处理 算法
心得经验总结:机器翻译评测——BLEU算法详解(新增在线计算BLEU分值)
心得经验总结:机器翻译评测——BLEU算法详解(新增在线计算BLEU分值)
162 0

热门文章

最新文章