style(doc): 重构 Lab6 文档结构

master
Lane0218 6 days ago
parent 262aad15f2
commit 72db506f71

@ -2,43 +2,13 @@
## 1. 本实验定位
Lab6 的目标是在 Lab5 基本标量优化之后,面向“循环密集型代码”继续优化。
本实验重点不是再补语义覆盖,而是提升中后端对循环结构的处理能力,为性能优化打基础。
Lab6 的重点是在 Lab5 基本标量优化之后,继续围绕循环结构开展更进一步的性能优化。本实验不再以补齐语义覆盖为主,而是把重点放在循环识别、循环变换以及可并行循环分析等问题上,为进一步提升最终生成代码的性能打基础。
核心方向:
## 2. Lab6 要求
1. 循环识别与规范化
2. 循环相关优化(如不变代码外提、强度削弱等)
3. 可并行循环分析与并行化改造(可选,按课程要求推进)
本实验需要完成的事情包括:在现有 IR 上识别循环结构,至少能够区分循环头、循环体与回边;实现有效的循环优化,并保证变换前后语义一致;将这些优化接入 `PassManager`,使其能够与 Lab5 的优化流程协同工作;最后通过回归测试和性能或代码规模对比,验证优化结果的正确性与收益。若课程要求涉及并行部分,也可以继续尝试可并行循环识别与并行化改造。
---
## 2. 先看这一点:循环优化依赖分析结果
循环优化通常依赖一组基础分析:
1. CFG 与支配关系
2. 循环层次信息LoopInfo
3. def-use/use-def 与副作用信息(保证变换合法)
如果这些基础信息不稳定,循环优化很容易“优化错程序”。
因此 Lab6 建议先把分析链路和验证手段搭好,再做具体变换。
---
## 3. Lab6 要求
需要同学完成:
1. 在现有 IR 上识别循环结构(至少能区分循环头、循环体、回边)。
2. 实现有效的循环优化,并保证语义不变。
3. 将循环优化并入 `PassManager`,与 Lab5 的优化流程协同工作。
4. 对优化前后结果做回归验证,并给出性能或代码规模对比(至少一种指标)。
5. 可选:尝试实现可并行循环识别与并行化改造(按课程要求)。
---
## 4. 相关文件
## 3. 相关文件
以下文件与本实验内容相关,建议优先阅读。
@ -47,96 +17,46 @@ Lab6 的目标是在 Lab5 基本标量优化之后,面向“循环密集型代
- `src/ir/analysis/LoopInfo.cpp`
- `src/ir/passes/PassManager.cpp`
---
## 5. 可选优化方向
1. 循环不变代码外提LICM
2. 归纳变量简化与强度削弱
3. 循环展开(小规模、受控展开)
4. 循环分裂
5. 简单并行化识别(无跨迭代写后读/写后写冲突时标记可并行)
...
## 4. 当前基础与前置准备
---
循环优化通常依赖一组相对稳定的基础分析,包括 CFG 与支配关系、循环层次信息(`LoopInfo`),以及 def-use/use-def 和副作用信息。只有这些基础信息足够稳定,后续的循环变换才不容易“优化错程序”。因此,在正式实现具体优化之前,建议先把分析链路与验证手段理顺。
## 6. 简要优化原理
## 5. 可实现的优化方向与实现提示
### 6.1 循环不变量外提Loop Invariant Code Motion
本实验可以选择的方向包括循环不变代码外提、归纳变量简化与强度削弱、循环展开、循环分裂,以及简单的并行化识别。如果你的实现还需要围绕当前框架补充其他循环相关优化,也可以按需扩展。
原理:
将循环中“每次迭代结果都不变”的表达式移动到循环外执行。若某表达式不依赖循环内变化的值,并且其操作数在循环内不被改写,则它是循环不变量。
### 5.1 循环不变量外提Loop Invariant Code Motion
作用:
减少循环体内重复计算,降低迭代开销。
循环不变代码外提的核心,是把循环中每次迭代结果都不变的表达式移动到循环外执行。若某个表达式不依赖循环内变化的值,并且其操作数在循环体内不被改写,那么它就具备外提条件。这样做的直接收益,是减少循环体内的重复计算,降低迭代开销。实现时,通常需要先识别循环结构,再判断哪些表达式对所有迭代都恒定,然后把它们外提到循环前置块或其他等价安全位置。
实现思路:
先识别循环结构,再判断循环体中哪些表达式对所有迭代恒定。把这些表达式外提到循环前置块(或等价安全位置),循环体改用外提结果。
### 5.2 强度削弱Strength Reduction
### 6.2 强度削弱Strength Reduction
强度削弱的思路,是把高开销运算替换为等价的低开销运算。循环中的典型场景,是把乘法、除法等操作改写为递增或递减更新。这样可以降低每次迭代的算术成本,提高整体执行效率。实现时,通常需要先识别归纳变量以及与其线性相关的表达式,再判断是否可以通过引入辅助变量,用加减更新替代高成本运算。
原理:
将高开销运算替换为等价低开销运算。典型场景是把循环中的乘法/除法改写为增量更新(加减)。
### 5.3 循环展开Loop Unrolling
作用:
降低每次迭代的算术成本,提高整体执行效率。
循环展开的做法,是在一次迭代中执行多份循环体副本,以减少控制指令比例,并提升指令级并行机会。它也常常能为后续向量化或流水线优化创造条件。实现时,需要选择合适的展开因子,复制循环体并调整步长;如果总迭代次数不能整除展开因子,还需要保留余数迭代路径以保证结果正确。
实现思路:
识别归纳变量与其线性相关表达式,判断是否可改写为递增/递减更新。引入辅助变量后,用加减替代高成本运算并保持语义一致。
### 5.4 循环分裂Loop Fission
### 6.3 循环展开Loop Unrolling
循环分裂是把一个包含多类语句的循环拆成多个循环,每个循环只执行原循环中的一部分语句。这样做通常有助于降低单个循环体的复杂度,改善数据局部性,并为并行化或向量化提供更好的前提。实现时,一般需要先做数据依赖分析;只有在若干语句之间不存在阻碍重排的依赖时,才适合将其拆分到不同循环中。
原理:
一次迭代中执行多份循环体副本。
### 5.5 循环并行化Loop Parallelization
作用:
减少控制指令比例,提高指令级并行机会,也有利于后续向量化或流水线优化。
循环并行化的目标,是让不同迭代可以并发执行,以利用多核并行能力。它成立的前提,是迭代间不存在破坏语义的数据依赖。若分析结果表明循环可以并行,就可以进一步考虑任务划分、执行与归并;如果课程对并行部分没有强制要求,这一方向也可以作为可选扩展来实现。
实现思路:
选择展开因子,复制循环体并调整步长。若总迭代次数不能整除展开因子,需要保留余数迭代处理路径以保证结果正确。
## 6. 推荐实验流程
### 6.4 循环分裂Loop Fission
比较自然的推进顺序是:先跑通循环分析,再选择一种或几种循环优化逐步实现,然后接入 `PassManager`,最后结合测试与输出对比检查优化结果是否正确、是否带来预期收益。
原理:
把一个包含多类语句的循环拆成多个循环,每个循环执行原循环的一部分语句。
作用:
降低单个循环体复杂度,改善数据局部性,并为并行化/向量化创造条件。
实现思路:
先做数据依赖分析。若若干语句间不存在阻碍重排的依赖,可将其拆分到不同循环中,同时维持必要执行顺序保证语义不变。
### 6.5 循环并行化Loop Parallelization
原理:
把循环不同迭代同时执行,以利用多核并行能力。前提是迭代间不存在破坏语义的数据依赖。
作用:
在数据规模较大时可显著缩短运行时间,尤其适用于数值计算与批量数据处理。
实现思路:
先判定迭代间读写依赖。若可并行,则将迭代划分为任务并分发到线程/处理单元;结束后进行同步与归并,确保结果正确。
---
## 7. 推荐实验流程
1. 跑通循环分析。
2. 选择循环优化实现
3. 接入 `PassManager`
4. 用样例验证正确性。
5. 统计优化前后差异。
---
## 8. 构建与验证
## 7. 构建与验证
```bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j "$(nproc)"
```
### 8.1 功能回归
### 7.1 功能回归
```bash
./scripts/verify_ir.sh test/test_case/simple_add.sy test/test_result/ir --run
@ -147,7 +67,7 @@ cmake --build build -j "$(nproc)"
完成 Lab6 后,不能只检查 `simple_add` 这类单个样例,而应对 `test/test_case` 下全部测试用例逐个回归;如有需要,也可以自行编写批量测试脚本统一执行。
### 8.2 优化效果对比(示例)
### 7.2 优化效果对比(示例)
```bash
# 对比优化前后 IR/汇编输出(按你实现的开关或日志方式执行)
@ -156,5 +76,3 @@ cmake --build build -j "$(nproc)"
```
这里的 `simple_add` 只用于展示如何观察单个样例的输出差异;实际评估优化效果时,仍应结合更多测试用例,必要时覆盖全部测试集。
---

Loading…
Cancel
Save