HLS设计方法论 - 结构优化策略

简介: HLS设计方法论 - 结构优化策略

写在前面


本文参考赛灵思的官方手册UG1270,主要介绍了结构优化策略,以帮助我们更好的进行HLS的开发设计。

结构优化策略


C代码可以包含防止函数或循环以所需性能流水线化的描述。 这通常由 C 代码的结构或用于实现 PL 逻辑的默认逻辑结构暗示。 在某些情况下,这可能需要修改代码,但在大多数情况下,这些问题可以使用额外的优化指令来解决。

以下示例显示了使用优化指令来改进实现结构和流水线性能的情况。 在这个初始示例中,将 PIPELINE 指令添加到循环中以提高循环的性能。 此示例代码显示了在函数内部使用的循环。

#include "bottleneck.h"
dout_t bottleneck(...) {
  ...
  SUM_LOOP: for(i=3;i<N;i=i+4) {
  #pragma HLS PIPELINE
  sum += mem[i] + mem[i-1] + mem[i-2] + mem[i-3];
  }
  ...
}

当上面的代码编译到硬件中时,会出现以下消息作为输出:

INFO: [SCHED 61] Pipelining loop ‘SUM_LOOP’.

WARNING: [SCHED 69] Unable to schedule ‘load’ operation (‘mem_load_2’,

bottleneck.c:62) on array ‘mem’ due to limited memory ports.

INFO: [SCHED 61] Pipelining result: Target II: 1, Final II: 2, Depth: 3.

I

此示例中的问题是使用 PL 结构中的高效的块 RAM 资源来实现阵列。这导致开销小、高效的快速设计。 但Block RAM 的缺点是,与 DDR 或 SRAM 等其他存储器一样,它们的数据端口数量有限,通常最多两个。

在上面的代码中,需要来自 mem 的四个数据值来计算 sum 的值。 由于 mem 是一个数组并在只有两个数据端口的块 RAM 中实现,因此每个时钟周期只能读取(或写入)两个值。 使用这种配置,不可能在一个时钟周期内计算 sum 的值,从而消耗或产生 II 为 1 的数据(每个时钟处理一个数据样本)。

内存端口限制问题可以通过在内存数组上使用 ARRAY_PARTITION 指令来解决。 该指令将数组划分为更小的数组,通过提供更多的数据端口和允许更高性能的管道来改进数据结构。

使用下面显示的附加指令,数组 mem 被划分为两个双端口存储器,以便所有四个读取都可以在一个时钟周期内发生。 对阵列进行分区有多种选择。 在这种情况下,因子为 2 的循环分区确保第一个分区包含来自原始数组的元素 0、2、4 等,而第二个分区包含元素 1、3、5 等。因为分区确保有 现在有两个双端口块 RAM(总共有四个数据端口),这允许在单个时钟周期内读取元素 0、1、2 和 3。

#include "bottleneck.h"
dout_t bottleneck(...) {
#pragma HLS ARRAY_PARTITION variable=mem cyclic factor=2 dim=1
...
SUM_LOOP: for(i=3;i<N;i=i+4) {
#pragma HLS PIPELINE
sum += mem[i] + mem[i-1] + mem[i-2] + mem[i-3];
}
...
}

尝试流水线化循环和函数时可能会遇到其他此类问题。 下表列出了可能通过帮助减少数据结构中的瓶颈来解决这些问题的指令。

image.png

除了 ARRAY_PARTITION 指令外,阵列分区的配置还可用于自动对阵列进行分区。

流水线循环时,可能需要 DEPENDENCE 指令来删除隐含的依赖项。 这种依赖性由消息 SCHED-68 报告。

@W [SCHED-68] Target II not met due to carried dependence(s)

INLINE 指令删除函数边界。 这可用于将逻辑或循环向上一层层次结构。 通过将函数包含在其上方的函数中,将其逻辑流水线化可能更有效,将循环合并到它们上面的函数中,其中 DATAFLOW 优化可用于同时执行所有循环,而无需没有中间子函数调用的开销。 这可能会导致更高性能的设计。

对于无法使用所需的 II 流水线化循环的情况,可能需要 UNROLL 指令。 如果一个循环只能在 II = 4 的情况下进行流水线化,它会将系统中的其他循环和函数限制为 II = 4。在某些情况下,可能值得展开或部分展开循环以创建更多逻辑和消除潜在的瓶颈。 如果循环只能达到 II = 4,那么将循环展开 4 倍将创建可以并行处理循环的四次迭代并达到 II = 1 的逻辑。

