当前位置:首页 > OpenCL&HLS > 正文内容

通俗易懂的Quartus HLS vs Vitis HLS 优化技巧及对比

chanra1n7个月前 (05-29)OpenCL&HLS1397

随着深度学习应用的普及,FPGA作为强大且高效的硬件加速器,其高性能和低延迟特性备受关注。为了简化FPGA开发流程,高级综合工具(HLS)如Intel的Quartus HLS和Xilinx的Vitis HLS相继推出,为用户提供了基于C/C++的编程方式,从而降低了开发门槛。然而,如何使用这些工具进行有效的优化仍然是一个挑战。本文将深入讲解基于Quartus HLS和Vitis HLS的优化技术,并通过示例进行对比,帮助读者快速理解并上手。

在另一方面,随时网络上有很多专业的教程,但是写的可谓是乱七八糟,尽可能凑专业名词,让人很难理解,我撰写本文的一个初心,也是帮助大家理解,所以如果描述有偏差,请见谅!

1. 基础概念

Quartus HLS: Intel推出的高级综合工具,支持C++和SystemC语言,用于将高层次代码转换为硬件描述语言(HDL)。

参考文档:https://www.intel.cn/content/www/cn/zh/docs/programmable/683680/20-2/pro-edition-getting-started-guide.html

Vitis HLS: Xilinx推出的高级综合工具,支持C、C++和SystemC语言,目标是将高层次代码转换为可在FPGA上执行的硬件实现。

2. 常见优化技术

2.1 循环展开 (Loop Unrolling)

循环展开是将循环中的多个迭代展开为一组独立的语句,从而减少循环控制开销,并增加并行度。

简单例子:
假设我们有如下的循环,用于对一个数组进行操作。

// 原始代码for (int i = 0; i < 8; i++) {
    array[i] = array[i] * 2;}

展开后的代码:
如果我们将循环展开2倍,代码会变成如下:

for (int i = 0; i < 8; i += 2) {
    array[i] = array[i] * 2;
    array[i + 1] = array[i + 1] * 2;}

展开4倍,则变成:

for (int i = 0; i < 8; i += 4) {
    array[i] = array[i] * 2;
    array[i + 1] = array[i + 1] * 2;
    array[i + 2] = array[i + 2] * 2;
    array[i + 3] = array[i + 3] * 2;}

这样做的好处是减少了循环控制的开销,可以提高效率。

Quartus HLS:

void loop_unroll_example(int a[128], int b[128]) {
    #pragma unroll 4
    for (int i = 0; i < 128; i++) {
        b[i] = a[i] * 2;
    }}

Vitis HLS:

void loop_unroll_example(int a[128], int b[128]) {#pragma HLS unroll factor=4
    for (int i = 0; i < 128; i++) {
        b[i] = a[i] * 2;
    }}

对比:

  • Quartus HLS使用 #pragma unroll 指令,并指定展开倍数。

  • Vitis HLS使用 #pragma HLS unroll 指令,并使用 factor 参数指定展开倍数。

  • 本质上两者实现相似,都是为了增加并行度,但语法和参数可能略有不同。

2.2 循环流水化 (Loop Pipelining)

循环流水化是指将一个循环的多个迭代进行重叠执行,提高硬件资源的利用率和软件的执行效率。

假设你有一条生产线,需要依次完成取料、加工和装箱三个步骤:

  1. 第一步:取料

  2. 第二步:加工

  3. 第三步:装箱

如果让一个人(单个迭代)每次都先取料,再加工,再装箱,这样三个步骤需要按顺序来。
但如果采用流水化,让三个工人同时参与:

  • 工人A取料后,立刻交给工人B。

  • 工人B加工后,立刻交给工人C。

  • 工人C装箱完成后,继续处理下个循环。

这样,每个时间节点都能处理多个步骤,从而提高整体效率。

Quartus HLS:

void loop_pipeline_example(int a[128], int b[128]) {
    #pragma ii 1
    for (int i = 0; i < 128; i++) {
        b[i] = a[i] * 2;
    }}

Vitis HLS:

void loop_pipeline_example(int a[128], int b[128]) {#pragma HLS pipeline II=1
    for (int i = 0; i < 128; i++) {
        b[i] = a[i] * 2;
    }}

对比:

  • Quartus HLS使用 #pragma ii 指令,指定循环间隔 (Initiation Interval)。

  • Vitis HLS使用 #pragma HLS pipeline 指令,并指定 II 参数。

  • 两者语法不同,但功能一致,都是为了将循环内的不同迭代重叠执行。

2.3 数组分块 (Array Partition)

数组分块是将一个大数组分拆成多个小数组,从而增加数据访问的并行度。

简单例子:
假设我们有如下的数组,有8个元素。

