告别CUDA无需Triton!Mirage零门槛生成PyTorch算子,人均GPU编程大师

4a2dbfc76273c13e57a076aff9902ca9.gif

©作者 | Catalyst Group

单位 | 卡内基梅隆大学

来源 | 机器之心

近日,来自 CMU 的 Catalyst Group 团队发布了一款 PyTorch 算子编译器 Mirage,用户无需编写任何 CUDA 和 Triton 代码就可以自动生成 GPU 内核,并取得更佳的性能。

随着 GPU 加速器的不断发展以及以大语言模型为代表的生成式 AI 应用的不断推广,通过开发高性能 GPU 内核来优化 PyTorch 程序的计算效率变得越来越重要。

目前,这项任务主要由专门的 GPU 专家来完成。在 NVIDIA CUDA 或 AMD ROCm 中编写高性能 GPU 内核需要高水平的 GPU 专业知识和大量的工程开发经验。目前的机器学习编译器(如 TVM、Triton 和 Mojo)提供了一些高级编程接口,以简化 GPU 编程,使用户可以使用 Python 而非 CUDA 或 ROCm 来实现 GPU 内核。

然而,这些语言仍然依赖用户自行设计 GPU 优化技术以达到更高的性能。例如,在 Triton 中实现一个 FlashAttention 内核大约需要 700 行 Python 代码(在 CUDA 中需要大约 7,000 行 C++ 代码)。在这些程序中,用户需要手动划分线程块之间的工作负载,组织每个线程块内的计算,并管理它们之间的同步与通信。

5cfbc7a6716a12d92cbc9a60be767268.png

▲ 图1. 综述结构概览图用 Triton 实现的 FlashAttention 算子

