Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
078f377
Implement Tensor::load; clarify tensor storage comments
Jan 16, 2026
27a0feb
作业1已完成,实现了基本的张量操作功能,包括load张量、基本算术运算、切片和形状变换。
Jan 16, 2026
c67a0e0
任务二已完成
Feb 3, 2026
3032433
任务2已完成,小修改
Feb 3, 2026
b334978
任务三——实现了大语言模型推理
Feb 3, 2026
d9100df
任务三
Feb 3, 2026
a79796e
self_attention中加上头文件
Feb 4, 2026
734f880
self_attention缺少头文件,已经补上
Feb 4, 2026
3edd5b8
已修复符号比较警告
Feb 4, 2026
942dd06
已补上 <cmath>
Feb 4, 2026
c9a4646
已补上 <cmath>
Feb 4, 2026
364d19b
将rope中的cosf和sinf改为cos和sin以支持double类型
Feb 4, 2026
0e9f067
Transformer 架构核心已完成
Mar 2, 2026
4281d5e
transformer核心算子笔记
Mar 4, 2026
f33f3ab
把所有 $$...$$ 块公式改为可直接显示的文本公式
Mar 4, 2026
60901c1
核心算子统一笔记具体化
Mar 4, 2026
1719320
任务三学习手册
Mar 4, 2026
030bdad
运用SIMD技术,提升了linear的运行舒服,但只对f32有效
Mar 7, 2026
562ef9d
增加了OpenBLAS和总的性能报告提升
Mar 7, 2026
00bae6c
实现了cuda算子
Mar 8, 2026
2cf8dd0
项目三,但远程连接还有些问题
Mar 8, 2026
9e0fc50
项目三补充
Mar 8, 2026
60a4ff7
Merge branch 'InfiniTensor:main' into main
yuanaohui Mar 16, 2026
d877fbc
项目二、项目三已完成
Mar 16, 2026
f97e97d
Merge branch 'main' of https://github.com/yuanaohui/llaisys
Mar 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions PROJECT1_CPU_OPT_REPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# 项目#1 CPU 推理优化笔记(LLAISYS)

## 1. 一页结论
- 优化对象:`Ops.linear`(CPU 上最耗时,等价 GEMM)。
- 主线策略:`朴素循环 -> SIMD+OpenMP -> OpenBLAS`。
- 结果:`f32` 从 `6173.788 ms` 降到 `253.513 ms`,累计约 `24.35x`。
- 扩展:`f16/bf16` 已接入专用快速路径,不再在最内层循环频繁类型转换。

## 2. 测试口径(统一)
- 平台:Windows x64, MSVC 19.50。
- 代码:`llaisys`。
- Python:`./.venv`。
- 线程:`OMP_NUM_THREADS=8`。
- 形状:`x=(512,4096)`, `w=(4096,4096)`, `bias=(4096,)`, `out=(512,4096)`。
- benchmark:`warmup=1`, `repeat=3`。
- 说明:Baseline 为历史记录;其余为同机复测。

## 3. 里程碑与改动
### A. Baseline(优化前)
- 三重循环逐元素乘加。
- 无 SIMD,无 BLAS。

### B. SIMD + OpenMP
- 文件:`src/ops/linear/op.cpp`。
- `f32` 点积内核:`_mm256_loadu_ps` + `_mm256_fmadd_ps`。
- 外层 `m` 维并行:`#pragma omp parallel for`。
- 不支持 AVX2 时自动回退标量路径。

### C. OpenBLAS
- 文件:`src/ops/linear/op.cpp`。
- `ENABLE_OPENBLAS` 下,`f32` 调用 `cblas_sgemm` 完成主计算,再加 bias。
- 文件:`xmake.lua`、`xmake/cpu.lua`。
- 新增构建开关:`--openblas=y|n`。

### D. `f16/bf16` 专用高性能路径(已完成)
- 文件:`src/ops/linear/op.cpp`。
- 新增 `linear_impl_lowp_fast<T>`。
- `weight/bias` 先批量转 `float`,避免最内层重复 `cast`。
- OpenBLAS 开启时:`in` 也批量转 `float` 后走 `sgemm`。
- OpenBLAS 关闭时:每线程复用 `in_row_f`,继续复用 `dot_f32`。

## 4. 性能对比
### 4.1 `f32` 分阶段结果
| 阶段 | LLAISYS (ms) | 说明 |
|---|---:|---|
| A. Baseline(历史) | 6173.788 | 优化前 |
| B. SIMD + OpenMP(复测) | 266.430 | `--openblas=n --openmp=y --cpu-avx2=y` |
| C. OpenBLAS(复测) | 253.513 | `--openblas=y --openmp=y --cpu-avx2=y` |

对应 Torch(同测参考):
- B 阶段 Torch:`56.534 ms`
- C 阶段 Torch:`47.332 ms`

### 4.2 `f16/bf16` 专用路径结果
| dtype | Torch (ms) | LLAISYS (ms) |
|---|---:|---:|
| `f16` | 283.002 | 271.528 |
| `bf16` | 297.637 | 268.177 |

