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

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

6.1.1. 删除循环携带的依赖项

基于从优化报告来的反馈,您可以通过实现更简单的存储器访问模式来删除循环携带的依赖项。

请参考如下内核:

1 #define N 128
 2 
 3 __kernel void unoptimized (__global int * restrict A,
 4                            __global int * restrict B,
 5                            __global int* restrict result)
 6 {
 7   int sum = 0;
 8 
 9   for (unsigned i = 0; i < N; i++) {
10     for (unsigned j = 0; j < N; j++) {
11       sum += A[i*N+j];
12     }
13     sum += B[i];
14   }
15 
16   * result = sum;
17 }

unoptimized的内核优化报告类似于以下内容:

  • 报告的第一行表明 Intel® FPGA SDK for OpenCL™ Offline Compiler成功推断外部循环的流水线化执行,并且每隔一个循环启动一个新的循环迭代。
  • due to Pipeline structure消息表明离线编译器创建一个流水线结构,该结构导致外部循环迭代每两个周期启动一次。该行为不是您构建内核代码方式的结果。
    注: 对于如何构建您单个work-item内核的建议,请参阅单个Work-Item内核的良好设计实践小节。
  • 报告中第一行中的剩余消息表明,由于变量sum的数据依赖性,在子循环中循环一次执行单个迭代。存在该数据依赖性是因为每个外部循环迭代需要先返回从前一次迭代来的sum值,然后内部循环才能开始执行。
  • 报告的第二行通知您内部循环以流水线方式执行,并且没有限制性能的循环携带的依赖项。

要优化该内核的性能, 请删除变量sum的数据依赖项,以使子循环中的外部循环迭代不会串行执行。执行以下任务去耦这两个循环中涉及sum的计算:

  1. 定义局部变量(例如,sum2)以仅用于内部循环。
  2. 使用从步骤1来的局部变量存储A[i*N + j]的累加值作为内部循环迭代。
  3. 在内部循环中,存储变量sum来存储B[i]的累加值和局部变量中存储的值。
以下是重构的内核optimized
 1 #define N 128
 2 
 3 __kernel void optimized (__global int * restrict A,
 4                          __global int * restrict B,
 5                          __global int * restrict result)
 6 {
 7   int sum = 0;
 8 
 9   for (unsigned i = 0; i < N; i++) {
10     // Step 1: Definition
11     int sum2 = 0;
12 
13     // Step 2: Accumulation of array A values for one outer loop iteration
14     for (unsigned j = 0; j < N; j++) {
15       sum2 += A[i*N+j];
16     }
17 
18     // Step 3: Addition of array B value for an outer loop iteration
19     sum += sum2;
20     sum += B[i];
21   }
22 
23   * result = sum;
24 }

类似于以下内容的优化报告指示变量sum上循环携带的依赖项被成功移除:

在优化报告中仅看到以下消息时,您就已经成功解决了所有循环携带依赖项的问题:

  • Pipelined execution inferred针对最内部循环。
  • Pipelined execution inferred. Successive iterations launched every 2 cycles due to: Pipeline structure针对全部其它循环。