ZHCU881C May   2020  – December 2023

 

  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 指令集具有许多功能强大的单指令多数据 (SIMD) 指令,可在一条指令中执行多个操作。为了利用这种优势,编译器会在可能而有益的情况下尝试对源代码进行矢量化。矢量化通常涉及使用矢量 (SIMD) 指令一次对数据的若干循环迭代执行操作。

以下示例从上一部分的示例中删除了 UNROLL pragma 和 MUST_ITERATE pragma。UNROLL(1) pragma 阻止了 C7000 编译器中的某些循环转换优化。

// weighted_vector_sum_v2.cpp
// Compile with "cl7x -mv7100 --opt_level=3 --debug_software_pipeline
// --src_interlist --symdebug:none weighted_vector_sum_v2.cpp"
void weighted_sum(int * restrict a,  int *restrict b,  int *restrict out,
                  int weight_a,      int weight_b,     int n)   
{
    for (int i = 0; i < n; i++)   {
        out[i] = a[i] * weight_a + b[i] * weight_b;
    }
}

下面显示生成的已被矢量化的内部编译器代码。 编译器的矢量化可通过“+= 16”地址增量和优化器临时变量名称中的“32x16”(表示临时变量中有 16 个 32 位元素)来推断。

;***    -----------------------g3:
;*** 6  -----------------------    if ( !((d$1 == 1)&U$33) ) goto g5;
;*** 6  -----------------------    VP$25 = VP$24;
;***    -----------------------g5:
;*** 7  -----------------------    VP$20 = VP$25;
;*** 7  -----------------------    __vstore_pred_p_P64_S32(VP$20, &(*(packed int (*)<[16]>)U$47), 
*(packed int (*)<[16]>)U$38*VRC$s32x16$001+*(packed int (*)<[16]>)U$42*VRC$s32x16$002);
;*** 6  -----------------------    U$38 += 16;
;*** 6  -----------------------    U$42 += 16;
;*** 6  -----------------------    U$47 += 16;
;*** 6  -----------------------    --d$1;
;*** 6  -----------------------    if ( L$1 = L$1-1 ) goto g3;

生成的汇编文件中获得的软件流水线信息块如下所示:

;*   SOFTWARE PIPELINE INFORMATION
;*
;*      Loop found in file               : weighted_vector_sum_v2.cpp
;*      Loop source line                 : 6
;*      Loop opening brace source line   : 6
;*      Loop closing brace source line   : 8
;*      Loop Unroll Multiple             : 16x
;*      Known Minimum Iteration Count    : 1
;*      Known Max Iteration Count Factor : 1
;*      Loop Carried Dependency Bound(^) : 1
;*      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
...
;*----------------------------------------------------------------------------*
;*        SINGLE SCHEDULED ITERATION
;*
;*        ||$C$C41||:
;*   0              TICK                               ; [A_U]
;*   1              VLD16W  .D1     *D0++(64),VBM0    ; [A_D1] |7| [SI]
;*   2              VLD16W  .D1     *D1++(64),VBM0    ; [A_D1] |7| [SI]
;*   3              NOP             0x4               ; [A_B]
;*   7              VMPYWW  .N2     VBM2,VBM0,VBL0    ; [B_N2] |7|
;*   8              VMPYWW  .N2     VBM1,VBM0,VBL1    ; [B_N2] |7|
;*   9              CMPEQW  .L1     AL0,0x1,D3        ; [A_L1] |6|  ^
;*  10              ANDW    .D2     D2,D3,AL1         ; [A_D2] |6|
;*     ||           ADDW    .L1     AL0,0xffffffff,AL0 ; [A_L1] |6|  ^
;*  11              CMPEQW  .S1     AL1,0,A0          ; [A_S1] |6|
;*  12      [!A0]   MV      .P2     P1,P0             ; [B_P] |6| CASE-1
;*     ||           VADDW   .L2     VBL1,VBL0,VB0     ; [B_L2] |7|
;*  13              VSTP16W .D2     P0,VB0,*A1(0)     ; [A_D2] |7|
;*     ||           ADDD    .M1     A1,0x40,A1        ; [A_M1] |6| [C1]
;*     ||           BNL     .B1     ||$C$C41||        ; [A_B] |6|
;*  14              ; BRANCHCC OCCURS {||$C$C41||}    ; [] |6|

本例比较了上一部分中的示例输出,以显示矢量化的效果:

  • “优化器”代码经过若干高级优化步骤,包括矢量化。(使用 -os 编译器选项时,此“优化器”代码显示在汇编文件中。)地址增量为 16,优化器临时变量的部分名称为“32x16”,表示 16 个 32 位元素。
  • 汇编文件中的“SOFTWARE PIPELINE INFORMATION”(软件流水线信息)注释块显示,循环已经按 16 倍展开。这可能表明也可能不表明矢量化已发生,但往往与矢量化有关。
  • 软件流水线循环现在使用 VMPYWW 和 VADDW 指令。指令助记符中的“V”通常(但不总是)表示编译器已经将代码序列矢量化(使用矢量/SIMD 指令)。
  • 负载和存储指令中更大的地址增量可以是矢量化已发生的另一条线索。

