Files
nudt-compiler-cpp/doc/Lab6-并行与循环优化.md
2026-03-09 20:36:26 +08:00

175 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Lab6并行与循环优化
## 1. 本实验定位
Lab6 的目标是在 Lab5 基本标量优化之后,面向“循环密集型代码”继续优化。
本实验重点不是再补语义覆盖,而是提升中后端对循环结构的处理能力,为性能优化打基础。
核心方向:
1. 循环识别与规范化
2. 循环相关优化(如不变代码外提、强度削弱等)
3. 可并行循环分析与并行化改造(可选,按课程要求推进)
---
## 2. 先看这一点:循环优化依赖分析结果
循环优化通常依赖一组基础分析:
1. CFG 与支配关系
2. 循环层次信息LoopInfo
3. def-use/use-def 与副作用信息(保证变换合法)
如果这些基础信息不稳定,循环优化很容易“优化错程序”。
因此 Lab6 建议先把分析链路和验证手段搭好,再做具体变换。
---
## 3. Lab6 要求
需要同学完成:
1. 在现有 IR 上识别循环结构(至少能区分循环头、循环体、回边)。
2. 实现有效的循环优化,并保证语义不变。
3. 将循环优化并入 `PassManager`,与 Lab5 的优化流程协同工作。
4. 对优化前后结果做回归验证,并给出性能或代码规模对比(至少一种指标)。
5. 可选:尝试实现可并行循环识别与并行化改造(按课程要求)。
---
## 4. 当前代码框架(与 Lab6 相关)
1. IR 与分析
- `src/ir/IR.h`
- `src/ir/analysis/DominatorTree.cpp`
- `src/ir/analysis/LoopInfo.cpp`
2. IR 优化
- `src/ir/passes/...`
3. 入口与验证
- `src/main.cpp`
---
## 5. 需要修改的文件
1. 核心文件
- `src/ir/analysis/LoopInfo.cpp`(完善循环分析)
- `src/ir/passes/PassManager.cpp`(接入新的循环优化 pass
- 新增循环优化 pass 文件(建议放在 `src/ir/passes/` 下)
2. 视实现需要可能修改
- `src/ir/analysis/DominatorTree.cpp`(若分析信息不足)
- `src/ir/IR.h``src/ir/Instruction.cpp`(若需要补充指令属性)
- `src/ir/IRPrinter.cpp`(调试输出增强)
---
## 6. 可选优化方向
1. 循环不变代码外提LICM
2. 归纳变量简化与强度削弱
3. 循环展开(小规模、受控展开)
4. 循环分裂
5. 简单并行化识别(无跨迭代写后读/写后写冲突时标记可并行)
...
---
## 7. 简要优化原理
### 7.1 循环不变量外提Loop Invariant Code Motion
原理:
将循环中“每次迭代结果都不变”的表达式移动到循环外执行。若某表达式不依赖循环内变化的值,并且其操作数在循环内不被改写,则它是循环不变量。
作用:
减少循环体内重复计算,降低迭代开销。
实现思路:
先识别循环结构,再判断循环体中哪些表达式对所有迭代恒定。把这些表达式外提到循环前置块(或等价安全位置),循环体改用外提结果。
### 7.2 强度削弱Strength Reduction
原理:
将高开销运算替换为等价低开销运算。典型场景是把循环中的乘法/除法改写为增量更新(加减)。
作用:
降低每次迭代的算术成本,提高整体执行效率。
实现思路:
识别归纳变量与其线性相关表达式,判断是否可改写为递增/递减更新。引入辅助变量后,用加减替代高成本运算并保持语义一致。
### 7.3 循环展开Loop Unrolling
原理:
一次迭代中执行多份循环体副本。
作用:
减少控制指令比例,提高指令级并行机会,也有利于后续向量化或流水线优化。
实现思路:
选择展开因子,复制循环体并调整步长。若总迭代次数不能整除展开因子,需要保留余数迭代处理路径以保证结果正确。
### 7.4 循环分裂Loop Fission
原理:
把一个包含多类语句的循环拆成多个循环,每个循环执行原循环的一部分语句。
作用:
降低单个循环体复杂度,改善数据局部性,并为并行化/向量化创造条件。
实现思路:
先做数据依赖分析。若若干语句间不存在阻碍重排的依赖,可将其拆分到不同循环中,同时维持必要执行顺序保证语义不变。
### 7.5 循环并行化Loop Parallelization
原理:
把循环不同迭代同时执行,以利用多核并行能力。前提是迭代间不存在破坏语义的数据依赖。
作用:
在数据规模较大时可显著缩短运行时间,尤其适用于数值计算与批量数据处理。
实现思路:
先判定迭代间读写依赖。若可并行,则将迭代划分为任务并分发到线程/处理单元;结束后进行同步与归并,确保结果正确。
---
## 8. 推荐实验流程
1. 跑通循环分析。
2. 选择循环优化实现
3. 接入 `PassManager`
4. 用样例验证正确性。
5. 统计优化前后差异。
---
## 9. 构建与验证
```bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j "$(nproc)"
```
### 9.1 功能回归
```bash
./scripts/verify_ir_with_llvm.sh test/test_case/simple_add.sy out/ir --run
./scripts/verify_asm_with_qemu.sh test/test_case/simple_add.sy out/asm --run
```
### 9.2 优化效果对比(示例)
```bash
# 对比优化前后 IR/汇编输出(按你实现的开关或日志方式执行)
./build/bin/compiler --emit-ir test/test_case/simple_add.sy
./build/bin/compiler --emit-asm test/test_case/simple_add.sy
```
---