YunSDR小课堂-AIE编程指南(第52讲)

Graph编程介绍

一个AI引擎程序必须包括一个用C++编写的数据流图规范。自适应数据流(ADF)图是具有单个AI引擎内核或通过数据流连接的多个AI引擎内核的网络。图形可以使用特定构造与可编程逻辑(PL)、全局存储器和/或主机处理器交互。input_plio和output_plio端口对象可用于建立到可编程逻辑或来自可编程逻辑的流连接,input_gmio和output_gmio端口对象可用于建立到全局存储器或来自全局存储器的存储器映射连接,RTP(RTP参数)对象可用于在图形执行期间设置和控制内核所需的参数。

    一个graph可以有多个内核、输入和输出端口。graph连通性,相当于数据流图中的网络,在内核之间,内核和输入端口之间,或者内核和输出端口之间,并且可以配置为连接。当与graph中的内核所期望的缓冲区或数据流相等的所有数据样本变得可用时,graph执行,并且产生与graph中的所有内核的输出处所期望的缓冲区或数据流相等的数据样本。

    如C++模板支持中所述,您可以使用模板类或函数来编写AI引擎graph或kernel。可以使用AI Engine工具链编译和执行应用程序。本章介绍如何编写AI引擎程序。

准备kernel

内核是构成数据流图规范的基本构建块的计算函数。内核被声明为返回void的普通C/C ++函数,可以使用特殊数据类型作为参数。每个内核都必须在自己的源文件中定义。建议采用这种组织方式以实现可重用性和更快的编译速度。此外,内核源文件应该包括所有相关的头文件,以允许独立编译。注:要使AI Engine内核使用AI Engine API,请在内核源代码中包含以下文件:

    · #include“aie_API/aie.hpp”

    · #include“aie_API/aie_adf.hpp”

    建议使用头文件(本文档中的kernels. h)声明图中使用的所有内核的函数原型。一个例子如下。

1.png

在示例中,使用#ifndef和#endif来确保只包含一次包含文件,这是一种很好的C/C++实践。

    kernels.cc文件是该简单函数的实现文件。内核实现使用了两个int 16变量in_t和out_t,这两个变量使用data()成员函数分别指向输入和输出缓冲区的底层值。

2.png

创建数据流图(包括内核)

下面的过程描述了如何在C++中构造数据流图。

    1.在单独的头文件中定义应用程序图形类(例如project. h)。首先,添加自适应数据流(ADF)头(adf. h)并包含内核函数原型。ADF库包括在AI引擎上定义和执行图形所需的所有结构。

3.png

  2.使用adf命名空间中定义的对象定义您的图形类。所有的用户图都是从类图中派生出来的。

4.png

这是一个图类定义的开始,它声明了两个内核(第一个和第二个)。

3.向图中添加一些顶级输入/输出对象input_plio和output_plio。

5.png

 4.使用kernel::create函数实例化第一个和第二个C++内核对象,使用C函数simple的功能。

6.png

5.配置指定PLIO宽度的输入/输出对象和输入/输出文件,并添加连通性信息,相当于数据流图中的网络。在本说明书中,输入/输出对象由索引引用。简单函数中的第一个输入缓冲区或流参数在输入端口数组(in)中被分配索引0。后续的输入参数采用递增的连续索引。简单函数中的第一个输出缓冲区或流参数在输出端口(out)数组中被分配索引0。后续的输出参数采用递增的连续索引。

7.png