能否在不使用 CUDA/Triton 编程的情况下就获得高效的 GPU 内核呢?基于这一动机,来自卡内基梅隆大学的 Catalyst Group 团队发布了 Mirage 项目,基于 SuperOptimization 技术(https://arxiv.org/abs/2405.05751),为 PyTorch 自动生成高效 GPU 内核算子。

例如,对于一个 FlashAttention 算子,用户只需编写几行 Python 代码来描述注意力(Attention)的计算过程而无需了解 GPU 编程细节,如下所示:

# Use Mirage to generate GPU kernels for attention
import mirage as mi
graph = mi.new_kernel_graph ()
Q = graph.new_input (dims=(64, 1, 128), dtype=mi.float16)
K = graph.new_input (dims=(64, 128, 4096), dtype=mi.float16)
V = graph.new_input (dims=(64, 4096, 128), dtype=mi.float16)
A = graph.matmul (Q, K)
S = graph.softmax (A)
O = graph.matmul (S, V)
optimized_graph = graph.superoptimize ()

Mirage 会自动搜索可能的 Attention GPU 内核实现,搜索空间不仅包括现有的手动设计的注意力内核(如 FlashAttention 和 FlashDecoding),还包括在某些场景中比目前的手写版本快多达 3.5 倍的其他实现。Mirage 生成的 GPU 内核可以直接在 PyTorch 张量上操作,并可以在 PyTorch 程序中直接调用。

import torch
input_tensors = [
    torch.randn (64, 1, 128, dtype=torch.float16, device='cuda:0'),
    torch.randn (64, 128, 4096, dtype=torch.float16, device='cuda:0'),
    torch.randn (64, 4096, 128, dtype=torch.float16, device='cuda:0')
]
# Launch the Mirage-generated kernel to perform attention
output = optimized_graph (input_tensors)

666a180d1f1d55b6d88a82e7590b3b43.png

Why Mirage?

与使用 CUDA/Triton 编程相比,Mirage 提供了一种新的编程范式,包含三个主要优势:

更高的生产力:随着 GPU 架构日新月异,现代 GPU 编程需要持续学习大量的专业知识。Mirage 的目标是提高机器学习系统工程师的生产力 —— 他们只需在 PyTorch 层面描述所需的计算,Mirage 便会自动生成适用于各种 GPU 架构的高性能实现。因此,程序员不再需要手动编写 CUDA/Triton 或特定架构的低级代码。

更好的性能:目前手动设计的 GPU 内核由于无法充分探索和利用各种 GPU 优化技术,往往只能达到次优性能。Mirage 可以自动搜索与输入的 PyTorch 程序功能等价的潜在 GPU 实现,探索并最终发现性能最优的内核。在多个 LLM/GenAI 基准测试中的测试结果显示,Mirage 生成的内核通常比 SOTA 的专家人工编写或编译器生成的替代方案快 1.2 至 2.5 倍。

更强的正确性:手动实现的 CUDA/Triton GPU 内核容易出错,而且 GPU 内核中的错误难以调试和定位,而 Mirage 则利用形式化验证(Formal Verification)技术自动验证生成的 GPU 内核的正确性。

 e3ad90db4b56d15f5c042db0c2c3e79c.jpeg

▲ LLaMA-3-8B 和 Chameleon-7B 端到端推理延迟对比(NVIDIA A100,batch size=1,context length=4K),相比于 CUDA/Triton 的实现,Mirage 可以实现 15-20% 的加速

c237cb7b373fbe50c303e07d710f518b.png

GPU架构与Mirage中的8762e269bf16ae42371e56983aec8051.png

GPU 计算的内核函数以单程序多数据(SPMD)方式在多个流处理器(SM)上同时运行。GPU 内核(Kernel)借助由线程块(Thread Block)组成的网格结构来组织其计算,每个线程块在单个 SM 上运行。

每个块进一步包含多个线程(Thread),以对单独的数据元素进行计算。GPU 还拥有复杂的内存层次结构,以支持这种复杂的处理结构。

每个线程都有自己的寄存器文件(Register File),以便快速访问数据。线程块内的所有线程可以访问一个公共的共享内存(Shared Memory),这有助于它们之间高效的数据交换和集体操作。

最后,内核内的所有线程可以访问分配给整个 GPU 的大型设备内存(Device Memory)。

 a570a6e5fd2b75b0a5ad43eb74d93810.png

▲ GPU 计算架构和编程抽象示意图

Mirage 使用 97dc91b9ad91120632089dba874874d0.png 来描述 GPU 内核,ab7aef389ccc916d55faa08db6dc20e3.png 包含多个层次,代表内核、线程块和线程级别的计算。大体上,Kernel Graph、Thread Block Graph 和 Thread Graph 分别代表整个 GPU、一个流处理器(SM)和一个 CUDA/tensor 核心上的计算。

对 c05e13598e9e2e90484f84f4a5cba6d4.png 细节感兴趣的读者可以参考:

https://mirage-project.readthedocs.io/en/latest/mugraph.html

98561cbfb8459afe3724778b547352e7.png

▲ Mirage工作流示意图

上图展示了 Mirage 的工作流程:对于输入的 PyTorch 程序,Mirage 的 41a11677bc881911938761a70b211595.png 生成器自动搜索与输入程序功能等价的其他 91b344e2435e544e09bc5d7a543429fb.png,搜索空间涵盖了内核、线程块和线程级别的各种 GPU 优化。

所有生成的 d3501459c3285bee6664bd208bbe1974.png 都被发送到等价性验证器,该验证器自动检查每个 d49b20314095aaad82ae940c0ec5c454.png 是否与所需程序等价。最后,36d03dff55237729115f0b418544eb55.png 转译器将所有经过验证的 22a4efc5f238fdb9784a7948bb75fd29.png 转译为 CUDA 内核。最后,Mirage 会从中返回性能最佳的 CUDA 内核。

a8a9ca2411e01c270ed2b741c3bcc460.png

论文链接:

https://arxiv.org/pdf/2405.05751

项目成员:

Mengdi Wu (CMU), Xinhao Cheng (CMU), Shengyu Liu (PKU), Chuan Shi (PKU), Jianan Ji (CMU), Oded Padon (VMWare), Xupeng Miao (Purdue), Zhihao Jia (CMU)

项目地址:

https://github.com/mirage-project/mirage

b764fd8e0f761265fd5758396990c720.png

为什么Mirage生成的内核更高效?

在多个 LLM/GenAI 基准测试中的测试结果显示,Mirage 生成的内核通常比现有的手写或编译器生成的内核快 1.2 至 2.5 倍。接下来,本文以 LLM 中的 Transformer 架构为例,展示现有系统中缺失的几项 GPU 程序优化技术:

0ce3960f29a47468059d79cf7b5abd3e.png

▲ Transformer 架构示意图

Case 1: Normalization + Linear

归一化(Normalization)操作,如 LayerNorm、RMSNorm、GroupNorm 和 BatchNorm,广泛应用于当今的机器学习模型。当前的机器学习编译器通常在独立的内核中启动归一化层,因为归一化涉及到归约和广播,难以与其他计算融合。然而,Mirage 发现,大多数归一化层可以通过进行适当的代数变换,与后续的线性层(如 MatMul)融合。

93817dbd68712f11f43181d868da3664.png

▲ Normalization + Linear 现有内核 v.s. Mirage 发现的内核

Mirage 发现的自定义内核利用了 RMSNorm 中的除法和 MatMul 中的乘法的可交换性,将除法移到 MatMul 之后。这一变换保持了功能等价性,同时避免了中间张量 Y 的实例化。该内核的性能比单独运行这两个操作快 1.5 到 1.7 倍。

edd9f852ea3dc6084dbbe8d4067e12f8.png

▲ Normalization + Linear 内核性能对比

Case 2: LoRA + Linear

LoRA 广泛用于预训练模型的微调场景,以适配到特定领域和任务。这些 LoRA 适配器通常会被插入到模型的线性层中,引入额外的矩阵乘法。现有系统通常为原始矩阵乘法和 LoRA 中的两个矩阵乘法启动独立的内核,从而导致较高的内核启动开销。

e6aec26795d634f3179808b45382c47a.png

▲ LoRA+Linear现有内核 v.s. Mirage发现的内核

如上图所示,Mirage 发现了一个将三个矩阵乘法和随后的加法融合为单个内核的内核。这是通过将计算重组为两个线程块级别的矩阵乘法实现的,利用了以下代数变换:W×X+B×A×X=(W|B)×(X|(A×X)),其中的两个拼接操作不涉及任何计算,而是通过在 GPU 共享内存中更新张量偏移量来完成。Mirage 发现的内核比现有系统中使用的内核快 1.6 倍。

1334970189e4a5b3093a910bc30bc4e3.jpeg

▲ LoRA+Linear 内核性能对比

Case 3: Gated MLP

Gated MLP 层目前在许多 LLM 中使用(如 LLAMA-2、LLAMA-3 及其变体),它的输入张量 X 与两个权重矩阵相乘,输出结果被组合以产生最终结果。Mirage 发现了一个内核,该内核执行两个矩阵乘法、SiLU 激活以及随后的逐元素乘法,从而减少了内核启动开销和对设备内存的访问。

c0b8c79f9da37461a43366e1d449854c.png

▲ Gated MLP现有内核 v.s. Mirage发现的内核

9e2f2b86137b7b941bde33cc734763c5.jpeg

▲ Gated MLP 内核性能对比

Case 4: Attention Variants

如今的大多数 LLM 基于注意力及其变体,虽然现有系统通常提供高度优化的注意力实现,如 FlashAttention、FlashInfer 和 FlexAttention,但支持注意力变体通常需要新的自定义内核。下面用两个例子来展示 Mirage 如何为非常规注意力计算发现自定义 GPU 内核。

Case 4.1: Attention with Query-Key Normalization

许多最近的 LLM 架构(包括 Chameleon、ViT-22B 等)在 LLaMA 架构中引入了 QK-Norm 来缓解训练过程中的数值发散问题。QK-Norm 在注意力之前对 Query 和 Key 向量应用 LayerNorm 层。现有注意力实现中并不支持这些额外的归一化层,并且它们还需要作为独立内核启动。

007cf6d4a2315ac0f3b7287ab5a14813.png

▲ QK-Norm注意力现有内核 v.s. Mirage发现的内核

对于在注意力之前和 / 或之后引入计算的注意力变体,这些计算可以与注意力融合以提高 GPU 性能,而这需要自定义内核。对于带有 QK-Norm 的注意力,Mirage 发现了上述内核来融合计算,从而避免在 GPU 设备内存中实例化中间结果。这个自定义内核还对注意力进行了现有的 GPU 优化,实现了 1.7 至 2.5 倍的性能提升。

694522b763941f2c6565cd3c305d639e.jpeg

▲ QK-Norm 注意力内核性能对比

Case 4.2: Multi-Head Latent Attention

634f7fec0699e3b280009372c24c8be1.png

▲ MLA的现有内核 v.s. Mirage发现的内核

另一个常用的注意力变体是 MLA(Multi-Head Latent Attention),它将注意力的 KV Cache 压缩为一个向量,以减少存储 KV Cache 的内存开销。这一变化还在注意力之前引入了两个线性层,如下图所示。与 QK-Norm 类似,现有注意力实现中并不支持这些额外的归一化层,同样需要作为独立内核启动,而 Mirage 可以将线性层和注意力融合为一个单独的自定义内核。

5b3309a98de4b6234a79abb226799b48.png

长期愿景

Mirage 项目的长期目标是希望能够让未来的 AI 开发者无需学习 CUDA 或者 Triton 等复杂的 GPU 编程语言,只需指定所需的数学操作,就能在 GPU 上轻松实现 AI 模型。通过利用 Mirage 的 SuperOptimization 技术,各种计算任务可以自动转换为高度优化的 GPU 实现。随着 LLM 和其他生成式 AI 应用的飞速发展,在各种实际部署场景都需要高效的 GPU 支持,降低 GPU 编程门槛并提高程序效率也愈发重要。

更多阅读

585046158efb2818fcbd430e05b6b070.png

69b49e7249730efbfab7bd83410ead17.png

a0e1c715073013f0e37bc29795125921.png

62b4e93f5c6b7981f753d447f29dba3e.gif

#投 稿 通 道#

 让你的文字被更多人看到 

如何才能让更多的优质内容以更短路径到达读者群体,缩短读者寻找优质内容的成本呢?答案就是:你不认识的人。

总有一些你不认识的人,知道你想知道的东西。PaperWeekly 或许可以成为一座桥梁,促使不同背景、不同方向的学者和学术灵感相互碰撞,迸发出更多的可能性。 

PaperWeekly 鼓励高校实验室或个人,在我们的平台上分享各类优质内容,可以是最新论文解读,也可以是学术热点剖析科研心得竞赛经验讲解等。我们的目的只有一个,让知识真正流动起来。

📝 稿件基本要求:

• 文章确系个人原创作品,未曾在公开渠道发表,如为其他平台已发表或待发表的文章,请明确标注 

• 稿件建议以 markdown 格式撰写,文中配图以附件形式发送,要求图片清晰,无版权问题

• PaperWeekly 尊重原作者署名权,并将为每篇被采纳的原创首发稿件,提供业内具有竞争力稿酬,具体依据文章阅读量和文章质量阶梯制结算

📬 投稿通道:

• 投稿邮箱:hr@paperweekly.site 

• 来稿请备注即时联系方式(微信),以便我们在稿件选用的第一时间联系作者

• 您也可以直接添加小编微信(pwbot02)快速投稿,备注:姓名-投稿

19593f32cdf0b4d6a1e9a15cb8f5dda1.png

△长按添加PaperWeekly小编

🔍

现在,在「知乎」也能找到我们了

进入知乎首页搜索「PaperWeekly」

点击「关注」订阅我们的专栏吧

·

·

·

6bd958ee9d5a963bcb2e716f7404180b.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值