本文档总结了从拿到一个完整的参考算子实现开始,如何编写初学者教程的完整工作流程。
参考实现 → 分析理解 → 创建最小实现 → 统一验证方式 → 创建工具函数 → 创建运行脚本 → 编写教程 → 整理代码 → 生成 README
目标:理解参考实现的结构、关键特性和设计思路
任务清单:
- 阅读参考实现的代码,理解整体结构
- 识别核心算法逻辑
- 识别性能优化技术(tiling、多任务并行、内存优化等)
- 识别关键配置参数(tile 大小、任务数量、任务类型等)
- 理解数据流动(GDRAM ↔ NRAM ↔ 计算单元)
- 记录关键 API 的使用方式
输出:
- 对参考实现的理解文档(可选)
- 关键特性列表
目标:创建一个简化的、易于理解的版本,去掉复杂的优化技术
任务清单:
- 创建
{operator}_minimal.mlu文件 - 移除 tiling 逻辑(如果参考实现有)
- 移除多任务并行(使用单个任务)
- 简化数据规模(使用较小的测试数据)
- 保留核心算法逻辑
- 保留基本的 BangC API 使用(
__memcpy、核心计算 API 等) - 统一验证方式:使用 vecadd 的验证模式
- 实现 CPU 参考函数(如
cpu_{operator}) - 使用
verify_rm_rm函数进行结果验证 - 移除其他验证方式(如自定义验证函数、文件保存等)
- 实现 CPU 参考函数(如
- 保留完整的 Host 端流程(设备初始化、内存分配、数据拷贝、Kernel 启动、结果验证)
- 确保代码可以编译和运行
设计原则:
- 简单优先:优先保证代码简单易懂,而不是性能
- 完整性:虽然简化,但要保证功能完整(能运行、能验证)
- 渐进式:最小实现应该能够自然地演进到完整实现
输出:
{operator}_minimal.mlu文件
目标:将所有算子的验证方式统一为 vecadd 的标准形式
标准验证模式(参考 vecadd.mlu):
// 1. CPU 参考实现(生成参考结果)
cpu_{operator}(host_input, host_ref, ...);
// 2. 使用统一的验证函数
(void)verify_rm_rm(host_output, host_ref, total_elements, "{operator_name}", 1e-6f, 1e-6f);任务清单:
- 分析参考实现的验证方式:
- 检查是否有 CPU 参考实现
- 检查使用的验证函数(可能是自定义的
verify_{operator}) - 检查是否有其他验证方式(如文件保存、自定义比较等)
- 实现 CPU 参考函数:
- 在
.mlu文件中添加cpu_{operator}函数 - 使用简单的 C 代码实现,便于理解
- 确保与 MLU Kernel 的逻辑一致
- 在
- 统一验证函数:
- 移除自定义的验证函数(如
verify_relu、verify_softmax等) - 统一使用
verify_rm_rm函数(已在utils.h中提供) - 移除文件保存等额外操作
- 移除自定义的验证函数(如
- 更新 main 函数:
- 确保验证流程与 vecadd 一致
- 保留性能测量(Notifier)
- 保留错误检查(CNRT_CHECK)
常见需要替换的验证方式:
- ❌
verify_{operator}(...)→ ✅verify_rm_rm(...) - ❌
save_output_to_file(...)→ ✅ 移除(验证已足够) - ❌ 自定义比较逻辑 → ✅ 使用
verify_rm_rm - ❌ 缺少 CPU 参考 → ✅ 添加
cpu_{operator}函数
验证模板示例:
// =============================================================================
// CPU 参考实现:用于验证 MLU 计算结果
// =============================================================================
static inline void cpu_{operator}(const float* input, float* output, int n) {
// 使用简单的 C 代码实现算子逻辑
for (int i = 0; i < n; ++i) {
// 实现算子的核心逻辑
output[i] = /* 算子计算逻辑 */;
}
}
// 在 main 函数中的验证部分:
// CPU 参考计算和结果验证
cpu_{operator}(host_input, host_ref, total_elements);
(void)verify_rm_rm(host_output, host_ref, total_elements, "{operator_name}", 1e-6f, 1e-6f);常见算子验证示例:
- VecAdd:
cpu_vector_add(a, b, c, n)→verify_rm_rm(c, ref, n, "vector_add", ...) - ReLU:
cpu_relu(src, dst, n)→verify_rm_rm(dst, ref, n, "relu", ...) - Softmax:
cpu_softmax(x, out, batch, dim)→verify_rm_rm(out, ref, total, "softmax", ...)
输出:
- 更新后的
.mlu文件,使用统一的验证方式
目标:提取公共工具函数,保持项目独立性
任务清单:
- 检查是否有未实现的工具函数(如
generate_random_matrix_row_major、verify_rm_rm等) - 在
Experiments/utils.h中实现这些函数 - 确保实现简单、独立,不依赖外部库
- 更新所有
.mlu文件中的 include 路径
注意:verify_rm_rm 函数已在 utils.h 中提供,所有算子都应使用它进行验证。
输出:
Experiments/utils.h(如果不存在则创建,存在则更新)
目标:提供一键编译和运行的脚本
任务清单:
- 创建
build_eval.sh脚本(统一命名) - 设置必要的环境变量(NEUWARE_HOME、LD_LIBRARY_PATH、PATH 等)
- 实现参数解析(接受
.mlu文件名) - 实现编译命令(使用
cncc) - 实现执行命令
- 确保脚本简洁明了,易于理解
脚本模板:
#!/bin/bash
# 环境变量设置
export NEUWARE_HOME=/usr/local/neuware
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$NEUWARE_HOME/lib64
export PATH=$PATH:$NEUWARE_HOME/bin
export MLU_VISIBLE_DEVICES=0
export TORCH_DEVICE_BACKEND_AUTOLOAD=0
set -euo pipefail
# 参数检查
if [ $# -ne 1 ]; then
echo "Usage: $0 <mlucode 文件名,*.mlu>"
exit 1
fi
MLU_SOURCE=$1
if [[ "$MLU_SOURCE" == *.mlu ]]; then
TARGET="${MLU_SOURCE%.mlu}"
else
TARGET="$MLU_SOURCE"
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# 激活环境
source /torch/venv3/pytorch/bin/activate
# 编译和运行
pushd "$SCRIPT_DIR" >/dev/null
cncc "${MLU_SOURCE}" -o "${TARGET}" --bang-mlu-arch=mtp_592 -O3 -lm
./"${TARGET}"
popd >/dev/null输出:
build_eval.sh脚本(统一命名)
目标:创建完整的初学者教程
任务清单:
- 创建
tutorial.md文件 - 编写教程概述
- 编写最小实现部分说明
- 编写完整实现(tiling/优化版本)部分说明
- 编写运行脚本部分说明(包括环境变量、使用方法、编译参数),脚本统一命名为
build_eval.sh - 编写探索任务:
- 最小实现的探索任务(上限/下限探索,参考硬件实现文档)
- 完整实现的参数调优任务(参考任务映射文档)
- 编写学习流程建议
教程结构模板:
# {Operator} 初学者教程
本目录围绕 `{operator}.mlu` 展示了两个学习阶段的程序、一个运行脚本(`build_eval.sh`)和如何快速上手的说明。
## 1. 最小实现(`{operator}_minimal.mlu`)
- 核心特性说明
- 设计思路说明
- 关键代码说明
**探索任务**:
- 上限探索(NRAM 容量限制)
- 下限探索(对齐要求)
- 存储层次理解
- 计算能力差异
## 2. {优化特性}实现(`{operator}.mlu`)
- 优化技术说明(tiling、多任务并行等)
- 关键参数说明
- 性能考虑
**探索任务 - 参数调优**:
- Tiling 大小调优
- 任务类型(ktype)调优
- 任务数量(dim)调优
- 任务映射理解
- 综合调优
## 3. 运行脚本(`build_eval.sh`)
### 3.1 环境变量配置
### 3.2 使用方法
### 3.3 编译参数说明
## 4. 建议的学习流程
1. 先用最小实现确认基本流程
2. 理解完整实现的优化技术
3. 进行探索任务
4. 进行参数调优参考文档链接:
输出:
tutorial.md文件
目标:统一代码风格,提高可读性
任务清单:
- 统一注释语言(中文)
- 为关键代码段添加注释
- 为配置参数添加说明
- 为函数添加功能说明
- 移除过时的注释(如 "FIXED:" 标记)
- 确保注释准确反映代码功能
注释规范:
- 使用中文注释
- 关键配置参数要有说明
- 复杂逻辑要有注释
- 数据流动要有说明
- 设计决策要有解释
输出:
- 更新后的
.mlu文件
目标:创建项目级别的 README,包含待办事项
任务清单:
- 创建或更新
Experiments/README.md - 列出所有算子教程
- 为每个算子添加状态标记(✅ 完成 / 🚧 进行中 / ⏳ 待开始)
- 添加待办事项列表
- 添加贡献指南(可选)
README 模板:
# BangC 算子教程集合
本目录包含一系列 BangC 算子实现的初学者教程,每个教程都包含最小实现、优化实现、运行脚本和详细说明。
## 教程列表
| 算子 | 状态 | 说明 | 教程链接 |
|------|------|------|----------|
| VecAdd | ✅ | 向量加法 | [tutorial.md](01_vecadd/tutorial.md) |
| ReLU | ⏳ | ReLU 激活函数 | - |
| Softmax | ⏳ | Softmax 归一化 | - |
| ... | ... | ... | ... |
## 待办事项
### 高优先级
- [ ] 完成 ReLU 教程
- [ ] 完成 Softmax 教程
- [ ] 添加更多探索任务示例
### 中优先级
- [ ] 统一代码风格
- [ ] 添加性能基准测试
- [ ] 添加常见问题解答
### 低优先级
- [ ] 添加视频教程链接
- [ ] 添加更多算子实现
- [ ] 国际化支持
## 贡献指南
欢迎贡献新的算子教程!请参考 [TUTORIAL_WORKFLOW.md](TUTORIAL_WORKFLOW.md) 了解编写流程。输出:
Experiments/README.md文件
在完成所有步骤后,使用以下检查清单验证工作质量:
- 最小实现可以编译和运行
- 完整实现可以编译和运行
- 验证方式统一:使用
verify_rm_rm函数 - CPU 参考实现完整:所有算子都有对应的 CPU 参考函数
- 代码注释清晰、准确
- 代码风格统一
- 教程文档完整、清晰
- 探索任务有明确的指导
- 参考文档链接正确
- 学习流程合理
- 运行脚本可以正常使用
- 工具函数实现正确
- 环境变量配置正确
- 项目结构清晰
- 文件命名规范
- README 更新完整
- 渐进式学习:从简单到复杂,最小实现 → 优化实现
- 实践导向:提供探索任务,让学习者自己发现规律
- 文档完善:提供硬件背景和理论支撑
- 工具支持:提供便捷的运行脚本
- 最小实现太复杂:应该进一步简化,只保留核心逻辑
- 验证方式不统一:应该统一使用
verify_rm_rm函数,移除自定义验证函数 - 缺少 CPU 参考实现:应该为每个算子添加简单的 CPU 参考函数
- 探索任务不明确:应该提供具体的操作步骤和思考问题
- 文档缺少背景:应该链接官方文档,提供硬件背景
- 代码注释不足:应该为关键代码添加详细注释
- 先理解参考实现,再创建最小实现
- 最小实现要能独立运行和验证
- 教程要包含探索任务,引导主动学习
- 代码注释要统一风格,提高可读性
- 定期更新 README,跟踪项目进度
完成教程编写后,可以考虑:
- 添加性能基准测试
- 添加常见问题解答(FAQ)
- 添加视频教程
- 收集学习者反馈,持续改进