在此循环中,编译器不知道循环将执行多少次。所以在我们的示例中,如果循环迭代的次数不是所选矢量宽度中元素数量的倍数,编译器就不能在最后一次循环迭代中将整个矢量存储到存储器中。例如,如果原始(未矢量化的)循环将执行 40 次迭代,而编译器将循环矢量化 16 次,则最后一次优化迭代将计算 16 个元素,但其中只有 8 个应该存储到存储器中。

C7000 ISA 具有某些矢量谓词特性,其中矢量谓词会影响哪些通道应该执行矢量运算。在这种情况下,BITXPND 指令生成矢量谓词,将在感知矢量谓词的存储指令中使用。此矢量存储指令 (VSTP16W) 使用矢量谓词来防止将上次迭代中仅作为矢量化过程的结果计算且不会原始循环中计算或存储的那些元素存储到存储器中。编译器尝试在矢量化过程中自动执行矢量谓词。矢量谓词有助于避免生成剥离式循环迭代的需求,这种迭代可抑制循环嵌套优化。

注: 如果启用了 Corepac 存储器管理单元 (CMMU),并且存储与非法存储器页重叠,则矢量谓词存储可能会导致页面错误。任何在运行时小于非法存储器页 63 字节的存储器范围都应该在链接器命令文件中缩减长度。有关更多信息,请参阅 C7000 C/C++ 编译器用户指南 (SPRUIG8)。

如果使用 MUST_ITERATE pragma 向编译器提供有关循环迭代次数的信息,则可以避免进行向量预测。例如,如果已知上一个示例中的循环仅以 32 的倍数执行,并且最小迭代计数为 1024,则以下示例会改进生成的汇编代码:

// weighted_vector_sum_v3.cpp
// Compile with "cl7x -mv7100 --opt_level=3 --debug_software_pipeline
// --src_interlist --symdebug:none weighted_vector_sum_v3.cpp"
void weighted_sum(int * restrict a,  int *restrict b,  int *restrict out,
                  int weight_a,      int weight_b,     int n) 
{
    #pragma MUST_ITERATE(1024, ,32)
    for (int i = 0; i < n; i++) {
        out[i] = a[i] * weight_a + b[i] * weight_b;
    }
}

在编译后,这个修改后的示例会生成以下软件流水线信息块:

;*   SOFTWARE PIPELINE INFORMATION
;*
;*      Loop found in file               : weighted_vector_sum_v3.cpp
;*      Loop source line                 : 7
;*      Loop opening brace source line   : 7
;*      Loop closing brace source line   : 9
;*      Loop Unroll Multiple             : 32x
;*      Known Minimum Iteration Count    : 32
;*      Known Max Iteration Count Factor : 1
;*      Loop Carried Dependency Bound(^) : 0
;*      Unpartitioned Resource Bound     : 4
;*      Partitioned Resource Bound       : 4 (pre-sched)
;*
;*      Searching for software pipeline schedule at ...
;*         ii = 4  Schedule found with 5 iterations in parallel
...
;*----------------------------------------------------------------------------*
;*        SINGLE SCHEDULED ITERATION
;*
;*        ||$C$C36||:
;*   0              TICK                               ; [A_U]
;*   1              VLD16W  .D1     *D1++(128),VBM0   ; [A_D1] |8| [SI][C1]
;*   2              VLD16W  .D1     *D1(-64),VBM0     ; [A_D1] |8| [C1]
;*   3              VLD16W  .D1     *D2++(128),VBM0   ; [A_D1] |8| [SI][C1]
;*   4              VLD16W  .D1     *D2(-64),VBM0     ; [A_D1] |8| [C1]
;*   5              NOP             0x2               ; [A_B]
;*   7              VMPYWW  .N2     VBM2,VBM0,VBL1    ; [B_N2] |8|
;*   8              VMPYWW  .N2     VBM2,VBM0,VBL0    ; [B_N2] |8|
;*   9              VMPYWW  .N2     VBM1,VBM0,VBL2    ; [B_N2] |8|
;*  10              VMPYWW  .N2     VBM1,VBM0,VBL1    ; [B_N2] |8|
;*  11              NOP             0x2               ; [A_B]
;*  13              VADDW   .L2     VBL2,VBL1,VB0     ; [B_L2] |8|
;*  14              VST16W  .D2     VB0,*D0(0)        ; [A_D2] |8|
;*     ||           VADDW   .L2     VBL1,VBL0,VB0     ; [B_L2] |8|
;*  15              VST16W  .D2     VB0,*D0(64)       ; [A_D2] |8| [C0]
;*  16              ADDD    .D2     D0,0x80,D0        ; [A_D2] |7| [C0]
;*     ||           BNL     .B1     ||$C$C36||        ; [A_B] |7|
;*  17              ; BRANCHCC OCCURS {||$C$C36||}    ; [] |7|

由于添加了 MUST_ITERATE pragma,编译器知道绝不需要向量预测,并且不会执行向量预测。因此,编译器删除了 CMPEQW、ANDW、VSTP16W 以及与向量预测相关的其他指令。