减少延迟


当编译器完成最小化启动间隔 (II) 后,它会自动寻求最小化延迟。 下表中列出的优化指令可以帮助指定特定的延迟或通知编译器实现低于生成的延迟,即指示编译器满足延迟指令,即使它导致更高的 II。 这可能会导致性能较低的设计。

通常不需要延迟指令,因为大多数应用程序具有所需的吞吐量但没有所需的延迟。 当硬件功能与处理器集成时,处理器的延迟通常是系统中的限制因素。

如果循环和函数没有流水线化,吞吐量会受到延迟的限制,因为在当前任务完成之前,任务不会开始读取下一组输入。

image.png

循环优化指令可用于展平循环层次结构或将连续循环合并在一起。 延迟的好处是因为它通常在控制逻辑中花费一个时钟周期来进入和离开由循环创建的逻辑。 循环之间的转换次数越少,完成设计所需的时钟周期数就越少。

减少面积


在硬件中,实现逻辑功能所需的资源数量称为设计区域。 设计面积也指资源在固定尺寸的PL结构上使用的面积。 当硬件太大而无法在目标设备中实现时,以及当硬件功能占用可用区域的百分比非常高(> 90%)时,该区域很重要。 当尝试将硬件逻辑连接在一起时,这可能会导致困难,因为连接线本身需要资源。

在满足所需的性能目标(或 II)后,下一步可能是在保持相同性能的同时减少面积。 这一步可以是最佳的,因为如果硬件功能以所需的性能运行,并且在 PL 的剩余空间中不实施其他硬件功能,则通过减少面积没有任何好处。

最常见的区域优化是数据流内存通道的优化,以减少实现硬件功能所需的块 RAM 资源数量。 每个设备具有数量有限的 Block RAM 资源。

如果您使用了 DATAFLOW 优化并且编译器无法确定设计中的任务是否是流式数据,它会使用乒乓缓冲区实现数据流任务之间的内存通道。 这些需要两个块 RAM,每个块的大小为 N,其中 N 是要在任务之间传输的样本数(通常是在任务之间传递的数组的大小)。 如果设计是流水线式的,并且数据实际上是从一个任务流到下一个任务,并以顺序方式产生和消耗值,那么您可以通过使用 STREAM 指令来指定数组将在一个 使用简单 FIFO 的流式传输方式,您可以为其指定深度。 深度较小的 FIFO 使用寄存器实现,PL 结构有许多寄存器。

  • 对于以相同速率产生和消耗数据的任务,指定它们之间的数组以深度为 1 进行流式传输。
  • 对于将数据速率降低 X 到 1 倍的任务,在任务的输入处指定数组以 X 的深度进行流式传输。函数中在此之前的所有数组也应具有 X 的深度以确保 硬件功能不会因为 FIFO 已满而停止。
  • 对于将数据速率提高 1 到 Y 倍的任务,在任务的输出处指定数组以 Y 的深度进行流式传输。函数中此之后的所有数组也应具有 Y 的深度以确保 硬件功能不会因为 FIFO 已满而停止。

下表列出了在尝试最小化用于实现设计的资源时要考虑的其他指令。

image.png

ALLOCATION 和 RESOURCE 指令用于限制操作数量并选择使用哪些内核(硬件资源)来实现操作。 例如,您可以将函数或循环限制为仅使用一个乘法器,并指定它使用流水线乘法器来实现。

如果 ARRAY_PARITION 指令用于改进启动间隔,您可能需要考虑使用 ARRAY_RESHAPE 指令。 ARRAY_RESHAPE 优化执行与数组分区类似的任务,但是,重塑优化将通过分区创建的元素重新组合到具有更宽数据端口的单个块 RAM 中。 这可能会阻止所需块 RAM 资源数量的增加。

如果 C 代码包含一系列具有相似索引的循环,将循环与 LOOP_MERGE 指令合并可能会允许进行一些优化。 最后,如果流水线区域中的一段代码只需要以低于该区域其余部分的启动间隔运行,则使用 OCCURENCE 指令指示可以优化此逻辑以以较低的速率执行。

设计优化工作流程


在执行任何优化之前,建议在项目中创建一个新的构建配置。 使用不同的构建配置允许将一组结果与另一组结果进行比较。 除了标准的 Debug 和 Release 配置之外,还可以使用 Manage Build Configurations for Project 工具栏按钮在 Project Settings 窗口中创建具有更有用名称的自定义配置(例如 Opt_ver1 和 UnOpt_ver)。