此图表示在前面的图形代码中指定的图形连接。在葡萄属IDE中打开编译结果时,可以查看图形连接。有关详细信息,请参阅《流图连接指南》(UG 1079)中的查看AI引擎编译摘要结果。如前图所示,顶层的输入端口连接到第一内核的输入端口,第一内核的输出端口连接到第二内核的输入端口,第二内核的输出端口连接到暴露给顶层的输出。当从外部源收集128字节的数据(32个复杂样本)到缓冲区中时,第一个内核执行。这是使用dimensions(first.in[0])={128}构造指定的。同样,当第二个内核的输入缓冲区具有作为第一个内核的输出而产生的有效数据时,第二个内核执行。最后,第二个内核的输出连接到顶级输出端口,并且dimensions(second.out[0])={128}指定在终止时内核将产生的数据样本的数量。

    buf 0和buf 0 d是分配给第一个内核输入缓冲区的乒乓缓冲区。类似地,buf 2和buf 2d是为第二个内核输出缓冲区分配的乒乓缓冲区。请注意,buf 1是从第一个内核输出到第二个内核的缓冲区,它不是一个乒乓缓冲区。这是因为第一个和第二个内核缓冲区都被放置在一个AI引擎瓦片中,它们将在其中顺序执行。有关详细信息,请参见“比率”。

    6.设置每个内核的源文件和切片使用情况。源文件kernel.cc包含内核第一和内核第二源代码。然后函数运行时间与周期预算的比率,称为运行时间比率,必须在0和1之间。周期预算是一个函数从其输入端消耗数据(当处理速率受限的输入数据流时)或在其输出端产生一个数据块(当处理速率受限的输出数据流时)所需的指令周期数。此周期预算可以通过更改块大小来影响。

8.png

7.定义包含图形类实例的顶级应用程序文件(例如project.cpp)。

9.png

 重要提示:

    ①默认情况下,mygraph.run()选项指定永久运行的图形。AI引擎编译器生成代码以在永久while循环中执行数据流图。要限制图形的执行以进行调试和测试,请mygraph.run<number_of_iterations>在图形代码中指定www.example.com()。指定的迭代次数可以是一次或多次。

   ②main函数必须有return语句。否则,AI引擎编译器将出错。

  ADF API具有返回枚举类型return_code,以显示API运行状态。主程序是图形的驱动程序。它用于加载、执行和终止图形。

    注:内核代码的编写方式必须确保在将两个内核分配给同一内核时不会发生名称冲突。

运行时间比率

运行时间比率是用户指定的约束,允许AI引擎工具灵活地将多个AI引擎内核放入单个AI引擎中,如果它们的总运行时间比率小于1。内核的运行时间比率可以使用以下公式计算。

公式1.png

周期预算是允许运行一次内核调用的周期数,它取决于系统吞吐量需求。

    内核运行一次的周期可以在初始设计阶段估计。例如,如果内核包含一个可以很好地流水线化的循环,并且每个循环都能够处理该数量的数据,则内核的一次运行的循环可以通过以下方式估计。

公式2.png

同步缓冲器的同步+功能初始化需要数十个周期,这取决于接口编号。在确定业绩目标时需要考虑到这一点。

    当向量化代码可用时,内核一次运行的周期也可以在aiesimulator中进行分析。如果将多个AI Engine内核放入单个AI Engine中,它们将以顺序方式一个接一个地运行,并且它们都将在graph::run的每次迭代中运行一次,除非存在多速率处理。这意味着以下内容。

    ·如果在graph::run的每次迭代中为内核分配AI Engine运行时百分比(由运行时约束指定)(或基于平均值,取决于系统要求),则可以满足内核性能要求。

    ·对于graph::run的单次迭代,内核所占的百分比不会超过运行时约束所指定的百分比。否则,它可能会影响位于同一AI引擎中的其他内核的性能。

    ·即使多个内核的汇总运行时间比率小于1,它们也不一定被放入单个AI引擎中。AI Engine内核到AI Engine的映射也受硬件资源的影响。例如,必须有足够的程序内存来允许内核在同一个AI引擎中,并且必须有流接口来允许所有内核在同一个AI引擎中。

    ·当多个内核被放入同一个AI引擎时,可能会节省资源。例如,同一AI引擎中内核之间的缓冲区是单个缓冲区,而不是乒乓缓冲区。

    ·提高内核的运行时比率并不一定意味着内核或图形的性能提高,因为性能还受内核的数据可用性以及进出图形的数据吞吐量的影响。过高的运行时比率设置可能会导致资源利用率低下。

    ·较低的运行时比率并不一定会将内核的性能限制在AI引擎的指定百分比内。例如,如果AI Engine中只有一个内核,则内核可以在所有数据都可用时立即运行,而不管设置了什么运行时间比率。

    ·不同顶层图中的内核不能放在同一个AI Engine中,因为图API需要独立控制不同的图。

    ·尽可能准确地设置运行时比率,因为它不仅影响要使用的AI Engine,还影响内核之间的数据通信路由。它还可能影响其他设计流程,例如功率估计。

文章来源:威视锐科技