ZHCU881D May   2020  – May 2024

 

  1.   1
  2.   请先阅读
    1.     关于本手册
    2.     相关文档
    3.     商标
  3. 2引言
    1. 2.1 C7000 数字信号处理器 CPU 架构概述
    2. 2.2 C7000 分离式数据路径和功能单元
  4. 3C7000 C/C++ 编译器选项
    1. 3.1 概述
    2. 3.2 为性能选择编译器选项
    3. 3.3 了解编译器优化
      1. 3.3.1 软件流水线
      2. 3.3.2 矢量化和矢量谓词
      3. 3.3.3 自动使用流引擎和流地址生成器
      4. 3.3.4 循环折叠和循环合并
      5. 3.3.5 自动内联
      6. 3.3.6 if 转换
  5. 4基本代码优化
    1. 4.1  迭代计数器和限制的有符号类型
    2. 4.2  浮点除法
    3. 4.3  循环携带依赖和 restrict (限制)关键字
      1. 4.3.1 循环携带依赖
      2. 4.3.2 restrict (限制)关键字
      3. 4.3.3 运行时别名消歧
    4. 4.4  函数调用和内联
    5. 4.5  MUST_ITERATE 和 PROB_ITERATE Pragma 与属性
    6. 4.6  if 语句和嵌套的 if 语句
    7. 4.7  内在函数
    8. 4.8  矢量类型
    9. 4.9  待使用和避免的 C++ 特性
    10. 4.10 流引擎
    11. 4.11 流地址生成器
    12. 4.12 优化库
    13. 4.13 存储器优化
  6. 5了解汇编注释块
    1. 5.1 软件流水线处理阶段
    2. 5.2 软件流水线信息注释块
      1. 5.2.1 循环和迭代计数信息
      2. 5.2.2 依赖和资源限制
      3. 5.2.3 启动间隔 (ii) 和迭代
      4. 5.2.4 常量扩展
      5. 5.2.5 使用的资源和寄存器表
      6. 5.2.6 阶段折叠
      7. 5.2.7 存储器组冲突
      8. 5.2.8 循环持续时间公式
    3. 5.3 单个调度迭代注释块
    4. 5.4 识别流水线故障和性能问题
      1. 5.4.1 阻止循环进行软件流水线作业的问题
      2. 5.4.2 软件流水线故障消息
      3. 5.4.3 性能问题
  7. 6修订历史记录

软件流水线

像 C7000 这样的超长指令字 (VLIW) 数字信号处理器 (DSP) 依靠循环的软件流水线实现性能最大化。软件流水线 是一种方法,其中源循环的连续迭代是重叠的,以便在整个循环中尽可能多的周期里利用 CPU 上的功能单元。

下图显示有软件流水线和无软件流水线的情况下循环迭代的执行。您可以看到,在没有软件流水线的情况下,循环被调试,因此循环迭代 i 完成之后迭代 i+1 才开始。在有软件流水线的情况下,迭代会重叠。因此,只要能够保持正确,即可在迭代 i 完成之前开始迭代 i+1。与其他方法相比,这种方法通常使机器资源的利用率更高。在软件流水线循环中,即使单个迭代可能需要 s 个周期才能完成,但每隔 ii 个周期就会启动一个新迭代。

C7000 软件流水线对执行的影响图 3-2 软件流水线对执行的影响

在高效的软件流水线循环中,ii 远小于 sii 称为启动间隔,其是启动迭代 i 与启动迭代 i+1 之间的周期数。s 是完成第一个迭代所需的周期数,或者等于软件流水线循环的单次调度迭代的长度。

编译器尝试对最里面的源循环进行软件流水线处理。这些循环里面不再有任何其他循环。请注意,在编译过程中,软件流水线发生在内联之后和可将循环合并的循环转换之后,因此在某些情况下,您可能会看到编译器执行软件流水线的代码比预期的更多。

经过软件流水线处理后,循环有三个主要阶段,如下图所示:

  • pipe-up (prolog) 阶段,在此期间启动重叠的迭代。
  • steady-state (kernel) 阶段,在此期间继续启动迭代。
  • pipe-down (epilog) 阶段,在此期间允许完成任何尚未完成的迭代。
C7000 有 prolog 和 epilog 的循环迭代图 3-3 有 prolog 和 epilog 的循环迭代

以下示例显示了简单加权矢量和的源代码。