## 5. 加速比(`f32`)
- A -> B:`6173.788 / 266.430 = 23.17x`
- A -> C:`6173.788 / 253.513 = 24.35x`
- B -> C:`266.430 / 253.513 = 1.05x`(约 `5.1%`)

结论:
- 最大收益来自结构性优化(SIMD + 并行)。
- OpenBLAS 在其上继续带来稳定增益。

## 6. 复现实验命令
### 6.1 `f32`:SIMD + OpenMP(关闭 OpenBLAS)
```powershell
xmake f --openblas=n --openmp=y --cpu-avx2=y -y
xmake -y
xmake install -y
$env:OMP_NUM_THREADS=8
D:/86188/大模型学习/llaisys/.venv/Scripts/python.exe -c "import sys; sys.path.insert(0,'test'); import llaisys, torch; from test_utils import random_tensor, benchmark; x,x_=random_tensor((512,4096),'f32','cpu',scale=0.1); w,w_=random_tensor((4096,4096),'f32','cpu',scale=0.01); b,b_=random_tensor((4096,),'f32','cpu'); out,out_=random_tensor((512,4096),'f32','cpu'); f1=lambda: torch.nn.functional.linear(x,w,b,out=out); f2=lambda: llaisys.Ops.linear(out_,x_,w_,b_); benchmark(f1,f2,'cpu',warmup=1,repeat=3)"
```

### 6.2 `f32`:OpenBLAS
```powershell
xmake f --openblas=y --openmp=y --cpu-avx2=y -y
xmake -y
xmake install -y
$env:OMP_NUM_THREADS=8
D:/86188/大模型学习/llaisys/.venv/Scripts/python.exe -c "import sys; sys.path.insert(0,'test'); import llaisys, torch; from test_utils import random_tensor, benchmark; x,x_=random_tensor((512,4096),'f32','cpu',scale=0.1); w,w_=random_tensor((4096,4096),'f32','cpu',scale=0.01); b,b_=random_tensor((4096,),'f32','cpu'); out,out_=random_tensor((512,4096),'f32','cpu'); f1=lambda: torch.nn.functional.linear(x,w,b,out=out); f2=lambda: llaisys.Ops.linear(out_,x_,w_,b_); benchmark(f1,f2,'cpu',warmup=1,repeat=3)"
```
165 changes: 165 additions & 0 deletions PROJECT2_CUDA_INTEGRATION_REPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# 项目#2 学习型报告:从 0 到 1 集成 CUDA(小白友好 + 弱 C++ 版)

## 1. 先说结论:你在项目二里完成了什么
你已经把 LLAISYS 从“只会 CPU”推进到“能在 NVIDIA GPU 上完整跑推理链路”。

你完成的是一条完整工程链,而不是单点代码:
- Runtime 层:支持 NVIDIA 设备初始化、显存申请、数据拷贝、流同步。
- 算子层:关键算子都能在 `--device nvidia` 下跑通(先用 staging 方案保证正确性)。
- 模型层:`Qwen2` 推理流程能在 nvidia 设备上正常执行。
- 验证层:`build -> install -> test_runtime -> test/ops -> test_infer` 全链路通过。

一句话总结:项目二的价值是“把 GPU 后端这条路打通,并且可复现”。

## 2. 给完全初学者的背景补课

### 2.1 什么是 Runtime API(为什么必须有它)
你可以把 Runtime API 理解为“统一设备操作的遥控器”:
- 不同设备(CPU、NVIDIA)内部实现不同。
- 但上层算子不想关心细节,只想调用统一接口。

所以项目里会有 `runtime_api.hpp` 这种抽象层,然后 CPU/NVIDIA 各自实现。

### 2.2 什么是“端到端可用”
不是“我写了几个 `.cpp` 文件就算完成”,而是下面全部成立:
1. 能编译。
2. 能安装成 Python 可调用动态库。
3. Python 测试能调到 GPU 后端。
4. 算子输出正确。
5. 模型推理也正确。

## 3. 你的核心实现(按模块拆解)

### 3.1 Runtime 层:你做了 NVIDIA API 对接
核心文件:`src/device/nvidia/nvidia_runtime_api.cpp`

你把框架抽象接口映射到了 CUDA 官方 API:
- 设备:`cudaGetDeviceCount`、`cudaSetDevice`、`cudaDeviceSynchronize`
- Stream:`cudaStreamCreate`、`cudaStreamDestroy`、`cudaStreamSynchronize`
- 内存:`cudaMalloc/cudaFree`、`cudaMallocHost/cudaFreeHost`
- 拷贝:`cudaMemcpy`、`cudaMemcpyAsync`

对新手最关键的理解:
- 你的框架不是直接 everywhere 写 CUDA,而是先走统一抽象,再由 NVIDIA 后端实现具体细节。
- 这种设计后面扩展 AMD/其它设备会更容易。

### 3.2 共享 GPU 稳定性:你做了“可用设备筛选”
场景是共享 A100,某些卡可能不可用,直接 `cudaSetDevice` 会失败。

