CS149 Programming Assignment 4 - Chat149 - A Flash Attention Transformer DNN
Stanford CS149 (Fall 2023) Parallel Computing
Programming Assignment 4: Chat149 - A Flash Attention Transformer DNN
环境配置
妈的,外校学生又没有服务器,只能自己配环境,又不给
requirements.txt
,装个 torch 因为版本问题整了半天,翻 issue
才知道要装 2.1.2 版本,至于其它 module 就一直编译靠报错找依赖(
Warm-Up: Accessing Tensors (3 Points)
简单来说就是实现 4D-Tensor(类似四维数组)和一维数组之间的转换,秒
(然而其实这个实现性能上还稍微有点问题,在 Part2 才查出来)
1 | inline float fourDimRead(std::vector<float> &tensor, int &x, int &y, int &z, |
1 | ~/codes/CS149/cs149gpt (master*) » python3 gpt149.py 4Daccess mizukicry@S-Terminal |
Part 1: A Simple (But Not So Efficient) Implementation of Attention (10 Points)
实现最基础的 Attention Module
总的就是三个步骤:矩阵乘法、Softmax、矩阵乘法。照着写就行了
1 | // -------- YOUR CODE HERE -------- // |
1 | ~/codes/CS149/cs149gpt (master*) » python3 gpt149.py part1 mizukicry@S-Terminal |
Part 2: Blocked Matrix Multiply and Unfused Softmax (20 Points)
使用课件中提到的分块矩阵乘法提高 cache locality
一开始实现出来莫名比 reference 慢不少,试了试发现是前面 4D-Tensor 访问的函数有点问题,一开始我自作聪明给优化了(减少乘法计算),结果反而不利于编译器优化,改成直接硬算快了不少
1 | // Step #2: Implement Read/Write Accessors for a 4D Tensor |
然后是关于块大小的问题,理论上让块的每行正好填满一个 cache line
最好,在我的机子上通过
cat /sys/devices/system/cpu/cpu1/cache/index0/coherency_line_size
查询得到 cache line size 是 64 bytes,所以我就直接用 16
作为块大小了(后来才看见 PA 里有说)
测试的时候我一开始在图书馆跑,笔记本开的静音模式,比 reference 差 20ms 以上,换平衡模式就好了,测试程序性能也是个深奥的内容(
后来把循环部分给优化了一下,比如
for (int i = b_i; i < b_i + L && i < N; i++)
这种,先预处理出
min(b_i + L, N)
,然后直接用这个值代替循环条件,效果还挺明显
1 | // -------- YOUR CODE HERE -------- // |
1 | ~/codes/CS149/cs149gpt (master) » python3 gpt149.py part2 mizukicry@S-Terminal |
Part 3: Fused Attention (25 Points)
使用 OpenMP 并行化
通过对矩阵的每一行分别进行操作,可以使用多线程加速,只需要加一行
#pragma
就行,非常方便
1 | // -------- YOUR CODE HERE -------- // |
1 | ~/codes/CS149/cs149gpt (master*) » python3 gpt149.py part3 mizukicry@S-Terminal |
Part 4 : Putting it all Together - Flash Attention (35 Points)
合并 Part 2 的矩阵分块与 Part 3 的 OpenMP 并行化
PA 中已经给出了算法伪代码,照着翻译就是了
话说他这伪代码也挺迷惑,0-indexed 和 1-indexed 都没分清。Tensor
的访问函数参数竟然不全是
const&
,给改了一下,不然没法传递临时变量
另外一提,伪代码中忽略了外面两层(Batch Size, Number of Heads)的循环,写的时候要记得每一次的初始化,最简单的是直接移到循环里面来定义
Part 4 中不需要考虑性能,主要看是否正确,所以我就没做什么优化了,不过只要照着写出来就很快了
1 | // Format All Tensors into Vectors |
1 | ~/codes/CS149/cs149gpt (master*) » python3 gpt149.py part4 mizukicry@S-Terminal |
- Extra Credit: Optimize Further (12 Total Points - 3 Points Per Part)
也就是用 ISPC 继续优化,这个就不写了,直接跳过