// weighted_vector_sum.cpp
// Compile with "cl7x -mv7100 --opt_level=3 --debug_software_pipeline
// --src_interlist --symdebug:none weighted_vector_sum.cpp"

void weighted_sum(int * restrict a, int *restrict b, int *restrict out,
                  int weight_a, int weight_b, int n)
{
    #pragma UNROLL(1)
    #pragma MUST_ITERATE(1024, ,32)
    for (int i = 0; i < n; i++)
    {
        out[i] = a[i] * weight_a + b[i] * weight_b;
    }
}

为了简化第一个软件流水线示例,使用了两个 pragma:

  • UNROLL pragma 告知编译器勿执行矢量化,这是一种转换方法,将在下一部分中说明。
  • MUST_ITERATE pragma 传递了有关循环执行次数的信息,这会在本文档后面说明。本例使用此 pragma 防止生成“重复循环”,这也会在本文档后面说明。

然后使用以下命令编译此代码:

cl7x --opt_level=3 --debug_software_pipeline --src_interlist --symdebug:none weighted_vector_sum.cpp

--symdebug:none 选项阻止编译器在汇编中生成调试信息和关联的调试指令。此调试信息与本文档的论述无关,如果包含该信息,则会不必要地延长此处所示的示例。一般不用关闭调试生成,因为调试信息的生成不会降低性能。

由于使用了 --src_interlist 选项,所以编译器生成的汇编文件未被删除,并包含以下内容:

;*----------------------------------------------------------------------------*
;*   SOFTWARE PIPELINE INFORMATION
;*
;*      Loop found in file               : weighted_vector_sum.cpp
;*      Loop source line                 : 10
;*      Loop opening brace source line   : 11
;*      Loop closing brace source line   : 13
;*      Known Minimum Iteration Count    : 1024                    
;*      Known Max Iteration Count Factor : 32
;*      Loop Carried Dependency Bound(^) : 0
;*      Unpartitioned Resource Bound     : 2
;*      Partitioned Resource Bound       : 2 (pre-sched)
;*
;*      Searching for software pipeline schedule at ...
;*         ii = 2  Schedule found with 7 iterations in parallel
;*
;*      Partitioned Resource Bound(*)    : 2 (post-sched)
. . .
;*----------------------------------------------------------------------------*
;*        SINGLE SCHEDULED ITERATION
;*
;*        ||$C$C36||:
;*   0              TICK                               ; [A_U] 
;*   1              SLDW    .D1     *D1++(4),BM0      ; [A_D1] |12| 
;*     ||           SLDW    .D2     *D2++(4),BM1      ; [A_D2] |12| 
;*   2              NOP     0x5     ; [A_B] 
;*   7              MPYWW   .N2     BM2,BM0,BL0       ; [B_N] |12| 
;*     ||           MPYWW   .M2     BM3,BM1,BL1       ; [B_M2] |12| 
;*   8              NOP     0x3     ; [A_B] 
;*  11              ADDW    .L2     BL1,BL0,B0        ; [B_L2] |12| 
;*  12              STW     .D1X    B0,*D0++(4)       ; [A_D1] |12| 
;*     ||           BNL     .B1     ||$C$C36||        ; [A_B] |10| 
;*  13              ; BRANCHCC OCCURS {||$C$C36||}    ; [] |10| 
;*----------------------------------------------------------------------------*
||$C$L1||:    ; PIPED LOOP PROLOG
;          EXCLUSIVE CPU CYCLES: 8
 
           TICK                               ; [A_U] (R) (SP) <1,0> 
||         SLDW    .D1     *D1++(4),BM1      ; [A_D1] |12| (P) <1,1> 
||         SLDW    .D2     *D2++(4),BM0      ; [A_D2] |12| (P) <1,1> 
 
 
           MV      .L2X    A7,B0             ; [B_L2] |7| (R) 
||         TICK                               ; [A_U] (P) <2,0> 
 
           MV      .L2X    A8,B1             ; [B_L2] |7| (R) 
||         SLDW    .D1     *D1++(4),BM0      ; [A_D1] |12| (P) <2,1> 
||         SLDW    .D2     *D2++(4),BM1      ; [A_D2] |12| (P) <2,1> 
 
           MV      .S2     B0,BM2            ; [B_S2] (R) 
||         MV      .L2     B1,BM3            ; [B_L2] (R) 
||         TICK                               ; [A_U] (P) <3,0> 
 
 
           MPYWW   .N2     BM2,BM1,BL0       ; [B_N] |12| (P) <0,7> 
