Intel®高层次综合编译器专业版: 最佳实践指南

ID 683152
日期 12/04/2023
Public
文档目录

5.2.3. 实例:循环流水线和展开

以一种设计为例,其中需要将矩阵的每一列与矩阵中其他列进行点积运算,并将6个结果存储在不同的上层三角矩阵中。矩阵中其余单元设置为0。



代码可能类似于如下代码实例:
1.	#define ROWS 4
2.	#define COLS 4
3.	
4.	component void dut(...) {
5.		float a_matrix[COLS][ROWS]; // store in column-major format
6.		float r_matrix[ROWS][COLS]; // store in row-major format
7.	
8.		// setup...
9.	
10.		for (int i = 0; i < COLS; i++) {
11.			for (int j = i + 1; j < COLS; j++) {
12.	
13.				float dotProduct = 0;
14.				for (int mRow = 0; mRow < ROWS; mRow++) {
15.					dotProduct += a_matrix[i][mRow] * a_matrix[j][mRow];
16.				}
17.				r_matrix[i][j] = dotProduct;
18.			}
19.		}
20.	 	
21.	 // continue...
22.		
23.	}

可将在特定栏条目中迭代的循环展开,以此提高组件性能。如果该循环独立操作,则编译器将并行执行这些循环。

浮点操作通常必须按照源代码中显示的顺序执行,以保持数值精确度。但是也可使用-ffp-reassociate编译器标志来放宽浮点操作的顺序。随着浮点操作顺序的放宽,该循环中会出现以下情况:
  • 乘法操作可并行进行。
  • 加法运算可组成“加法器树”而非“加法器链”。
请查看教程: <quartus_installdir>/hls/examples/ tutorials/best_practices/floating_point_ops了解更多信息。
您可以通过展开位于第11行的j-loop来提高吞吐量,但是要允许编译器展开循环,您必须确保它具有constant bound。您可以通过启动位于j = 0而非j = i + 1j-loop来确保constant bound。您还必须添加预测语句以避免在j-loop的迭代 0,1,2,... i期间将无效数据分配到r_matrix
01: #define ROWS 4
02: #define COLS 4
03: 
04: component void dut(...) {
05: 	float a_matrix[COLS][ROWS]; // store in column-major format
06: 	float r_matrix[ROWS][COLS]; // store in row-major format
07: 
08: 	// setup...
09: 
10: 	for (int i = 0; i < COLS; i++) {
11: 
12: #pragma unroll
13: 			for (int j = 0; j < COLS; j++) {
14: 				float dotProduct = 0;
15: 
16: #pragma unroll

17: 				for (int mRow = 0; mRow < ROWS; mRow++) {
18: 					dotProduct += a_matrix[i][mRow] * a_matrix[j][mRow];
19: 				}
20: 
21: 				r_matrix[i][j] = (j > i) ? dotProduct : 0; // predication
22: 			}
23: 		}
24:  	}
25:  	
26: 	// continue...
27: 	
28: }

此时j-loop已完全展开。由于4个迭代之间无依赖关系,所以可同时运行。

请从 <quartus_installdir>/hls/examples/tutorials/best_practices/resource_sharing_filter处参阅resource_sharing_filter教程了解更多详细信息。

可继续在第10行展开循环,但展开此处的循环可能导致使用面积又再增加。允许编译器流水线处理此循环以代替将其展开,就可避免面积增加且可能i-loop仅有的II为1则仅多花4个时钟周期。如果II不为1,可在高级设计报告(report.html)中Loops Analysis页面的Details窗格找到改进建议。

以下为影响循环II的常见因素:
  • 循环携带依赖关系

    可在 <quartus_installdir>/hls/examples/tutorials/best_practices/loop_memory_dependency处参阅教程

  • 关键且长的循环路径
  • 循环II > 1的内部循环