int array[8] = {1, 2, 3, 4, 5, 6, 7, 8};

分块后的操作:
我们将数组分成两部分,每部分4个元素。

int part1[4] = {1, 2, 3, 4};int part2[4] = {5, 6, 7, 8};

这样可以同时访问part1和part2,从而提高处理速度。

Quartus HLS:

void array_partition_example(int a[128], int b[128]) {
    #pragma partition variable=a cyclic factor=4
    for (int i = 0; i < 128; i++) {
        b[i] = a[i] * 2;
    }}

Vitis HLS:

void array_partition_example(int a[128], int b[128]) {#pragma HLS array_partition variable=a cyclic factor=4
    for (int i = 0; i < 128; i++) {
        b[i] = a[i] * 2;
    }}

对比:

  • 两者都使用 #pragma 指令,并指定 cyclic 分块方式和 factor 分块因子。

  • 语法一致,但HLS工具不同,两者目的都是为了增加数据的并行访问能力。

3. 综合应用实例:卷积层优化

以下是一个基于深度卷积层的优化实例,展示了如何同时在Quartus HLS和Vitis HLS中进行循环展开、流水化和数组分块优化。

Quartus HLS:

void convolution(float input[32][32], float output[30][30], float kernel[3][3]) {
    #pragma ii 1
    for (int i = 1; i < 31; i++) {
        #pragma unroll 3
        for (int j = 1; j < 31; j++) {
            float result = 0;
            #pragma unroll
            for (int ki = 0; ki < 3; ki++) {
                for (int kj = 0; kj < 3; kj++) {
                    result += input[i + ki - 1][j + kj - 1] * kernel[ki][kj];
                }
            }
            output[i - 1][j - 1] = result;
        }
    }}

Vitis HLS:

void convolution(float input[32][32], float output[30][30], float kernel[3][3]) {
        #pragma HLS pipeline II=1#pragma HLS array_partition variable=input cyclic factor=2
    for (int i = 1; i < 31; i++) {#pragma HLS unroll factor=3
        for (int j = 1; j < 31; j++) {
            float result = 0;#pragma HLS unroll
            for (int ki = 0; ki < 3; ki++) {
                for (int kj = 0; kj < 3; kj++) {
                    result += input[i + ki - 1][j + kj - 1] * kernel[ki][kj];
                }
            }
            output[i - 1][j - 1] = result;
        }
    }}

扫描二维码推送至手机访问。

版权声明:本文由我的FPGA发布,如需转载请注明出处。

本文链接:https://myfpga.cn/index.php/post/425.html

分享给朋友:

“通俗易懂的Quartus HLS vs Vitis HLS 优化技巧及对比” 的相关文章

Ubuntu下Quartus OpenCL实现手写数字识别 QuartusPro 18.1 Update2 Arria10

Ubuntu下Quartus OpenCL实现手写数字识别 QuartusPro 18.1 Update2 Arria10

环境:Arria10 FPGA 10AXF40GAE 10AXF40GAAAMD Ryzen™ 7 7735HS 8核16线程(实测编译速度与主频和核心数有关,可以上X99的双路服务器)英睿达DDR5 4800Mhz 32G*2(注意,内存应大于8G,并且越大越好,否则...

Intel N3000 PAC开发板 Arria10FPGA 100G智能网卡 支持PCIE程序上传/调试 OpenCL OPAE

Intel N3000 PAC开发板 Arria10FPGA 100G智能网卡 支持PCIE程序上传/调试 OpenCL OPAE

近期,从小黄鱼上收了两块N3000的Intel PAC板子,FPGA型号和Microsoft 1768那个差不多,区别是这个支持100G网口,但是DDR4只有9G(组合后为9G)。型号是10AT115S1F45E1SG先把坑说前面,解决了能减少很多麻烦:To compile an AFU using...

使用Vitis HLS实现和优化Conv2D函数

使用Vitis HLS实现和优化Conv2D函数

现代卷积神经网络(CNNs)中,卷积操作(Conv2D)是最基本且计算量集中的部分。为了在嵌入式系统和FPGA平台上加速这一计算,我们可以利用Xilinx的Vitis高层次综合(HLS)工具。本文将介绍如何使用Vitis HLS实现一个基础的卷积操作,随后进行各种优化以提高其性能。一、基础实现我们首...

 使用Vitis HLS实现和优化DethSepConv函数

使用Vitis HLS实现和优化DethSepConv函数

1初版功能验证1.0设计框图+-----------------------------------+ |            Input Data &nb...

使用Vitis HLS实现和优化SPECK加解密函数

使用Vitis HLS实现和优化SPECK加解密函数

设计代码如下:// speck_hls.cpp #include "ap_int.h" // 定义密钥和回合数 #define WORD_SIZE 16 #define KEY_SIZE 4 #d...