||         MPYWW   .M2     BM3,BM0,BL1       ; [B_M2] |12| (P) <0,7> 
||         SLDW    .D1     *D1++(4),BM0      ; [A_D1] |12| (P) <3,1> 
||         SLDW    .D2     *D2++(4),BM1      ; [A_D2] |12| (P) <3,1> 
 
           TICK                               ; [A_U] (P) <4,0> 
 
           MPYWW   .N2     BM2,BM1,BL0       ; [B_N] |12| (P) <1,7> 
||         MPYWW   .M2     BM3,BM0,BL1       ; [B_M2] |12| (P) <1,7> 
||         SLDW    .D1     *D1++(4),BM0      ; [A_D1] |12| (P) <4,1> 
||         SLDW    .D2     *D2++(4),BM1      ; [A_D2] |12| (P) <4,1> 
 
           MV      .D2     A6,D0             ; [A_D2] (R) 
||         ADDD    .D1     SP,0xfffffff8,SP  ; [A_D1] (R) 
||         TICK                               ; [A_U] (P) <5,0> 

;** --------------------------------------------------------------------------*
||$C$L2||:    ; PIPED LOOP KERNEL
;          EXCLUSIVE CPU CYCLES: 2

           ADDW    .L2     BL1,BL0,B0        ; [B_L2] |12| <0,11> 
||         MPYWW   .N2     BM2,BM0,BL0       ; [B_N] |12| <2,7> 
||         MPYWW   .M2     BM3,BM1,BL1       ; [B_M2] |12| <2,7> 
||         SLDW    .D1     *D1++(4),BM0      ; [A_D1] |12| <5,1> 
||         SLDW    .D2     *D2++(4),BM1      ; [A_D2] |12| <5,1>            BNL     .B1     ||$C$L2||         ; [A_B] |10| <0,12> 
||         STW     .D1X    B0,*D0++(4)       ; [A_D1] |12| <0,12> 
||         TICK                               ; [A_U] <6,0>
;** --------------------------------------------------------------------------*
||$C$L3||:    ; PIPED LOOP EPILOG
;          EXCLUSIVE CPU CYCLES: 7
;**     -----------------------    return;

           ADDD    .D2     SP,0x8,SP         ; [A_D2] (O) 
||         LDD     .D1     *SP(16),A9        ; [A_D1] (O) 
||         ADDW    .L2     BL1,BL0,B0        ; [B_L2] |12| (E) <4,11> 
||         MPYWW   .N2     BM2,BM0,BL1       ; [B_N] |12| (E) <6,7> 
||         MPYWW   .M2     BM3,BM1,BL0       ; [B_M2] |12| (E) <6,7> 

           STW     .D1X    B0,*D0++(4)       ; [A_D1] |12| (E) <4,12> 
           ADDW    .L2     BL1,BL0,B0        ; [B_L2] |12| (E) <5,11> 
           STW     .D1X    B0,*D0++(4)       ; [A_D1] |12| (E) <5,12> 
           ADDW    .L2     BL0,BL1,B0        ; [B_L2] |12| (E) <6,11> 
           STW     .D1X    B0,*D0++(4)       ; [A_D1] |12| (E) <6,12> 

           RET     .B1     ; [A_B] (O) 
||         PROT                               ; [A_U] (E) 

           ; RETURN OCCURS {RP}              ; [] (O) 

此汇编输出显示编译器生成的汇编文件中的软件流水线循环以及部分软件流水线信息注释块,其中包括关于各种循环特征的重要信息。

如果编译器成功地对循环进行软件流水线处理,那么编译器生成的汇编代码将包含软件流水线信息注释块,其中包含有关“ii = xx Schedule found with yy iterations in parallel”的消息。启动间隔 (ii) 用于度量软件流水线循环开始执行循环新迭代的频率。启动间隔越小,执行整个循环所需的周期就越少。软件流水线循环信息还包括循环开始的源行、对循环资源和延迟要求的描述以及循环是否已展开(还有其他信息)。使用 –mw 编译时,该信息还包含单次调度迭代副本。

在本例中,实现的启动间隔 (ii) 是 2 个周期,并行运行的迭代次数为 7。

注释块还包括软件流水线循环的单次调度迭代 视图。从软件流水线循环的单次调度迭代视图中,可看到编译器如何转换代码以及编译器如何调度软件流水线循环的一次迭代与软件流水线中的迭代重叠。有关如何解释此注释块中的信息,请参阅节 5.2