Intel® FPGA SDK for OpenCL™ Pro Edition: 最佳实践实践指南

ID 683521
日期 9/26/2022
Public
文档目录

8.3.2. 将数据预加载到局部存储器

通常认为局部存储器比全局存储器小得多,但是它的吞吐量显然更高并且延迟更低。不同于全局存储器访问,内核可以随机访问局部存储器而不会造成任何性能损失。 构建您的内核代码时,请尝试按顺序访问全局存储器,并先将数据缓存在片上局部存储器中,然后您内核将该数据用于计算。

Intel® FPGA SDK for OpenCL™ Offline Compiler在FPGA中以片上存储器模块实现OpenCL™局部存储器。片上存储器模块有两个读和写端口,它们的时钟频率可以是OpenCL内核工作频率的两倍。该双倍时钟频率使得存储器被“double pumped,”(双泵),从而使同一存储器的带宽增加一倍。因此,每个片上存储器模块最多支持四个同时访问。

理想情况下,对每个bank的访问均匀分布在bank的片上存储器模块上。因为单个时钟周期内只能有四个同时访问到片上存储器模块,所以分配访问有助于避免bank争用。

这种banking配置通常是有效的;然而,离线编译器必须创建一个复杂的存储器系统来适应大量的bank。大量的bank可能会使仲裁网络复杂化,并可能降低整体系统性能。

因为离线编译器实现位于FPGA片上存储器模块中得局部存储器,所以离线编译器必须选择局部存储器系统的大小。离线编译器用于确定局部存储器系统大小的方法取决于您OpenCL代码中使用的局部数据类型。

优化局部存储器访问

要优化局部存储器访问效率,请参考以下指导:

  • 实现某些优化技术,例如循环展开,可能会导致更多并发存储器访问。
    警告:
    增加存储器访问数量会使存储器系统复杂化并且降低性能。

  • 尽可能将内核中唯一局部存储器访问的数量限制在四个或者更少,从而简化局部存储器子系统。

    在对局部存储器只有四个或更少的存储器访问时,您就可以获得最好的局部存储器性能。如果对特定存储器系统的访问个数大于四,那么离线编译器会将存储器系统的片上存储器模块排列成带状(banked)配置。

  • 如果您有函数作用域(function scope)局部数据,离线编译器会在编译时静态调整您函数体内定义的局部数据的大小。您应该通过指示离线编译器将存储器设置为需要的大小来定义局部存储器,向上舍入到最接近的值,即,2的幂。

  • 避免使用local_mem_size属性。请使用__local内核变量而不是__local内核自变量。

    请参阅 Intel® FPGA SDK for OpenCL™ Pro版编程指南中的优化Pointer-to-Local Memory(指针到局部存储器)大小的编程策略小节获得更多信息。

  • 访问局部存储器时,尽可能使用最简单的地址计算并避免非强制性的指针数学运算。

    Intel® 建议使用该编码风格,通过允许离线编译器的静态代码分析来更好的保证访问模式,从而降低FPGA资源利用率并提高局部存储器效率。复杂地址计算和指针数学运算可能会阻止离线编译器创建代表不同部分的独立存储器系统,进而导致面积使用增加和运行时性能下降。

  • 尽可能避免存储指向存储器的指针。随后从存储器中检索指针时,存储的指针通常会阻止确定已访问数据集的静态编译器分析。存储指向存储器的指针基本上总是会导致次优的面积和性能结果。

  • 创建为2字节的幂的局部数组单元,使得离线编译器提供有效的存储器配置。

    只要有可能,离线编译器就会自动将局部存储器的单元填充为2的幂,以提供更高效的存储器配置。例如,如果您有一个包含3个字符串(char)的结构,离线编译器会将其填充成4个字节,而非创建一个更窄和更深的多访问存储器(即,1字节宽的存储器配置)。然而,在某些情况下,离线编译器可能不会填充存储器,例如,当内核通过指针算法间接访问局部存储器时。

    要确定离线编译器是否已经填充局部存储器,请在Kernel Memory Viewer中查看存储器维度。如果离线编译器无法填充局部存储器,就会在区域报告中打印如下消息:

    Memory system contains arrays whose element size is not a power of two.  This may result in extra loads and stores, leading to stalls.  Try padding structs to a power of two, or break them into multiple arrays of smaller elements.