一些改进AI引擎应用程序的延迟和吞吐量的方法
扫描二维码
随时随地手机看文章
虽然AI引擎是软件可编程的,但为了在改善AI引擎的延迟和吞吐量方面获得最佳结果,了解实际硬件上发生的事情非常重要。如果你是一个FPGA设计者,你会发现很多并行的FPGA编码。
让我们试着把图表的不同部分分开,一步一步地理解硬件上发生了什么,以及我们如何改进它。
在本教程中,我们仍然使用来自本教程加速系统的相同ai引擎应用程序。
我们试图理解本教程中的代码,在本教程中模拟运行它,并试图估计本教程中图形的延迟。
注意:本教程是使用2025.1创建的。工具流程在其他版本的工具中可能会有所不同。
输入PLIO输入存储器
正如我们在本教程中看到的,内核首先处理来自输入缓冲区的数据。当在AI引擎上使用缓冲区时,内核在开始之前等待缓冲区被图中的数据集填满。
在启用了Traces的情况下运行AI Engine仿真时,我们可以看到内核的第一次迭代是在523ns之后开始的。这意味着在大约523 ns后,ping缓冲区被32个样本填满。
输入缓冲区满之前的延迟取决于两个因素:
PL输入PLIO的速度
输入缓冲区的大小。填充缓冲区所需的数据越少,内核启动速度就越快。然而,这只是减少了初始延迟,并将增加图的总体延迟。因为如果您正在处理较小的数据集,则需要更频繁地切换上下文。因此,您将增加获取缓冲区锁的延迟,如果您的内核是管道的,您将获得更多的实例,您必须填充管道。所以一般来说,为了提高图的吞吐量,你需要增加缓冲区的大小。
在前一篇文章中,我们已经看到两个输出样本之间的时间戳间隔为6.4 ns,对应于156.25 MHz的频率,这是我们平台的默认PL频率。
这意味着AI引擎模拟器假设我们的PL输入和接收来自AI引擎的数据运行在156.25 MHz。
然而,查看AMD Versal AI Edge设备(DS958)的数据表,我们可以看到AI引擎到PL接口可以在-1L速度级设备(如TE0950板上的设备)上以高达500 MHz的频率运行。
我们可以通过在project.h中声明输入PLIO时添加频率作为参数来告诉AI Engine编译器我们打算以500mhz的频率运行PLIO
在进行更改并运行AI Engine模拟器并启用trace后,我们可以看到内核的第一次调用现在发生在368ns之后
有一种方法可以改善这种延迟。
我们现在的样本到达了AIE-ML的边界,频率为500MHz。AI引擎的速度等级为-1L,运行频率为1000ghz。
目前,我们在500MHz的频率下以32位的数据包发送PL数据。在AIE-ML阵列内部,流是32位的,频率为1000MHz。这意味着通过时钟域交叉,我们只使用了AIE-ML流所能支持的发送带宽的一半。
为了优化这一点,AIE-ML的本机PL接口实际上是64位的。这样,您可以从PL到AI引擎接口(64位,500MHz)和从AI引擎接口到AI - ml阵列获得相同的带宽。
PLIO接口的大小也在图.h中的PLIO声明中定义。我们可以把它改成plio_64_bits
我们还必须将模拟文件更改为每行有2个样本(即4个数字,每2个复杂整数16样本)。我已将文件input_64.txt附加到该页。
在进行更改并运行AI引擎模拟器并启用trace后,我们可以看到内核的第一次调用现在发生在337ns之后
这基本上非常接近我们在不减少输入缓冲区大小(或使用流)的情况下所能获得的最佳“初始”延迟。
注意:这并不是填充缓冲区的真正延迟。这还包括初始化数组的一些延迟(如跟踪中灰色的_main_init行所示)。因此,如果ai - ml数组已经初始化并等待数据,那么从第一个示例进入到内核启动的延迟将会低得多
然后,一旦内核开始从ping缓冲区读取数据并将其写入其输出缓冲区,输入pong缓冲区就会并行填充,为下一次内核调用做好准备。从图形层面来看,我们无法在输入端做出更多改进
我们可以减少内核处理数据所花费的时间,但这是我们将在下一个教程中看到的。
内核并行化
在之前的教程中,我们已经看到内核第一和内核第二运行在同一个磁贴上,一个接一个,并在同一个缓冲区上工作,即内核第一和内核第二之间没有双缓冲区。
如果您不需要更快地处理数据,这是一个很好的资源节省(和省电)。在这个场景中,内核第二等待内核第一完成获取输入数据的处理。对于第一次迭代,如果内核在相同的tile上工作,这将是相同的情况。但是,当内核第二运行时,即使从pong输入缓冲区中有数据可用,内核第一也不会运行。
在这个场景中,我们可以看到第一次图迭代(内核第二次调用的结束)在大约1,270 ns后结束。
第四次(我们模拟的最后一次迭代)在4123秒后结束
如果您希望获得高吞吐量,您可能希望内核在不同的ai - ml上运行。我们已经看到,运行时属性决定了内核是否合并到同一个tile中。因此,如果我们想要最大的吞吐量我们可以说我们的内核将需要所有可用的处理时间通过将运行时设置为1(最大值)
我们可以在数组报告中看到,两个内核现在在两个不同的块上实现
从图表报告中我们可以看到,在两个内核之间实现了双缓冲区,这意味着内核将能够并行运行。
在运行AI引擎模拟器并启用trace后,我们现在可以看到两个内核并行运行。我们可以看到,第二次调用kernel first和第一次调用kernel second是同时发生的。
当查看内核秒第一次调用结束的时间时,它发生在1,290ns之后。所以比内核共用一个瓦片时稍微多一点。这可以用以下事实来解释:每个内核都必须管理用于通信的双缓冲区的锁。
对于图的第一次执行,预计会看到类似的时间,因为即使内核从不同的块运行,因此不共享该块的处理时间,内核第二仍然需要等待内核第一完成其第一次执行,然后才能通过ping缓冲区获得可用的输入数据。
但是,如果我们看一下内核的第四次迭代,它发生在2751 ns之后,因此我们可以看到延迟和吞吐量的巨大改进。
这个数字很容易解释。第4个内核第2次调用的结束速度快了1372ns (4,123 ns - 2,751 ns)。我们可以看到,内核的3次迭代是并行进行的,每次内核迭代大约需要460 ns来完成。
输出存储器到输出PLIO
输出PLIO上的延迟与我们在输出上看到的情况类似。当检查输出文本文件(output.txt)时,我们可以看到以下第一行:
我们可以看到第一个样本发生在1344 ns之后。我们已经可以看到系统延迟的改善,如果您还记得在更改之前的教程中,第一个输入样本在1,5104 ns之后到达。
我们还可以看到,以下连续的采样发生在6.4ns之后,提醒我们输入的156.25 MHz频率。
我们可以在project.h中对输出PLIO进行与输入PLIO相同的设置:将频率更改为500MHz并将数据宽度更改为64位。
在构建图形并运行AI Engine模拟器后,我们可以看到以下第一行输出文本文件(output.txt):
我们可以看到,我们稍微改善了第一个样本的延迟,但最重要的是,连续的样本现在只有2ns的间隔,我们每次接收2个样本,大大提高了系统的吞吐量。
总结
在本教程中,我们已经看到了如何修改图形文件,以实现更低的延迟和更高的吞吐量在我们的AI引擎应用程序。我们仍然可以对内核代码进行改进,通过向量化来提高系统的吞吐量和延迟。这是我们将在下一个教程中看到的内容。
本文编译自hackster.io