你做的处理是:
- 启动时逐卡探测。
- 仅把“可成功激活”的卡加入可用列表。
- 上层看到的是逻辑设备编号,底层再映射到物理卡。

价值:避免“机器有 8 张卡但你正好选到坏卡”导致测试假失败。

### 3.3 构建系统:你把 NVIDIA 后端接入 xmake
关键文件:`xmake/nvidia.lua`、`xmake.lua`

你完成了:
- 新增 nvidia 目标库并纳入总构建。
- `--nv-gpu=y` 时启用 `ENABLE_NVIDIA_API`。
- Linux 动态库链接增加 `gomp`(OpenMP 运行时)。

为什么这一步重要:
- 很多“代码没错但跑不起来”的问题都在链接阶段。
- 你把“编译通过”和“运行时符号可解析”都兜住了。

### 3.4 算子层:你采用 staging 方案优先保正确
关键思路:
- 数据 D2H(Device 到 Host)
- 复用 CPU 算法
- 结果 H2D 写回

涉及关键算子:
- `add`、`linear`、`argmax`、`embedding`、`rms_norm`、`rope`、`self_attention`、`swiglu`

你这个选择非常工程化:
- 第一阶段先拿到“可跑、正确”。
- 第二阶段再逐个替换成真正 CUDA Kernel 做性能优化。

### 3.5 模型层:你把 `qwen2` 的设备读写改成安全模式
关键文件:`src/llaisys/qwen2.cc`

你新增/使用了安全 helper:
- `zero_tensor`
- `tensor_write_i64`
- `tensor_read_i64`
- `tensor_copy_bytes`

这一步解决了新手常见大坑:
- 在 GPU 上不能像 CPU 一样随便 `memcpy`/解引用设备指针。
- 必须通过 runtime API 做合法的 H2D/D2H/D2D 操作。

## 4. 你踩过并修复的关键问题(学习价值很高)

### 4.1 本机无 CUDA SDK
- 现象:`Cuda SDK not found!`
- 处理:转远端 A100 环境。

### 4.2 远端缺 xmake
- 现象:`xmake: command not found`
- 处理:安装 `~/.local/bin/xmake`,后续固定绝对路径。

### 4.3 动态库 CUDA 注册符号异常
- 现象:`undefined symbol: __cudaRegisterLinkedBinary...`
- 处理:统一为 `cpp + cudart` 路径,移除问题 `.cu` 路线。

### 4.4 OpenMP 链接缺失
- 现象:`omp_get_thread_num` undefined
- 处理:`xmake.lua` 添加 `add_syslinks("gomp")`。

### 4.5 共享卡不可用
- 处理:设备筛选+逻辑映射+固定 `CUDA_VISIBLE_DEVICES`。

## 5. 如何复现你的项目二成果(一步一步)

### 5.1 环境准备
```bash
cd /home/yuanstar/llaisys
export PYTHONPATH=/home/yuanstar/llaisys/python
export CUDA_VISIBLE_DEVICES=2
```

### 5.2 构建安装
```bash
/home/yuanstar/.local/bin/xmake f --nv-gpu=y -cv
/home/yuanstar/.local/bin/xmake -y
/home/yuanstar/.local/bin/xmake install -y
```

### 5.3 运行验证
```bash
python3 test/test_runtime.py --device nvidia

python3 test/ops/add.py --device nvidia
python3 test/ops/linear.py --device nvidia
python3 test/ops/argmax.py --device nvidia
python3 test/ops/embedding.py --device nvidia
python3 test/ops/rms_norm.py --device nvidia
python3 test/ops/rope.py --device nvidia
python3 test/ops/self_attention.py --device nvidia
python3 test/ops/swiglu.py --device nvidia

python3 test/test_infer.py --model /home/yuanstar/models/DeepSeek-R1-Distill-Qwen-1___5B --device nvidia --test --max_steps 8
```

### 5.4 成功标准(你答辩时可以直接说)
- Runtime 测试通过。
- 核心算子测试通过。
- `test_infer` 在 nvidia 下通过,且与参考输出一致。

## 6. 你项目二的能力成长(面向答辩表达)
你不仅“会调 API”,而且已经体现了以下工程能力:
- 抽象层思维:Runtime 统一接口。
- 系统排错能力:从编译、链接、运行时逐层定位。
- 资源受限环境适配:共享 GPU 稳定性处理。
- 交付意识:不仅改代码,还确保复现脚本和验证闭环。

## 7. 面向下一步优化(可选,不影响已完成)
- 把 staging 算子逐步替换为原生 CUDA kernel,重点先做 `linear`、`self_attention`。
- 给关键性能路径增加 benchmark,量化 GPU 加速收益。
- 将“设备筛选 + 健康检查”做成统一工具函数,降低维护成本。

## 8. 项目二一句话复盘
项目二你已经完成到“可提交且可复现”的标准:CUDA 后端集成成功,nvidia 测试链路打通,模型推理在真实 GPU 环境下验证通过。
Loading