不同的构建配置不仅可以比较结果,还可以比较用于实现 FPGA 的日志文件甚至输出 RTL 文件(RTL 文件只推荐给非常熟悉硬件设计的用户)。

高性能设计的基本优化策略是:

  • 创建初始或基线设计。
  • 流水线化循环和函数。 应用 DATAFLOW 优化以同时执行循环和函数。
  • 解决任何限制流水线的问题,例如数组瓶颈和循环依赖(使用 ARRAY_PARTITION 和 DEPENDENCE 指令)。

总之,目标是始终首先满足性能,然后再减少面积。 如果策略是用最少的资源创建设计,只需省略提高性能的步骤,尽管基线结果可能非常接近最小的设计。

在整个优化过程中,强烈建议在编译后查看控制台输出(或日志文件)。 当编译器无法达到优化的指定性能目标时,它会自动放宽目标(时钟频率除外)并创建具有可满足目标的设计。 查看编译日志文件和报告的输出以了解已执行的优化非常重要。

reference


  1. UG1270
目录
相关文章
|
26天前
|
缓存 前端开发 JavaScript
利用代码分割优化前端性能:策略与实践
在现代Web开发中,代码分割是提升页面加载性能的有效手段。本文介绍代码分割的概念、重要性及其实现策略,包括动态导入、路由分割等方法,并探讨在React、Vue、Angular等前端框架中的具体应用。
|
22天前
|
机器学习/深度学习 人工智能 自然语言处理
深度剖析:注意力机制中的兼容性函数及其优化策略
深度剖析:注意力机制中的兼容性函数及其优化策略
|
3月前
|
存储 DataWorks 数据挖掘
方案内容的技术细节评估
该方案通过明确的文档指导、可用的代码示例及数据分析需求评估,提升用户体验。文档需详列操作步骤与资源要求,并提供异常处理指南;代码示例应保持更新,附带错误处理与必要注释;方案需评估数据处理规模、复杂分析功能及实时性需求,以确定是否需额外优化或补充策略。总体而言,方案在基础架构描述上表现良好,但需在技术细节等方面进一步完善。
|
5月前
业务系统架构实践问题之平衡SPI的语义精确性和实现的复杂性问题如何解决
业务系统架构实践问题之平衡SPI的语义精确性和实现的复杂性问题如何解决
|
7月前
|
项目管理 数据库
简述软件质量的概念及质量保障体系,简述SQA的基本目标,简述CMM的分级结构及其主要特征,简述软件质量标准等级及适用范围
简述软件质量的概念及质量保障体系,简述SQA的基本目标,简述CMM的分级结构及其主要特征,简述软件质量标准等级及适用范围
231 0
|
存储 SQL Web App开发
迭代技术方案设计文档规范
规范在团队管理中的意义无需多言,对于开发团队来说,技术方案的设计和执行无疑是日常工作中很重要的一块。编码一定要在思考清楚之后在开始,以免把问题带入线上,或者反复修改造时间、精力的浪费。
564 0
|
Arthas 缓存 运维
如何设计高效的基准场景?揭秘大厂的实战策略!
RESAR性能工程中,场景分为基准、容量、稳定性、异常。每类场景对应不同目标。 基准场景是为找到系统中明显配置及软件Bug,也为容量场景提供可对比的基准数据。基准场景要有确定结论。
168 0
量化现货交易系统开发(功能细化及源码分析)
量化现货交易系统开发(功能细化及源码分析)
|
人工智能 数据挖掘 API
马丁策略量化交易系统开发(逻辑特性)| 马丁策略量化交易源码参考
马丁策略,全名马丁格尔策略,倍投原理,是在亏损之后进行加倍下注,直到连本带利赚回本金为止。随着下注的连续失败,追加的筹码也在成倍增加,只要赢一次,就可以赢回所有的本金和利润,简单来说马丁策略就是输掉后加倍投资的交易策略。量化是在用户授权交易所账户API的前提下,选择交易策略和主流货币,启用AI智能数据分析引擎帮助用户自动匹配“大概率”交易策略,根据数据模型自动分析市场关键点,通过毫秒级闪电交易,向交易所发出买入卖出指令,大幅提高账户交易速率与频率。
|
消息中间件 存储 SQL
谈谈如何构建优化的流数据架构(上)
流处理最初是一种“特定群体”技术。但随着 SaaS、物联网和机器学习的快速发展,各行各业的组织现在都在试行或全面实施流分析。
谈谈如何构建优化的流数据架构(上)