ZHCU881D May 2020 – May 2024
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 位元素。在此循环中,编译器不知道循环将执行多少次。所以在我们的示例中,如果循环迭代的次数不是所选矢量宽度中元素数量的倍数,编译器就不能在最后一次循环迭代中将整个矢量存储到存储器中。例如,如果原始(未矢量化的)循环将执行 40 次迭代,而编译器将循环矢量化 16 次,则最后一次优化迭代将计算 16 个元素,但其中只有 8 个应该存储到存储器中。
C7000 ISA 具有某些矢量谓词特性,其中矢量谓词会影响哪些通道应该执行矢量运算。在这种情况下,BITXPND 指令生成矢量谓词,将在感知矢量谓词的存储指令中使用。此矢量存储指令 (VSTP16W) 使用矢量谓词来防止将上次迭代中仅作为矢量化过程的结果计算且不会原始循环中计算或存储的那些元素存储到存储器中。编译器尝试在矢量化过程中自动执行矢量谓词。矢量谓词有助于避免生成剥离式循环迭代的需求,这种迭代可抑制循环嵌套优化。
如果使用 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 以及与向量预测相关的其他指令。