AI 引擎可在每个周期内执行多次矢量加载或存储操作。但这些操作必须以不同存储体为目标才能并行执行加载或存储操作。如果在同一周期内多次访问同一存储体,就会发生存储器停滞。
存储器种类包括内核、RTP 缓冲器与系统存储器之间的窗口缓冲器和 DMA FIFO。系统存储器包括前 32 字节、栈和堆之间的内核同步信息。静态变量位于堆内,而函数控制逻辑则位于栈内。系统存储器占据连续存储体。工具可以在特定存储体上自动或手动完成窗口缓冲器、RTP 缓冲器、DMA FIFO 和系统存储器的布局。为缓解这些存储器之间的存储器停滞,请尝试尽可能将其布局到独立存储体内。但如果无法为所有存储器都找到独立的存储体,或者在同一个存储器上发生多次访问,那么仍可能发生存储器停滞。
总之,编译器会尝试尽可能在同一个周期内调度多次存储器访问,但也存在例外情况。源自同一指针的多次存储器访问调度为在不同周期内执行。如果编译器在同一个周期内对多个变量或指针调度执行多项操作,则可能发生存储体冲突。每个存储体都有其自己的仲裁器用于在所有请求之间执行仲裁,且仲裁采用循环方式执行。解决所有请求后,就会释放存储器停滞。
您可通过“Performance Metrics”(性能指标)分析来识别是否需要对存储器停滞执行分析。
- 选择Trace(追踪)视图。
- 选择底部的Stalls(停滞)视图,然后从下拉列表中选择Memory Stalls(存储器停滞)。图 1. Trace视图中的“Memory Stall”
此处停滞名为 MS_<NUM>。编号随时间增加。每次停滞都包含下列关联信息。
- Stall ID(停滞 ID)
- 存储器停滞 ID。停滞发生时间越早,编号越小。此编号在所有类型的停滞中都是唯一的。
- Stalled Tile(停滞的拼块)
- 已停滞的内核所在的 AI 引擎 tile。
- Stalled Kernel(停滞的内核)
- 已停滞的内核。此内核名为
<Kernel_function_name>.<Schedule_ID>.<Graph_instance_name>
。有时,它显示为_main
,随后,需通过交叉探测来查找实际的内核函数。 - Start (ns)(开始 (ns))
- 发生停滞的开始时间
- Duration (ns)(持续时间 (ns))
- 停滞的持续时间。
- PC
- 发生停滞时的程序计数器。
- Bank Conflict(存储体冲突)
- 发生停滞的存储器。
- Buffer 1、Buffer 2、Buffer 3
- 导致存储器停滞的缓冲器。可能仅有一个缓冲器,也可能有多个缓冲器导致停滞。
- 单击Stalls(停滞)视图中的每一行停滞时,它会转至Trace(追踪)视图中存储器停滞的起始位置。通过缩放Trace(追踪)视图即可观察存储器停滞的发生频率,以及运行中的内核中发生停滞的位置。 注释: 如果在运行中的内核中重复发生大量存储器停滞,这表明停滞可能循环发生。最好对此问题进行调查并解决。如果仅在内核开始运行时发生一次存储器停滞,或者在内核运行期间仅发生少量停滞,通常可将其忽略。根据导致停滞的缓冲器名称可以识别出导致停滞的是窗口缓冲器、系统缓冲器或其它缓冲器。如果是可在 graph 中加以控制的窗口缓冲器或 RTP 缓冲器,那么可以使用约束对其进行手动布局,前提是能够找到更好的布局方法。如果是系统存储器(名为
system<NUM>+<NUM>
),则需识别停滞中所涉及的变量。 - 单击特定停滞对应的行,然后切换至Events(事件)视图。 图 2. “Memory Stall”的Events视图
-
Events视图可显示器件中发生的事件。发生存储器停滞的周期会高亮显示。您可看到发生 DM_BANK_CONFLICT 事件的 tile(拼块),以及正在读取或写入的变量。在前图中,同一周期内正在同时读取
delay_line
变量和eq_coef1
变量。 - 请尝试浏览停滞周期之前或之后的其它周期以寻找线索。例如,下图显示了前一个停滞周期之后发生的事件。此外还能看到,在同一周期内的不同部分(128 位)同时也在读取
delay_line
和eq_coef1
。通过检验源代码和汇编代码可以发现,在同一周期内对delay_line
和eq_coef1
发出了 256 位,因而导致存储器停滞。由于存储器停滞,两次 256 位存储器访问被拆分到两个周期中。图 3. “Memory Stall”的Events视图
在 AI 引擎内核编码最佳实践指南 (UG1079) 的“含虚拟资源注解的加载和存储”中记录了一种解决方案。例如,重定义指向
eq_coef0
和delay_line
的指针,并为其添加注解__aie_dm_resource_a
。改用下列新指针。const v8cint16 __aie_dm_resource_a* __restrict coeff = (v8cint16 __aie_dm_resource_a*) eq_coef0; const v8cint16 coe = *coeff;
v16cint16 __aie_dm_resource_a* __restrict p_buff = (v16cint16 __aie_dm_resource_a*) &delay_line; v16cint16 buff=*p_buff;
下表列出了导致存储器停滞的部分可能场景以及可能的解决方案。
源 | 目标 | 停滞类型 | 可能的解决方案 | 注释 |
---|---|---|---|---|
单个内核 | 单个存储体上的缓冲器 | 存储器停滞 |
单个内核访问同一存储体上的多个存储器。 或者,单个内核对同一存储体上的单个存储器进行多次访问。 (每个周期均可包含两次加载和一次存储) |
|
相邻 AI 引擎 tile 上的多个内核 | 单个存储体上的多个缓冲器 | 存储器停滞 |
|
多个内核访问同一存储体上的多个存储器。 |