forked from metax-maca/op_optimization
Compare commits
64 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
4f2aa14e92 | |
|
|
2b9725da72 | |
|
|
291fa3fd6d | |
|
|
0362e5aeea | |
|
|
a62495f371 | |
|
|
034f4408d2 | |
|
|
cf3196825a | |
|
|
1a1ab4d91c | |
|
|
bd19176119 | |
|
|
bed86dafbf | |
|
|
232f2631c7 | |
|
|
18268c2639 | |
|
|
395c607128 | |
|
|
a8d08bcbc5 | |
|
|
931fd9e3de | |
|
|
6e78e3defd | |
|
|
641ade97b6 | |
|
|
e6a416096d | |
|
|
a686eb3b5b | |
|
|
8e266c819c | |
|
|
88f7103e02 | |
|
|
33d8c6ea3c | |
|
|
8b2d154405 | |
|
|
e6b9449632 | |
|
|
cd60d02057 | |
|
|
3342f411cd | |
|
|
43d0afee79 | |
|
|
781d2d0a18 | |
|
|
ffae51da85 | |
|
|
cbbcd27eb8 | |
|
|
97ff737c87 | |
|
|
c06e7fa12b | |
|
|
c329d96b56 | |
|
|
ac2c4d9eb1 | |
|
|
db044853c5 | |
|
|
69def4e063 | |
|
|
6fe514c7b7 | |
|
|
f533b2d736 | |
|
|
46c939acc0 | |
|
|
8628b5b38c | |
|
|
72bebcf3d4 | |
|
|
73fde4ea0f | |
|
|
56971980e0 | |
|
|
374871a838 | |
|
|
bf17669650 | |
|
|
4ad16ac26c | |
|
|
fc7db438e0 | |
|
|
c0751f642c | |
|
|
911d79c1ac | |
|
|
af2909cf63 | |
|
|
b207cc5501 | |
|
|
cfa6613a6d | |
|
|
da9181fe18 | |
|
|
19737990e2 | |
|
|
007b6a5a61 | |
|
|
f2429f1685 | |
|
|
b1a44c3488 | |
|
|
4df45b671c | |
|
|
483ca9ccbc | |
|
|
794e15fbc6 | |
|
|
2e3aa374cd | |
|
|
4b4a56eda8 | |
|
|
0e87786f89 | |
|
|
16a44f3561 |
|
|
@ -0,0 +1,329 @@
|
|||
# 常见问题 FAQ
|
||||
|
||||
> 最后整理:2026-07-13
|
||||
|
||||
本文档汇总沐曦“揭榜挂帅”两项赛题的常见问题。使用 `Ctrl+F`(Windows/Linux)或 `⌘F`(macOS)搜索关键词。
|
||||
|
||||
环境版本、评测配置、时间安排可能调整。请以仓库 README、XPU-OJ 公告、比赛群通知和容器中的实际版本为准。发现内容过期或未覆盖的问题,请[提交 Issue](https://gitlink.org.cn/metax-maca/op_optimization/issues)。
|
||||
|
||||
## 快速导航
|
||||
|
||||
- [重要入口与咨询方式](#entry):XPU-OJ 开放情况、问题反馈渠道和赛事联系人。
|
||||
- [XPU-OJ、提交与排行榜](#xpuoj):账号申请、提交环境、测试样例、评测硬件和排名指标。
|
||||
- [赛题一:TileLang 与 Fused MoE](#track-one):提交限制、算子实现、Baseline、性能优化和决赛加分规则。
|
||||
- [赛题二:AI Agent 与推理算子库](#track-two):任务范围、Agent 参与证明、Baseline、测试参数和上游代码引用。
|
||||
- [评测规则与通用技术问题](#evaluation):技术资料发布、正确性与稳定性要求,以及 FAQ 内容纠错。
|
||||
- [环境、镜像与算力资源](#environment):比赛镜像、MACA 与 PyTorch 版本、开发工具、算力券和资源申请。
|
||||
- [报名、组队与资格审核](#registration):参赛资格、跨校组队、指导教师、材料盖章和审核流程。
|
||||
|
||||
<a id="entry"></a>
|
||||
|
||||
## 重要入口与咨询方式
|
||||
|
||||
<a id="q-xpuoj-open"></a>
|
||||
|
||||
### ❓ 问题 1:XPU-OJ 平台是否已经上线?
|
||||
|
||||
**回答:** XPU-OJ 已开放。账号申领流程和使用指南见[赛事 XPU-OJ 账号申领说明](赛事XPUOJ账号申领说明.md)。
|
||||
|
||||
<a id="q-contact"></a>
|
||||
|
||||
### ❓ 问题 2:两个赛题的联系人不同,遇到问题应该联系谁?
|
||||
|
||||
**回答:** 请先通过 [GitLink Issue](https://gitlink.org.cn/metax-maca/op_optimization/issues) 提问,维护者会把可复用的答案更新到本文档。比赛群用于接收赛事通知和临时信息。需要单独沟通时,请按对应比赛方案联系章老师或杨老师。
|
||||
|
||||
## XPU-OJ、提交与排行榜
|
||||
|
||||
<a id="q-moe-score-decrease"></a>
|
||||
|
||||
### ❓ 问题 1:XPUOJ测评 MoE 耗时减少了但是分数反而降低了
|
||||
|
||||
**回答:**
|
||||
|
||||
针对近期部分同学反馈的“XPU.OJ”第三方评测系统中基线(baseline)不稳定的问题,我们高度重视,并已第一时间组织排查与测试。在此,我们对因此给大家带来的困扰深表歉意,也衷心感谢各位同学提出的宝贵意见。
|
||||
目前,相关问题已修复完毕。为确保评测的公平性与准确性,我们将对现有榜单进行清空处理。历史提交记录仍可查看,但后续排名将统一以基线修复后重新提交的算子成绩为准。
|
||||
|
||||
比赛期间,我们将持续关注系统运行状态,也欢迎大家继续向我们反馈建议。
|
||||
祝大家比赛顺利,取得理想成绩!
|
||||
|
||||
<a id="q-xpuoj-environment"></a>
|
||||
|
||||
### ❓ 问题 2:XPU-OJ 与模力方舟的运行环境一致吗?
|
||||
|
||||
**回答:** 排行榜评测环境与模力方舟开发环境保持一致。版本调整时以 XPU-OJ 公告为准。
|
||||
|
||||
<a id="q-xpuoj-account"></a>
|
||||
|
||||
### ❓ 问题 3:如何申请 XPU-OJ 账号?
|
||||
|
||||
**回答:** 请按照[赛事 XPU-OJ 账号申领说明](赛事XPUOJ账号申领说明.md)提交申请。账号发放进度以赛事通知和回复邮件为准。
|
||||
|
||||
<a id="q-official-ranking"></a>
|
||||
|
||||
### ❓ 问题 4:赛题一 MoE 初赛排名以哪个入口为准?
|
||||
|
||||
**回答:** 正式排名和初筛结果以 XPU-OJ 的评测结果为准。Sample benchmark 用于本地功能验证、调试和性能对比,不作为正式榜单依据。
|
||||
|
||||
<a id="q-test-cases"></a>
|
||||
|
||||
### ❓ 问题 5:MACA C++、Triton 和 TileLang 是否分别设榜?
|
||||
|
||||
**回答:** 不按语言分别设榜。每个任务支持 MACA C++、Triton 和 TileLang,团队可以使用一种或多种语言提交。排行榜采用通过正确性和稳定性测试后的最高成绩。
|
||||
|
||||
<a id="q-ranking-metric"></a>
|
||||
|
||||
### ❓ 问题 6:排行榜使用 latency、speedup 还是综合 score?
|
||||
|
||||
**回答:** 当前 XPU-OJ 以 speedup 作为核心排名指标。赛事方调整计算方式时,以 XPU-OJ 公告为准。
|
||||
|
||||
<a id="track-one"></a>
|
||||
|
||||
## 赛题一:TileLang 与 Fused MoE
|
||||
|
||||
<a id="q-track-one-submission"></a>
|
||||
|
||||
### ❓ 问题 1:赛题一的提交要求有哪些调整?
|
||||
|
||||
**回答:** 正式提交禁止使用 `MACA Maca running` 方式,也不能使用 PyTorch 实现算子。参赛者需使用 TileLang 实现并提交。
|
||||
|
||||
<a id="q-ops-reference"></a>
|
||||
|
||||
### ❓ 问题 2:OPS 目录中的 TileLang、CUDA、CUTLASS 和 MACA 代码有什么用途?
|
||||
|
||||
**回答:** 这些代码用于解释算子的实现原理和设计思路,可作为 TileLang 实现的参考。
|
||||
|
||||
<a id="q-baseline-modification"></a>
|
||||
|
||||
### ❓ 问题 3:官方 Baseline 可以修改到什么范围?
|
||||
|
||||
**回答:** 参赛者可以重新设计和优化算子实现,但需保持与统一 Workload 测试框架的接口兼容。
|
||||
|
||||
<a id="q-gemm-optimization"></a>
|
||||
|
||||
### ❓ 问题 4:GEMM 计算中可以引入其他优化策略吗?
|
||||
|
||||
**回答:** 可以,前提是实现符合赛题规则和评测要求。
|
||||
|
||||
<a id="q-benchmark-modification"></a>
|
||||
|
||||
### ❓ 问题 5:可以优化 `fusedmoe_benchmark` 吗?
|
||||
|
||||
**回答:** 本地修改 benchmark 不会提高正式成绩,XPU-OJ 使用赛事方的评测框架。参赛者应把优化工作放在规定的算子实现和允许修改的接口上。
|
||||
|
||||
<a id="q-forward-modification"></a>
|
||||
|
||||
### ❓ 问题 6:可以修改 `fusedmoe_benchmark.py` 中的 MoE forward 吗?
|
||||
|
||||
**回答:** 正式成绩以 XPU-OJ 的独立评测为准。本地修改 forward 不能替代对提交算子的优化,也不会改变赛事方的评测代码。
|
||||
|
||||
<a id="q-async-copy"></a>
|
||||
|
||||
### ❓ 问题 7:赛题一允许使用异步拷贝吗?
|
||||
|
||||
**回答:** 不允许。当前比赛规则禁用异步拷贝。
|
||||
|
||||
<a id="q-baseline-performance"></a>
|
||||
|
||||
### ❓ 问题 8:Fused MoE 初赛成绩如何影响决赛?
|
||||
|
||||
**回答:** 当前规则只给初赛前 10 名决赛加分,第 11 名及之后不获得额外初赛加分。
|
||||
|
||||
<a id="track-two"></a>
|
||||
|
||||
## 赛题二:AI Agent 与推理算子库
|
||||
|
||||
<a id="q-track-two-update"></a>
|
||||
|
||||
### ❓ 问题 1:赛题二的内容有什么调整?
|
||||
|
||||
**回答:** “Agent 推理算子库优化 - FlashAttention KV Cache Decode”新增 `mctlass/cute` 要求。参赛者需基于 `mctlass/cute` 实现或优化对应任务。
|
||||
|
||||
<a id="q-track-two-selection"></a>
|
||||
|
||||
### ❓ 问题 2:赛题二可以选择几个任务?
|
||||
|
||||
**回答:** 参赛团队可以从 FlashInfer、FlashAttention、MCTLASS/Fused MoE 等方向选择一项或多项。提交多个任务时,每个任务按赛事规则取有效最高成绩。支持语言包括 Triton、MXMACA C++ 和 TileLang。
|
||||
|
||||
<a id="q-agent-proof"></a>
|
||||
|
||||
### ❓ 问题 3:如何证明 AI Agent 参与了优化过程?
|
||||
|
||||
**回答:** 参赛团队应保留 Agent 配置、Skill 文件、关键提示词、操作日志、代码变更记录、测试结果和复现实验步骤,等待赛事方发布核验细则。
|
||||
|
||||
<a id="q-agent-baseline"></a>
|
||||
|
||||
### ❓ 问题 4:Agent 赛题的性能 baseline 使用哪个版本?
|
||||
|
||||
**回答:** 当前评测使用赛事方提供的 baseline,标准环境为 `PyTorch-Agent / 2.8.0 / Python 3.12 / MACA 3.7.1.5`。赛事方变更 baseline 或评测方式时会发布通知。
|
||||
|
||||
<a id="q-mla-dimensions"></a>
|
||||
|
||||
### ❓ 问题 5:MLA 的 `QK dim = 576, VO dim = 512` 与 `race_tests` 参数冲突吗?
|
||||
|
||||
**回答:** 不冲突。`race_tests` 中的 `dim=512, pe_dim=64` 对应 `QK dim = 576, V dim = 512`。
|
||||
|
||||
<a id="q-nsa-ranking"></a>
|
||||
|
||||
### ❓ 问题 6:NSA 的 109 个测试 case 如何计算榜单成绩?
|
||||
|
||||
**回答:** XPU-OJ 通过统一接口统计测试总运行时间,再根据 baseline 计算整体 speedup。
|
||||
|
||||
<a id="q-upstream-code"></a>
|
||||
|
||||
### ❓ 问题 7:可以引用或修改 FlashInfer、FlashAttention 等上游代码吗?
|
||||
|
||||
**回答:** 可以。参赛团队需遵守上游项目许可证,保留版权和许可证声明,并在提交材料中说明引用范围、迁移工作和自主优化内容。
|
||||
|
||||
<a id="evaluation"></a>
|
||||
|
||||
## 环境、镜像与算力资源
|
||||
|
||||
<a id="q-environment-image"></a>
|
||||
|
||||
### ❓ 问题 1:比赛使用哪个在线算力环境?
|
||||
|
||||
**回答:** 比赛使用模力方舟沐曦算力专区。仓库 README 当前标注的统一镜像为:
|
||||
|
||||
```text
|
||||
PyTorch-Agent / 2.8.0 / Python 3.12 / MACA 3.7.1.5
|
||||
```
|
||||
|
||||
创建实例和连接环境的步骤见[模力方舟快速使用 SOP](模力方舟快速使用SOP.md)。
|
||||
|
||||
<a id="q-download-maca"></a>
|
||||
|
||||
### ❓ 问题 2:如何获取 MACA 镜像或安装包?
|
||||
|
||||
**回答:** 参赛者可以在[模力方舟沐曦算力专区](https://ai.gitee.com/compute/metax)选择 `PyTorch-Agent` 镜像。需要单独获取软件包时,请前往[沐曦开发者社区软件中心](https://developer.metax-tech.com/softnova/docker?chip_name=%E6%9B%A6%E4%BA%91C500%E7%B3%BB%E5%88%97&package_kind=AI&dimension=docker),并选择与比赛标准环境一致的 MACA `3.7.1.5` 版本。
|
||||
|
||||
<a id="q-download-pytorch"></a>
|
||||
|
||||
### ❓ 问题 3:比赛使用的 PyTorch 镜像可以下载吗?
|
||||
|
||||
**回答:** 可以。请在[沐曦开发者社区](https://developer.metax-tech.com/)或其 [PyTorch 镜像列表](https://developer.metax-tech.com/softnova/docker?chip_name=%E6%9B%A6%E4%BA%91C500%E7%B3%BB%E5%88%97&package_kind=AI&dimension=docker&deliver_type=%E5%88%86%E5%B1%82%E5%8C%85&ai_frame=pytorch)中查询。下载前请核对比赛镜像的 Python、PyTorch 和 MACA 版本。
|
||||
|
||||
<a id="q-version-mismatch"></a>
|
||||
|
||||
### ❓ 问题 4:页面标注的 MACA 版本与容器内版本不一致怎么办?
|
||||
|
||||
**回答:** 比赛标准版本为 MACA `3.7.1.5`。
|
||||
|
||||
<a id="q-pytorch-source"></a>
|
||||
|
||||
### ❓ 问题 5:Linux 版本的 mcprofiler 是否可用?
|
||||
|
||||
**回答:** mcprofiler 的 Linux 版本已经打包进模力方舟上的 pytorch-agent 比赛镜像。
|
||||
|
||||
<a id="q-compute-coupons"></a>
|
||||
|
||||
### ❓ 问题 6:算力券按团队还是按个人领取?
|
||||
|
||||
**回答:** 新人礼和启悟社区算力券按学生个人发放,符合条件的团队成员均可领取。团队主申请人的额度用完后,其他成员可以继续申请资源。
|
||||
|
||||
- [沐曦开发者社区新人礼](https://developer.metax-tech.com/activities/6)
|
||||
- [启悟社区学生算力券](https://developer.metax-tech.com/activities/11)
|
||||
- [赛事算力券活动](https://developer.metax-tech.com/activities/17)
|
||||
|
||||
<a id="q-more-compute"></a>
|
||||
|
||||
### ❓ 问题 7:算力额度不足时可以追加申请吗?
|
||||
|
||||
**回答:** 可以先领取上述活动中的算力券。仍需额外资源时,请发送需求邮件至 `opensource@metax-tech.com`。
|
||||
|
||||
<a id="q-commercial-agent-cost"></a>
|
||||
|
||||
<a id="registration"></a>
|
||||
|
||||
## 报名、组队与资格审核
|
||||
|
||||
<a id="q-register-both"></a>
|
||||
|
||||
### ❓ 问题 1:同一团队或个人可以同时参加两个赛题吗?
|
||||
|
||||
**回答:** 可以。同一团队或个人可以同时报名两个赛题。
|
||||
|
||||
<a id="q-register-multiple-tracks"></a>
|
||||
|
||||
### ❓ 问题 2:同一名学生可以报名不同赛道的不同赛题吗?
|
||||
|
||||
**回答:** 可以。赛事不统一限制学生报名不同赛道或不同赛题,但同一作品不得用相同核心技术内容重复申报不同赛题。
|
||||
|
||||
<a id="q-new-graduate"></a>
|
||||
|
||||
### ❓ 问题 3:本科应届毕业、尚未正式入学的研一新生可以报名吗?
|
||||
|
||||
**回答:** 可以。参赛者可联系原本科学校完成认证手续,并以本科生身份报名。
|
||||
|
||||
<a id="q-advisor-required"></a>
|
||||
|
||||
### ❓ 问题 4:参赛必须配备指导教师吗?
|
||||
|
||||
**回答:** 不强制。填写指导教师时,每支队伍可以设置 1 至 3 名指导教师。
|
||||
|
||||
<a id="q-advisor-team-limit"></a>
|
||||
|
||||
### ❓ 问题 5:一名指导教师最多可以指导几支队伍?
|
||||
|
||||
**回答:** 赛事暂未设置统一的硬性上限。指导教师应根据可投入的时间控制队伍数量。
|
||||
|
||||
<a id="q-cross-school-stamp"></a>
|
||||
|
||||
### ❓ 问题 6:跨校组队时,报名表应该由哪所学校盖章?
|
||||
|
||||
**回答:** 资格审批阶段,每名参赛者需到本人学校的校团委或院团委完成盖章确认。后续材料由团队牵头学生统一整理和提交。
|
||||
|
||||
<a id="q-upload-stamped-form"></a>
|
||||
|
||||
### ❓ 问题 7:提交报名后还可以补充已盖章的报名表扫描件吗?
|
||||
|
||||
**回答:** 审核人员发现材料缺少盖章时,会退回申请。团队补齐材料后可以重新提交。尚未完成盖章的团队应先与学院、校团委或学校相关部门确认办理方式。
|
||||
|
||||
<a id="q-stamp-department"></a>
|
||||
|
||||
### ❓ 问题 8:资格审查材料应该加盖哪个部门的公章?
|
||||
|
||||
**回答:** 各高校的管理口径不同。教务处、学生处等学籍或学生管理部门通常可以办理,参赛团队应以本校校团委或相关管理部门的要求为准。
|
||||
|
||||
<a id="q-no-youth-league"></a>
|
||||
|
||||
### ❓ 问题 9:学校未设校团委,可以用院系公章替代吗?
|
||||
|
||||
**回答:** 赛事原则上要求校级部门公章。学校未设校团委时,可以联系校级学工、双创或教务部门盖章,并提交情况说明。院系公章不能直接替代校级部门公章。
|
||||
|
||||
<a id="q-student-status-proof"></a>
|
||||
|
||||
### ❓ 问题 10:学校无法配合盖章,可以用学籍证明替代吗?
|
||||
|
||||
**回答:** 不可以。参赛团队应使用报名系统导出的报名表,并按要求完成学校盖章。
|
||||
|
||||
<a id="q-public-notice"></a>
|
||||
|
||||
### ❓ 问题 11:公示材料需要包含哪些内容?
|
||||
|
||||
**回答:** 请参考赛事工作群发布的参考文本,并按学校要求调整。跨校团队涉及的学校应分别在学校官网公示,公示渠道原则上使用学校官网。
|
||||
|
||||
<a id="q-review-deadline"></a>
|
||||
|
||||
### ❓ 问题 12:报名审核需要在报名截止日前完成吗?
|
||||
|
||||
**回答:** 原则上需要。往届出现过系统延后关闭的情况,但本届参赛团队不应据此推迟材料提交或审核。
|
||||
|
||||
<a id="q-review-flow"></a>
|
||||
|
||||
### ❓ 问题 13:报名材料的审核顺序是什么?
|
||||
|
||||
**回答:** 学生提交材料后,学校校团委先审核;学校审核通过后,企业再审核。企业审核通过即视为报名成功。
|
||||
|
||||
<a id="q-school-review-account"></a>
|
||||
|
||||
### ❓ 问题 14:后台显示“校团委审核”,但学校不了解审核事项,怎么办?
|
||||
|
||||
**回答:** 校团委需在报名系统内完成审核。省级团委通常会向各高校团委发放账号和密码。学校未收到或不了解安排时,请学校联系省级团委确认。
|
||||
|
||||
## FAQ 维护约定
|
||||
|
||||
1. 参赛者通过 Issue 提交问题。
|
||||
2. 维护者确认答案后更新本文档。
|
||||
3. 每个答案保留稳定锚点;需要时附上来源 Issue 或公告。
|
||||
4. 维护者在原 Issue 中回复 FAQ 锚点链接,并关闭已经解决的问题。
|
||||
5. 涉及版本、日期、评测参数的答案应标注确认日期。
|
||||
21
README.md
21
README.md
|
|
@ -1,5 +1,22 @@
|
|||
# 降低Token 成本,攻坚国产推理生态|沐曦两大赛题登陆 2026 揭榜挂帅擂台赛,邀青年共破局!
|
||||
|
||||
## 常用入口
|
||||
|
||||
- [常见问题 FAQ](FAQ.md)
|
||||
- [沐曦通用GPU MXMACA编译器内建函数编程指南](https://developer.metax-tech.com/api/client/document/preview/1395/index.html)
|
||||
|
||||
## XPU-OJ 基线修复及榜单调整通知
|
||||
|
||||
针对近期部分同学反馈的“XPU.OJ”第三方评测系统中基线(baseline)不稳定的问题,我们高度重视,并已第一时间组织排查与测试。在此,我们对因此给大家带来的困扰深表歉意,也衷心感谢各位同学提出的宝贵意见。
|
||||
目前,相关问题已修复完毕。为确保评测的公平性与准确性,我们将对现有榜单进行清空处理。历史提交记录仍可查看,但后续排名将统一以基线修复后重新提交的算子成绩为准。
|
||||
|
||||
比赛期间,我们将持续关注系统运行状态,也欢迎大家继续向我们反馈建议。
|
||||
祝大家比赛顺利,取得理想成绩!
|
||||
|
||||
- XPU-OJ 地址:[https://xpuoj.com/](https://xpuoj.com/)
|
||||
- 账号申领说明:[赛事 XPU-OJ 账号申领说明](赛事XPUOJ账号申领说明.md)
|
||||
- 账号申领邮箱:`opensource@metax-tech.com`
|
||||
|
||||
2026 年度中国青年科技创新「揭榜挂帅」擂台赛正式启幕。沐曦股份重磅发布两大 AI 算力硬核榜题,聚焦国产 GPU 大模型推理算子优化,以硬核赛事搭建科研攻关平台,邀全国青年学子、科研人才揭榜攻坚,用技术重构推理效率,用创新拉低每 Token 算力成本!
|
||||
|
||||
## 两大重磅赛题 直击推理成本核心痛点
|
||||
|
|
@ -29,7 +46,7 @@
|
|||
|
||||
### 赛题二:基于 AI Agent 开发范式的国产 GPU 大模型推理算子库优化
|
||||
|
||||
大模型推理具有高并发、长序列、高调用频次等特点,FlashInfer、FlashAttention、Fused MoE 等核心算子直接决定模型服务的吞吐、延迟与显存开销,影响单 Token 综合推理成本。
|
||||
大模型推理具有高并发、长序列、高<EFBFBD><EFBFBD><EFBFBD>用频次等特点,FlashInfer、FlashAttention、Fused MoE 等核心算子直接决定模型服务的吞吐、延迟与显存开销,影响单 Token 综合推理成本。
|
||||
|
||||
本赛题面向沐曦国产 GPU 及 MXMACA 软件栈,鼓励参赛团队构建或使用 AI Agent / Skill 工作流,围绕推理算子库开展代码理解、算子迁移、性能分析、Kernel 优化、自动调优、Benchmark 验证和多轮迭代,探索“Agent 驱动算子优化”的新型开发范式。
|
||||
|
||||
|
|
@ -50,7 +67,7 @@
|
|||
- [模力方舟 Agent 部署准备教程](基于AI%20Agent开发范式的国产GPU大模型推理算子库优化/模力方舟Agent部署准备教程.md)
|
||||
- [赛题二说明及资料参考](基于AI%20Agent开发范式的国产GPU大模型推理算子库优化/赛题说明.md)
|
||||
|
||||
### **两个赛题统一使用模力方舟上的镜像PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.2.1**
|
||||
### **两个赛题统一使用模力方舟上的镜像PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.1.5**
|
||||
|
||||
## 参赛对象
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Flashattention 迁移 Benchmark 实战:从性能基线到 XPU-OJ 评测
|
||||
# Agent推理算子库优化-FlashAttention KV Cache Decode Benchmark 实战:从性能基线到 XPU-OJ 评测
|
||||
|
||||
## 1. 教程定位
|
||||
|
||||
本教程是参赛训练课程的 **FlashAttention Benchmark 入门与评测提交衔接** 模块,主要帮助用户跑通 FlashAttention paged KV-cache 推理核函数 `flash_attn_with_kvcache` 的基准测试流程,理解 benchmark 脚本的输入输出、性能指标和评测含义,并基于 XPU-OJ 题包完成一个最小正确版 `run_kernel` 的实现与提交。
|
||||
本教程面向 **沐曦-揭榜挂帅-Agent推理算子库优化-FlashAttention任务**,是围绕题目 **Agent推理算子库优化-FlashAttention KV Cache Decode** 的 **FlashAttention Benchmark 入门与评测提交衔接** 模块,主要帮助用户跑通 FlashAttention paged KV-cache 推理核函数 `flash_attn_with_kvcache` 的基准测试流程,理解 benchmark 脚本的输入输出、性能指标和评测含义,并基于 XPU-OJ 题包完成一个最小正确版 `run_kernel` 的实现与提交。
|
||||
|
||||
需要特别说明:本教程中的 benchmark 脚本主要用于帮助参赛者理解目标算子的调用方式、输入输出结构和性能基线,benchmark 脚本不是最终提交物。最终评测以 XPU-OJ 题包为准,参赛者需要根据题包中的接口约定实现自己的 `run_kernel`,并在输出结果对齐 OJ 参考结果的前提下提升性能。
|
||||
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
本模块适合以下人员:
|
||||
|
||||
* 参与 AI 基础设施竞赛的参赛者
|
||||
* 参与 **沐曦-揭榜挂帅-Agent推理算子库优化-FlashAttention任务** 的参赛者
|
||||
* 对 GPU 算子性能优化感兴趣的开发者
|
||||
* 需要了解 FlashAttention KV-Cache 推理性能的研究人员
|
||||
|
||||
|
|
@ -54,10 +54,10 @@
|
|||
**创建并启动实例**
|
||||
|
||||
1. 进入算力市场,筛选“沐曦”芯片厂商,选择合适的 GPU 规格,推荐曦云 C500 节点。
|
||||
2. 关键配置:在预装镜像处,务必选择专属开发镜像 PyTorch Agent / 2.8.0 / Python 3.12 / maca 3.7.2.1。
|
||||
2. 关键配置:在预装镜像处,务必选择专属开发镜像 PyTorch Agent / 2.8.0 / Python 3.12 / maca 3.7.1.5。
|
||||
3. 创建完成后,进入算力容器,点击“工具-lab”即可打开 JupyterLab 终端开始项目创作。
|
||||
|
||||

|
||||

|
||||
|
||||
**说明:** 由于本次使用的是预装的专属镜像,环境中已经默认安装并配置好了 PyTorch、FlashAttention、einops 等依赖包。因此在启动实例后,无需再进行繁琐的依赖库版本验证即可直接进入测试环节。
|
||||
|
||||
|
|
@ -200,7 +200,7 @@ ls
|
|||
**预期结果:**
|
||||
|
||||
```text
|
||||
benchmark结果实例 benchmark_kvcache.py
|
||||
benchmark_kvcache.py
|
||||
```
|
||||
|
||||
### Step 4:配置基准测试参数
|
||||
|
|
@ -371,7 +371,7 @@ Benchmark 脚本用于理解目标算子的调用方式、输入输出 shape 和
|
|||
跑完 benchmark、建立性能基线后,选手需要完成以下转换:
|
||||
|
||||
1. 从 benchmark 脚本中理解目标 API,本任务对应 `flash_attn.flash_attn_interface` 中的 `flash_attn_with_kvcache`,使用 paged KV cache 布局。
|
||||
2. 在 XPU-OJ 平台上查看 `FlashAttention KV Cache Decode` 题目的接口约定。
|
||||
2. 在 XPU-OJ 平台上查看 **Agent推理算子库优化-FlashAttention KV Cache Decode** 题目的接口约定。
|
||||
3. 对照题包中的输入 shape、数据范围和精度要求。
|
||||
4. 编写自己的 `run_kernel(...)`。
|
||||
5. 提交 OJ,先通过正确性。
|
||||
|
|
@ -379,20 +379,20 @@ Benchmark 脚本用于理解目标算子的调用方式、输入输出 shape 和
|
|||
|
||||
### Step 9:题目说明与提交入口
|
||||
|
||||
注意:每个子题的接口参数、数据范围和精度要求可能不同,正式要求以对应 XPU-OJ 题目界面为准。本节以 **FlashAttention KV Cache Decode** 为例,演示从 benchmark 到 XPU-OJ 提交的完整流程。
|
||||
本教程只覆盖 **Agent推理算子库优化-FlashAttention KV Cache Decode** 这一题。正式接口、数据范围和精度要求以 XPU-OJ 题目界面为准。本节演示从 benchmark 到 XPU-OJ 提交的完整流程。
|
||||
|
||||
* **算子说明**:实现 paged KV cache 下的 decode 注意力,每个 batch 只有 1 个 query token,KV cache 按 page 存储,长度由 `seqlen_k` 决定。
|
||||
* **对应 OJ 题目**:XPU-OJ 上 `FlashAttention KV Cache Decode` 题(题号 20005)。
|
||||
* **对应 OJ 题目**:XPU-OJ 上 **Agent推理算子库优化-FlashAttention KV Cache Decode** 题。
|
||||
|
||||
使用组委会统一发放的账号登录 XPU-OJ,并进入对应赛题页面。
|
||||
使用组委会统一发放的账号登录 XPU-OJ,并进入对应比赛页面和题目页面。
|
||||
|
||||
1. 打开 XPU-OJ 平台:https://xpuoj.com/
|
||||
2. 使用组委会统一发放的账号和初始密码登录。
|
||||
3. 登录后进入比赛 / 题目列表页面。
|
||||
4. 找到对应题目,例如 `20005 FlashAttention KV Cache Decode`。
|
||||
3. 登录后进入比赛 / 题目列表页面,找到比赛 **沐曦-揭榜挂帅-Agent推理算子库优化-FlashAttention任务**。
|
||||
4. 找到对应题目 **Agent推理算子库优化-FlashAttention KV Cache Decode**。
|
||||
5. 点击进入题目详情页,查看题目描述、接口约定、数据范围和提交入口。
|
||||
|
||||

|
||||

|
||||
|
||||
### Step 10:理解 CUDA Maca 接口约定与精度要求
|
||||
|
||||
|
|
@ -468,7 +468,7 @@ PAGE_BLOCK_SIZE = 16
|
|||
CAUSAL = 0
|
||||
```
|
||||
|
||||
当前 `FlashAttention KV Cache Decode` 题的校验方式为:
|
||||
当前 **Agent推理算子库优化-FlashAttention KV Cache Decode** 题的校验方式为:
|
||||
|
||||
```python
|
||||
torch.allclose(output_t.float(), output_ref.float(), rtol=1e-2, atol=1e-2)
|
||||
|
|
@ -505,14 +505,14 @@ OJ 对每次提交大致会走以下流程:
|
|||
7. 正确性通过后,统计运行耗时或性能指标
|
||||
8. 根据题目评分规则换算该题得分
|
||||
9. 更新该题历史最好成绩
|
||||
10. 汇总各题最好成绩,得到排行榜总分
|
||||
10. 在榜单中展示本题得分和排名
|
||||
```
|
||||
|
||||
### Step 12:分析评测结果与评分机制
|
||||
|
||||
**目标:** 理解 OJ 评测结果的含义,分析性能表现。
|
||||
|
||||
在 XPU-OJ 平台上查看提交结果。提交详情会显示状态、总得分、时间、内存、编译信息以及各测试点结果。
|
||||
在 XPU-OJ 平台上查看提交结果。提交详情会显示状态、本题得分、时间、内存、编译信息以及各测试点结果。
|
||||
|
||||

|
||||
|
||||
|
|
@ -555,15 +555,22 @@ OJ 对每次提交大致会走以下流程:
|
|||
|
||||
OJ 平台对单测试点的评分遵循以下公式:
|
||||
|
||||
$$
|
||||
```math
|
||||
S(T_k) = \frac{100}{1 + \left(\frac{1}{0.5} - 1\right) \cdot \frac{T_k - T_h}{T_b - T_h}}
|
||||
$$
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
* $T_k$:你的 kernel 平均执行时间
|
||||
* $T_b$:Baseline 参考实现平均执行时间,对应 50 分
|
||||
* $T_h$:硬件理论下限耗时,$T_h = \max\left(\frac{\text{FLOPs}}{\text{peak\_tflops}},\ \frac{\text{bytes}}{\text{peak\_bw}}\right)$,对应 100 分
|
||||
* $T_h$:硬件理论下限耗时,对应 100 分,计算方式为:
|
||||
|
||||
```math
|
||||
T_h = \max\left(
|
||||
\frac{\mathrm{FLOPs}}{\mathrm{peak\_tflops}},
|
||||
\frac{\mathrm{bytes}}{\mathrm{peak\_bw}}
|
||||
\right)
|
||||
```
|
||||
|
||||
**关键分数节点:**
|
||||
|
||||
|
|
@ -576,23 +583,23 @@ $$
|
|||
|
||||
当单测试点得分超过 150 分时,平台会按对数压缩规则显示:
|
||||
|
||||
$$
|
||||
S_{\text{display}} = 150 + 10 \cdot \log_{10}(S/150)
|
||||
$$
|
||||
```math
|
||||
S_{\mathrm{display}} = 150 + 10 \cdot \log_{10}\left(\frac{S}{150}\right)
|
||||
```
|
||||
|
||||
总得分为各测试点得分的算术平均,总耗时为各测试点 $T_k$ 的求和。
|
||||
本题得分为各测试点得分的算术平均,本题总耗时为各测试点 $T_k$ 的求和。
|
||||
|
||||
### Step 13:榜单查看与初步优化方向
|
||||
|
||||
在榜单页面,可以查看所有参赛者的排名情况:
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
* **总得分:** 各题目得分的总和,排名按总得分从高到低排序。
|
||||
* **个人排名:** 页面顶部会显示“我的排名”和“我的总分”,方便快速了解自己的位置。
|
||||
* **各题目得分:** 表格中每列对应一个题目的得分,帮助分析不同算子优化任务上的表现。
|
||||
* **本题得分:** 展示该题的历史最好成绩,排名按本题得分从高到低排序。
|
||||
* **个人排名:** 页面顶部会显示你的当前排名和当前得分,方便快速了解自己的位置。
|
||||
* **提交次数:** 分数下方括号中的数字表示该账号在本题下的提交次数。
|
||||
|
||||
初步优化方向包括:
|
||||
|
||||
|
|
@ -637,7 +644,7 @@ opencode
|
|||
|
||||
| 任务阶段 | 参考 Prompt 模板 | 核心目的 |
|
||||
| --- | --- | --- |
|
||||
| 题包解析 | 请阅读题号 20005 的题目界面和 FlashAttention 任务包材料,总结 CUDA Maca `run_kernel` 函数签名、Paged KV Cache 寻址公式、精度校验方式和测试数据范围。 | 提取接口契约,明确参数 shape 和边界条件 |
|
||||
| 题包解析 | 请阅读 **Agent推理算子库优化-FlashAttention KV Cache Decode** 的题目界面和 FlashAttention 任务包材料,总结 CUDA Maca `run_kernel` 函数签名、Paged KV Cache 寻址公式、精度校验方式和测试数据范围。 | 提取接口契约,明确参数 shape 和边界条件 |
|
||||
| 生成冒烟代码 | 请生成一个最小可运行的 CUDA Maca `run_kernel` 实现,要求严格匹配 `extern "C"` 签名,支持 Paged KV Cache 的 `block_table` 寻址,支持 head 映射,优先保证正确性。 | 快速验证接口和环境 |
|
||||
| OJ 报错调试 | 我的代码提交后 `Wrong Answer`。这是我的代码和 SPJ Report。请检查 Paged KV 地址映射、bf16 到 float32 的计算转换、尾部 page 有效 token 判断是否正确。 | 结构化排查功能错误 |
|
||||
| 性能瓶颈分析 | 这是测试点的 SPJ Report。请分析 `User kernel` 与 `Hardware bound` 的差距,判断更接近 compute-bound 还是 memory-bound,并给出具体的 mctlass 或访存优化建议。 | 将 OJ 反馈转化为优化行动 |
|
||||
|
|
@ -645,7 +652,7 @@ opencode
|
|||
**题包解析 Prompt:**
|
||||
|
||||
```text
|
||||
请阅读 XPU-OJ 上题号 20005 FlashAttention KV Cache Decode 的题目说明,以及 flashattn_task_package 中与 benchmark / 提交相关的材料。
|
||||
请阅读 XPU-OJ 上 **Agent推理算子库优化-FlashAttention KV Cache Decode** 的题目说明,以及 flashattn_task_package 中与 benchmark / 提交相关的材料。
|
||||
|
||||
请输出以下内容:
|
||||
1. CUDA Maca 版本 run_kernel 的完整函数签名;
|
||||
|
|
@ -686,7 +693,7 @@ opencode
|
|||
**Wrong Answer 调试 Prompt:**
|
||||
|
||||
```text
|
||||
我的 FlashAttention KV Cache Decode 代码提交后出现 Wrong Answer。
|
||||
我的 Agent推理算子库优化-FlashAttention KV Cache Decode 代码提交后出现 Wrong Answer。
|
||||
|
||||
这是我的代码:
|
||||
[粘贴代码]
|
||||
|
|
@ -708,7 +715,7 @@ opencode
|
|||
**性能瓶颈分析 Prompt:**
|
||||
|
||||
```text
|
||||
这是 FlashAttention KV Cache Decode 某个测试点的 SPJ Report:
|
||||
这是 Agent推理算子库优化-FlashAttention KV Cache Decode 某个测试点的 SPJ Report:
|
||||
[粘贴报告]
|
||||
|
||||
请分析:
|
||||
|
|
@ -770,11 +777,11 @@ opencode
|
|||
|
||||
### 10.4 结合 OJ Report 做定向优化
|
||||
|
||||
不要只看总分。单测试点的 `Config`、`User kernel`、`Hardware bound` 和 `Speedup vs base` 更适合指导下一轮优化方向。长序列、大 batch、小 batch 的瓶颈可能完全不同。
|
||||
不要只看本题最终得分。单测试点的 `Config`、`User kernel`、`Hardware bound` 和 `Speedup vs base` 更适合指导下一轮优化方向。长序列、大 batch、小 batch 的瓶颈可能完全不同。
|
||||
|
||||
### 10.5 扩展到其他赛题
|
||||
### 10.5 继续优化本题
|
||||
|
||||
完成 FlashAttention KV Cache Decode 后,可以继续尝试 XPU-OJ 上的其他算子优化题目,例如 FlashInfer MLA Paged Attention 或 FlashInfer Paged Prefill
|
||||
完成 Agent推理算子库优化-FlashAttention KV Cache Decode 的冒烟提交后,可以继续围绕不同 batch、KV 长度和 head dimension 配置做定向优化。
|
||||
|
||||
## 附录:完整代码参考
|
||||
|
||||
|
|
|
|||
|
|
@ -2,21 +2,21 @@
|
|||
|
||||
## 1. 教程定位
|
||||
|
||||
本教程是赛题二 FlashInfer 任务的 “benchmark 性能基线与 XPU-OJ 提交衔接” 模块,主要帮助学员跑通目标算子的 benchmark 脚本,理解原库 API、输入输出结构、性能指标和性能基线结果,并进一步读懂 XPU-OJ 题目包中的接口约定、测试数据、参考输出和精度要求。
|
||||
本教程是 **沐曦 - 揭榜挂帅 - Agent 推理算子库优化 - FlashInfer 任务** 的 “benchmark 性能基线与 XPU-OJ 提交衔接” 模块,主要帮助学员跑通目标算子的 benchmark 脚本,理解原库 API、输入输出结构、性能指标和性能基线结果,并进一步读懂 XPU-OJ 题目中的接口约定、测试数据、参考输出和精度要求。
|
||||
|
||||
需要特别说明:
|
||||
|
||||
- 本教程不提供可直接提交的 OJ 参考实现源代码或标准答案代码。
|
||||
- 本教程不提供可直接提交的 OJ 参考实现源代码或标准答案代码;
|
||||
|
||||
- 本教程仅提供冒烟级 starter 示例代码,用于验证环境、语言、提交链路和 `run_kernel(...)` 接口。
|
||||
- 本教程仅提供冒烟级 starter 示例代码,用于验证环境、语言、提交链路和 `run_kernel(...)` 接口;
|
||||
|
||||
- benchmark 脚本用于建立性能基线,不是最终提交物。
|
||||
- benchmark 脚本用于建立性能基线,不是最终提交物;
|
||||
|
||||
- XPU-OJ 题包中的 `baseline()` 属于 OJ 后台参考实现,用于生成 `output_ref`,不是选手提交代码。
|
||||
- XPU-OJ 上题目说明中的 `baseline()` 属于 OJ 后台参考实现,用于生成 `output_ref`,不是选手提交代码;
|
||||
|
||||
- 选手最终需要自行实现 `run_kernel(...)`,并在正确性通过后继续优化性能。
|
||||
|
||||
完成本教程后,学员应能够跑通 benchmark 脚本,记录性能基线结果,读懂 XPU-OJ 题包,理解 OJ 的测试输入与参考实现,并完成一次冒烟级 OJ 提交。
|
||||
完成本教程后,学员应能够跑通 benchmark 脚本,记录性能基线结果,读懂 XPU-OJ 上的题目,理解 OJ 的测试输入与参考实现,并完成一次冒烟级 OJ 提交。
|
||||
|
||||
## 2. 学习目标
|
||||
|
||||
|
|
@ -28,11 +28,11 @@
|
|||
|
||||
3. 跑通 BatchDecode、BatchPrefill、MLA 等典型算子的 benchmark 脚本,记录性能基线结果,为后续算子优化提供对比基准;
|
||||
|
||||
4. 读懂对应 XPU-OJ 题包中的题目描述、接口约定、数据范围和精度要求;
|
||||
4. 读懂对应 XPU-OJ 题目中的题目描述、接口约定、数据范围和精度要求;
|
||||
|
||||
5. 完成一次冒烟级 `run_kernel(...)` 提交,确认 OJ 链路、语言环境和接口调用正常;
|
||||
|
||||
6. 使用 AI Agent 辅助阅读题包、生成初版实现、定位错误并规划性能优化方向。
|
||||
6. 使用 AI Agent 辅助阅读题目、生成初版实现、定位错误并规划性能优化方向。
|
||||
|
||||
## 3. 适用对象
|
||||
|
||||
|
|
@ -75,7 +75,15 @@
|
|||
|
||||
使用兑换码兑换 GPU 租用余额:
|
||||
|
||||
访问 [*模力方舟官网*](https://ai.gitee.com/),在左侧边栏进入 “费用中心”,点击右上角 “兑换” 使用兑换码兑换代金券;
|
||||
访问 [*模力方舟官网*](https://ai.gitee.com/),首次登录需要使用手机号或者 Gitee 账号进行注册。登录后显示页面:
|
||||
|
||||

|
||||
|
||||
点击页面左上角 “giteeAI - 模力方舟” 图标,进入用户控制台:
|
||||
|
||||

|
||||
|
||||
在左侧边栏选择 “费用中心”,点击右上角 “兑换” 使用兑换码兑换代金券:
|
||||
|
||||

|
||||
|
||||
|
|
@ -86,9 +94,9 @@
|
|||
|
||||

|
||||
|
||||
2. 进入 “创建实例” 页面,确认计费方式为 “按量收费”,预装镜像选择:基础镜像 - **PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.2.1**,点击下一步;
|
||||
2. 进入 “创建实例” 页面,确认计费方式为 “按量收费”,预装镜像选择:基础镜像 - **PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.1.5**,点击下一步;
|
||||
|
||||

|
||||

|
||||
|
||||
3. 勾选同意服务条款,点击 “创建实例”;
|
||||
|
||||
|
|
@ -126,7 +134,7 @@
|
|||
|
||||

|
||||
|
||||
此部分内容可参考教程:[*模力方舟快速使用SOP*](../模力方舟快速使用SOP.md)
|
||||
此部分内容可参考教程:[*模力方舟快速使用SOP*](https://gitlink.org.cn/metax-maca/op_optimization/tree/master/%E6%A8%A1%E5%8A%9B%E6%96%B9%E8%88%9F%E5%BF%AB%E9%80%9F%E4%BD%BF%E7%94%A8SOP.md)
|
||||
|
||||
#### 4.1.4 深度学习环境配置
|
||||
|
||||
|
|
@ -161,7 +169,7 @@ which mxcc && mxcc --version || echo "mxcc 未找到,请确认 MACA 工具链
|
|||
|
||||
| 问题 | 解决方法 |
|
||||
| --- | --- |
|
||||
| `mxcc: command not found` | MACA 工具链未安装或 `PATH` 未配置,检查镜像是否预装或参考 [*模力方舟快速使用SOP*](../模力方舟快速使用SOP.md) |
|
||||
| `mxcc: command not found` | MACA 工具链未安装或 `PATH` 未配置,检查镜像是否预装或参考 [*模力方舟快速使用SOP*](https://gitlink.org.cn/metax-maca/op_optimization/tree/master/%E6%A8%A1%E5%8A%9B%E6%96%B9%E8%88%9F%E5%BF%AB%E9%80%9F%E4%BD%BF%E7%94%A8SOP.md) |
|
||||
|
||||
### 4.2 工具准备
|
||||
|
||||
|
|
@ -171,7 +179,7 @@ which mxcc && mxcc --version || echo "mxcc 未找到,请确认 MACA 工具链
|
|||
|
||||
- 已确认 Agent 可以正常调用模型。
|
||||
|
||||
配置过程可参考 [*模力方舟Agent部署准备教程*](模力方舟Agent部署准备教程.md)。
|
||||
配置过程可参考 [*模力方舟Agent部署准备教程*](https://gitlink.org.cn/metax-maca/op_optimization/tree/master/%E5%9F%BA%E4%BA%8EAI%20Agent%E5%BC%80%E5%8F%91%E8%8C%83%E5%BC%8F%E7%9A%84%E5%9B%BD%E4%BA%A7GPU%E5%A4%A7%E6%A8%A1%E5%9E%8B%E6%8E%A8%E7%90%86%E7%AE%97%E5%AD%90%E5%BA%93%E4%BC%98%E5%8C%96%2F%E6%A8%A1%E5%8A%9B%E6%96%B9%E8%88%9FAgent%E9%83%A8%E7%BD%B2%E5%87%86%E5%A4%87%E6%95%99%E7%A8%8B.md)。
|
||||
|
||||
|
||||
**以配置 OpenCode 为例**
|
||||
|
|
@ -202,6 +210,23 @@ XPU-OJ 账号由组委会统一发放,参赛者无需自行注册。
|
|||
|
||||
> 如果登录后看不到题目,请联系助教或赛事运营确认账号是否已加入对应比赛 / 用户组
|
||||
|
||||
### 4.5 评分规则概要
|
||||
|
||||
评分规则详情参考 [*基于AI Agent开发范式的国产GPU大模型推理算子库优化比赛方案*](https://gitlink.org.cn/metax-maca/op_optimization/tree/master/%E5%9F%BA%E4%BA%8EAI%20Agent%E5%BC%80%E5%8F%91%E8%8C%83%E5%BC%8F%E7%9A%84%E5%9B%BD%E4%BA%A7GPU%E5%A4%A7%E6%A8%A1%E5%9E%8B%E6%8E%A8%E7%90%86%E7%AE%97%E5%AD%90%E5%BA%93%E4%BC%98%E5%8C%96%2F%E5%9F%BA%E4%BA%8EAI%20Agent%E5%BC%80%E5%8F%91%E8%8C%83%E5%BC%8F%E7%9A%84%E5%9B%BD%E4%BA%A7GPU%E5%A4%A7%E6%A8%A1%E5%9E%8B%E7%AE%97%E5%AD%90%E6%8E%A8%E7%90%86%E5%BA%93%E4%BC%98%E5%8C%96%E6%96%B9%E6%A1%88.md)。
|
||||
|
||||
提交作品的最终评价采用 **100 分制**,由客观评测和专家评审共同组成:
|
||||
|
||||
| 类别 | 评审维度 | 权重 | 说明 |
|
||||
|------|---------|:---:|------|
|
||||
| 客观评测 | **性能提升效果** | **60%** | 基于 XPU-OJ 各任务榜单排名计分。各任务独立计分,**最终仅取得分最高的一个任务**计入"性能提升效果",不叠加计分。进入前 30 名且 OJ 分数 > 50 的提交按排名计分:第 1 名得 60 分,每降 1 名扣 2 分,第 30 名得 2 分。未进前 30 或 OJ 分数 ≤ 50 的提交不得分。未通过正确性或稳定性测试的作品记 0 分 |
|
||||
| 客观评测 | **Agent/Skill 可复现性** | **20%** | Agent 应真实参与源码理解、代码生成、性能分析、自动调优、Benchmark 和多轮迭代等优化过程。分四档:1. 功能可复现得 5 分;2. 性能复现达提交标称 60% 以上得 10 分;3. 80% 以上得 15 分;4. 90% 以上得 20 分 |
|
||||
| 客观+主观 | **文档说明与演示报告** | **20%** | 根据技术报告、README、运行说明、性能测试报告、Agent/Skill 说明文档、演示视频及答辩材料的完整性、规范性、技术表达质量和工程可复现性综合评分 |
|
||||
|
||||
|
||||
> - OJ 榜单分数与比赛最终得分属于不同分值体系。OJ 内部以各算子 baseline 为 50 分基准、硬件理论上限约 100 分;比赛性能部分满分 60 分,按排名映射;
|
||||
> - FlashInfer 方向包含多个子任务,任选一种或多种提交,各子任务独立计分,取通过正确性和稳定性测试的最高成绩参与排名,即 **FlashInfer 任务分数** = $\max$\{子题1分数, ..., 子题4分数\};
|
||||
> - 组委会对提交内容严格审查,严禁抄袭,一经发现取消成绩。
|
||||
|
||||
## 5. 知识预备
|
||||
|
||||
### 5.1 LLM 推理阶段重要概念
|
||||
|
|
@ -239,11 +264,11 @@ XPU-OJ 账号由组委会统一发放,参赛者无需自行注册。
|
|||
|
||||
[*GitHub - MetaX-MACA/McFlashInfer*](https://github.com/MetaX-MACA/McFlashInfer)
|
||||
|
||||
> 链接内容可供用于学习 API、算子实现思路、benchmark 方法和优化策略。选手仍需根据 XPU-OJ 题包接口**自行实现**可提交的 `run_kernel(...)`
|
||||
> 链接内容可供用于学习 API、算子实现思路、benchmark 方法和优化策略。选手仍需根据 XPU-OJ 题目接口**自行实现**可提交的 `run_kernel(...)`
|
||||
|
||||
## 6. 项目实践 -- FlashInfer Benchmark
|
||||
|
||||
**目标:** 以一个具体算子题目 **20001 FlashInfer Ragged Prefill** 为例,跑通 benchmark 脚本,建立性能基线,理解对应 XPU-OJ 题目包,并完成一次 OJ 冒烟提交,为后续 Agent 辅助优化建立起点。
|
||||
**目标:** 以一个具体算子题目 **20001 FlashInfer Ragged Prefill** 为例,理解对应题目,跑通 benchmark 脚本,建立性能基线,并完成一次 OJ 冒烟提交,为后续 Agent 辅助优化建立起点。
|
||||
|
||||
### 6.1 在赛事镜像中运行 FlashInfer Benchmark
|
||||
|
||||
|
|
@ -329,7 +354,7 @@ pip install pandas
|
|||
|
||||
**预期结果:**
|
||||
|
||||

|
||||

|
||||
|
||||
#### Step 3:验证项目脚本
|
||||
|
||||
|
|
@ -441,55 +466,52 @@ else:
|
|||
|
||||
**Benchmark 与 XPU-OJ 的关系**
|
||||
|
||||
赛事镜像中的 Benchmark 和 XPU-OJ 在线评测任务不同。Benchmark 脚本用于理解目标算子的调用方式、输入输出 shape 和性能基线;XPU-OJ 题包用于定义最终评测接口、数据范围、参考输出和精度要求。
|
||||
赛事镜像中的 Benchmark 和 XPU-OJ 在线评测任务不同。Benchmark 脚本用于理解目标算子的调用方式、输入输出 shape 和性能基线;XPU-OJ 题目说明用于定义最终评测接口、数据范围、参考输出和精度要求。
|
||||
|
||||
| 维度 | Benchmark 脚本 | XPU-OJ 提交 |
|
||||
|------|---------------|------------|
|
||||
| **目的** | 理解算子接口、建立性能基线 | 统一环境下的正确性+性能评测 |
|
||||
| **接口形式** | Python API(`wrapper.plan()` + `wrapper.run()`) | C 接口(`extern "C" void run_kernel(...)`) |
|
||||
| **数据范围** | 多种 head_dim / batch_size / seq_len 组合 | 固定参数范围(以题包为准) |
|
||||
| **数据范围** | 多种 head_dim / batch_size / seq_len 组合 | 固定参数范围(以 XPU-OJ 题目说明为准) |
|
||||
| **验证** | 无自动正确性校验 | 强制通过 `torch.allclose(rtol=1e-2, atol=1e-2)` |
|
||||
| **输出** | CSV 性能记录 | 排行榜得分 |
|
||||
| **输出** | CSV 性能记录 | 排行榜得分(XPU-OJ 内部得分) |
|
||||
|
||||
跑完 benchmark、建立性能基线后,选手需要完成以下转换:
|
||||
1. 从 benchmark 脚本中理解目标 API,例如 `BatchPrefillWithRaggedKVCacheWrapper`;
|
||||
|
||||
2. 在对应 OJ 题包中查看 `run_kernel(...)` 接口;
|
||||
2. 在 XPU-OJ 上查看对应题目说明的 `run_kernel(...)` 接口;
|
||||
|
||||
3. 对照题包中的输入 shape、数据范围和精度要求;
|
||||
3. 对照题目说明的输入 shape、数据范围和精度要求,编写自己的 `run_kernel(...)`;
|
||||
|
||||
4. 编写自己的 `run_kernel(...)`;
|
||||
4. 提交 OJ,先保证正确性;
|
||||
|
||||
5. 提交 OJ,先通过正确性;
|
||||
|
||||
6. 正确性通过后,再对比 benchmark 耗时 / OJ 耗时继续优化。
|
||||
5. 正确性通过后,再对比 benchmark 耗时 / OJ 耗时继续优化。
|
||||
|
||||
**选择目标题目**
|
||||
|
||||
FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benchmark 脚本、OJ 题包、`run_kernel(...)` 接口和数据范围可能不同。
|
||||
FlashInfer 方向包含 **4 个可选算子题目**,均属于同一比赛通道,**FlashInfer 任务分** = $\max$\{各子题得分\}。每个对应独立的 benchmark 脚本、题目说明、`run_kernel(...)` 接口和数据范围。
|
||||
|
||||
| OJ 题号 | 算子类型 | 核心特点 | Benchmark 脚本 | FlashInfer API |
|
||||
| OJ 题号 | OJ 题目名称 | 核心特点 | Benchmark 脚本 | FlashInfer API |
|
||||
|---------|---------------|----------------|----------|----------|
|
||||
| **20001** | Ragged Prefill | GQA布局,Q/K/V平坦存储,causal=1 | `bench_batch_prefill_ragged.py` | `BatchPrefillWithRaggedKVCacheWrapper` |
|
||||
| **20002** | Paged Prefill | KV Cache分页存储,需解析page table | `bench_batch_prefill_paged.py` | `BatchPrefillWithPagedKVCacheWrapper` |
|
||||
| **20003** | MLA Paged Attention | DeepSeek MLA特有,双路Q(nope+pe)/双路Cache(ckv+kpe) |`bench_batch_mla.py` | `BatchMLAPagedAttentionWrapper` |
|
||||
| **20004** | Paged Decode | 每次只1个query token,memory-bound |`bench_batch_decode.py` | `BatchDecodeWithPagedKVCacheWrapper` |
|
||||
| **20001** | FlashInfer Ragged Prefill | GQA布局,Q/K/V平坦存储,causal=1 | `bench_batch_prefill_ragged.py` | `BatchPrefillWithRaggedKVCacheWrapper` |
|
||||
| **20002** | FlashInfer Paged Prefill | KV Cache分页存储,需解析page table | `bench_batch_prefill_paged.py` | `BatchPrefillWithPagedKVCacheWrapper` |
|
||||
| **20003** | FlashInfer MLA Paged Attention | DeepSeek MLA特有,双路Q(nope+pe)/双路Cache(ckv+kpe) |`bench_batch_mla.py` | `BatchMLAPagedAttentionWrapper` |
|
||||
| **20004** | FlashInfer Paged Decode | 每次只1个query token,memory-bound |`bench_batch_decode.py` | `BatchDecodeWithPagedKVCacheWrapper` |
|
||||
|
||||
**每个子题的接口参数、数据范围和精度要求以对应 XPU-OJ 题包为准。** 下文以题目 **20001 Flashinfer Ragged Prefill** 为例演示从 benchmark 到 XPU-OJ 提交的完整流程。
|
||||
**每个子题的接口参数、数据范围和精度要求以对应题目说明为准。** 下文以题目 **20001 Flashinfer Ragged Prefill** 为例演示从 benchmark 到 XPU-OJ 提交的完整流程。
|
||||
|
||||
题目的更多信息可参考 [*20001 Flashinfer Ragged Prefill 题目说明*](https://gitlink.org.cn/metax-maca/op_optimization/tree/master/%E5%9F%BA%E4%BA%8EAI%20Agent%E5%BC%80%E5%8F%91%E8%8C%83%E5%BC%8F%E7%9A%84%E5%9B%BD%E4%BA%A7GPU%E5%A4%A7%E6%A8%A1%E5%9E%8B%E6%8E%A8%E7%90%86%E7%AE%97%E5%AD%90%E5%BA%93%E4%BC%98%E5%8C%96%2Foperator_task_package%2Fflashinfer_task_package%2Fxpuoj_problem%2Fproblem_20001%2Fzh_CN)。
|
||||
|
||||
#### Step 6:理解 XPU-OJ 评测接口与精度要求
|
||||
**目标:** 明确 Benchmark 与最终评测提交之间的关系,理解选手需要实现的内容。
|
||||
|
||||
> 完成 benchmark 后,需要注意 benchmark 脚本主要用于建立性能基线,并不需要最终提交
|
||||
> 最终评测以 XPU-OJ 题包为准,评测程序会调用选手提交代码中的 `run_kernel`,并将输出结果与 OJ 参考实现结果进行比较
|
||||
> 最终评测以 XPU-OJ 为准,评测程序会调用选手提交代码中的 `run_kernel`,并将输出结果与 OJ 后台参考实现结果进行比较
|
||||
|
||||
下面以题目 **20001 FlashInfer Ragged Prefill** 为例,逐份解读 OJ 题包中的关键文件。
|
||||
下面以题目 **20001 FlashInfer Ragged Prefill** 为例,从本地题目文档 [*Agent 推理算子库优化 - FlashInfer Ragged Prefill*](https://gitlink.org.cn/metax-maca/op_optimization/tree/master/%E5%9F%BA%E4%BA%8EAI%20Agent%E5%BC%80%E5%8F%91%E8%8C%83%E5%BC%8F%E7%9A%84%E5%9B%BD%E4%BA%A7GPU%E5%A4%A7%E6%A8%A1%E5%9E%8B%E6%8E%A8%E7%90%86%E7%AE%97%E5%AD%90%E5%BA%93%E4%BC%98%E5%8C%96%2Foperator_task_package%2Fflashinfer_task_package%2Fxpuoj_problem%2Fproblem_20001%2FAgent%20%E6%8E%A8%E7%90%86%E7%AE%97%E5%AD%90%E5%BA%93%E4%BC%98%E5%8C%96%20-%20FlashInfer%20Ragged%20Prefill.md) 中逐节解读关键信息。
|
||||
|
||||
1. `00_题目描述.md` — 我要实现什么?
|
||||
1. `## 1. 题目描述` — 我要实现什么?
|
||||
|
||||
该文件明确了三个核心信息:
|
||||
该章节明确了三个核心信息:
|
||||
|
||||
1. **算子功能**:实现 FlashInfer Ragged KV Cache Prefill 的前向 CUDA C++ 算子。给定扁平存储的 Q / K / V,计算带 causal mask 的 scaled dot-product attention,结果写入 `output` 张量。
|
||||
|
||||
|
|
@ -505,7 +527,7 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
|
||||
你的 `run_kernel` 需要实现等效的注意力计算,最终输出与上述 FlashInfer API 在 `rtol = 1e-2, atol = 1e-2` 容差内一致。
|
||||
|
||||
2. `01_接口约定.md` — 我提交的函数签名是什么?
|
||||
2. `## 2. 接口约定` — 我提交的函数签名是什么?
|
||||
|
||||
以 CUDA C++ 为例,`run_kernel` 的精确 C 符号如下(函数名、参数类型、顺序、`const` 修饰均不可修改):
|
||||
|
||||
|
|
@ -555,81 +577,55 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
|
||||
- **GQA 头映射**:`kv_head = qo_head / (num_qo_heads / num_kv_heads) = qo_head / 8`。
|
||||
|
||||
- **Triton / TileLang 接口**:函数名同样是 `run_kernel`,参数顺序相同,数据类型映射遵循对应语言的沙箱规则(详见题包中的 `01_接口约定triton.md` 和 `01_接口约定tilelang.md`)。
|
||||
- **Triton / TileLang 接口**:函数名同样是 `run_kernel`,参数顺序相同,数据类型映射遵循对应语言的沙箱规则(详见题目文档中 `### 2.2 Triton` 和 `### 2.3 TileLang` 章节)。
|
||||
|
||||
3. `02_数据范围.md` — 输入规模有多大?
|
||||
3. `## 6. 数据范围与提示` — 输入规模有多大?
|
||||
|
||||
该文件定义了测试用例的参数组合和精度容差:
|
||||
该章节定义了测试用例的参数组合和精度容差:
|
||||
|
||||
- **固定参数**:`num_qo_heads = 32`、`num_kv_heads = 4`、`head_dim_qk = 128`、`head_dim_vo = 128`、`causal = 1`、数据类型 `bfloat16`
|
||||
- **可变参数**(共 **12 个测试用例**):
|
||||
|
||||
| batch_size | seq_len | 估算 Q 张量大小 | 估算 KV 张量大小 |
|
||||
|:---:|:---:|:---:|:---:|
|
||||
| 1 | 1024 | 1×1024×32×128×2B = 8 MB | 2×1×1024×4×128×2B ≈ 2 MB |
|
||||
| 1 | 4096 | 32 MB | 8 MB |
|
||||
| 1 | 8192 | 64 MB | 16 MB |
|
||||
| 1 | 16384 | 128 MB | 32 MB |
|
||||
| 4 | 1024~16384 | 32 MB ~ 512 MB | 8 MB ~ 128 MB |
|
||||
| 16 | 1024~16384 | 128 MB ~ 2 GB | 32 MB ~ 512 MB |
|
||||
- **可变参数**:batch_size 覆盖 {1, 2, 4, 15, 16, 27, 33},seq_len 上界覆盖 {1, 65, 123, 873, 987, 1024, 1280, 2048, 4096, 16384},共 **15 个测试用例**,覆盖等长长序列、变长 ragged、`q_len < kv_len`、短段和非 2 的幂长度。
|
||||
|
||||
- **精度要求**:`torch.allclose(output.float(), output_ref.float(), rtol = 1e-2, atol = 1e-2)`
|
||||
- **显存上限**:OJ 评测环境设计 `VRAM_SIZE = 48 GB`(见 `testcase_config.py`)
|
||||
- **精度要求**:`torch.allclose(output.float(), output_ref.float(), rtol = 1.6e-2, atol = 1.6e-2)`,且允许不超过 1% 的元素超差(匹配率需 ≥ 0.99)。
|
||||
|
||||
- **显存上限**:OJ 评测环境设计 `VRAM_SIZE = 48 GB`(OJ 后台配置)
|
||||
|
||||
> **优化提示**:`batch_size = 16, seq_len = 16384` 是最大 workload(Q ≈ 2 GB),需要特别关注显存使用和 compute 效率
|
||||
|
||||
4. `testcase_config.py` — OJ 后台如何评测我?
|
||||
|
||||
这个文件不是给选手阅读的文档,但理解它的逻辑有助于定位 OJ 提交错误:
|
||||
|
||||
1. `TESTCASES`:定义 12 组测试参数(`batch_size × seq_len` 组合),与 benchmark 脚本的参数顺序一致。
|
||||
2. `genTestCase()`:OJ 用固定随机种子生成测试张量,同一测试用例每次运行数据相同。
|
||||
3. `baseline()`:OJ 的参考实现——调用 FlashInfer Python API 得到 `output_ref`。
|
||||
4. `check()`:比较你的 `output` 与 `output_ref`。失败时返回 `max_abs_diff` 和 `mean_abs_diff` 帮助定位数值偏差。
|
||||
5. `getWorkload()`:计算该测试点的 FLOPs 和显存读写量,用于估算硬件理论下限 `T_h`(以此计算得分)。
|
||||
|
||||
当前题目 **20001 FlashInfer Ragged Prefill** 的校验方式为:
|
||||
```python
|
||||
torch.allclose(output_t.float(), output_ref.float(), rtol=1e-2, atol=1e-2)
|
||||
```
|
||||
说明:
|
||||
- `output_t` 是选手 `run_kernel(...)` 的输出;
|
||||
|
||||
- `output_ref` 是 OJ 题包参考实现生成的输出;
|
||||
|
||||
- `rtol / atol` 是容差。
|
||||
|
||||
选手实现的输出需要在上述容差范围内与 OJ 参考实现输出一致。不同算子的容差可能不同,正式精度要求以对应 OJ 题包说明为准。
|
||||
|
||||
#### Step 7:登录 XPU-OJ 并进入题目页面
|
||||
|
||||
使用组委会统一发放的账号登录 XPU-OJ,并进入对应赛题页面。
|
||||
使用组委会统一发放的账号登录 XPU-OJ,并在 XPU-OJ 上找到 **比赛 -> 沐曦 - 揭榜挂帅 - Agent 推理算子库优化 - FlashInfer 任务**,进入后可见 4 个题目。
|
||||
|
||||
1. 打开 [*XPU-OJ*](https://xpuoj.com/) 平台,使用组委会统一发放的账号和初始密码登录 **【后续发布】**;
|
||||
1. 打开 [*XPU-OJ*](https://xpuoj.com/) 平台,使用组委会统一发放的账号和初始密码登录;
|
||||
|
||||

|
||||
|
||||
2. 登录后进入比赛 / 题目列表页面;
|
||||
2. 登录后进入 **比赛** 列表,找到 **沐曦 - 揭榜挂帅 - Agent 推理算子库优化 - FlashInfer 任务**;
|
||||
|
||||

|
||||

|
||||
|
||||
3. 找到对应题目,例如 **20001 FlashInfer Ragged Prefill**;
|
||||
|
||||

|
||||
3. 找到对应题目,例如 **Agent 推理算子库优化 - FlashInfer Ragged Prefill(OJ 题号 20001)** ;
|
||||
|
||||

|
||||
|
||||
4. 点击进入题目详情页,查看题目描述、接口约定、数据范围和提交入口。
|
||||
|
||||

|
||||

|
||||
|
||||
#### Step 8:提交 OJ 冒烟代码
|
||||
**目标**:完成一次最小提交,确认 OJ 提交链路、语言环境和 `run_kernel(...)` 接口可用。
|
||||
|
||||
1. 在语言下拉框中选择本题支持的提交语言,例如 CUDA Maca、Triton 或 TileLang;
|
||||
|
||||
2. 借助 Agent 从题包生成 `run_kernel` 初版;
|
||||
2. 借助 Agent 阅读 XPU-OJ 的题目信息生成 `run_kernel` 初版;
|
||||
在下方参考 prompt 的引导下,Agent 会:
|
||||
1. 读取对应 OJ 题包中的接口约定文档(`01_接口约定.md`),提取 `run_kernel` 函数签名;
|
||||
2. 读取数据范围文档(`02_数据范围.md`),了解输入张量 shape 和精度要求;
|
||||
|
||||
1. 读取对应题目文档中的接口约定章节(`## 2. 接口约定`),提取 `run_kernel` 函数签名;
|
||||
|
||||
2. 读取数据范围章节(`## 6. 数据范围与提示`),了解输入张量 shape 和精度要求;
|
||||
|
||||
3. 生成一个能编译通过的最小 `run_kernel` 实现,优先保证接口正确性,不追求性能。
|
||||
|
||||
生成的代码可在本地编译验证后,直接提交到 OJ 上冒烟测试,确认提交链路可用。
|
||||
|
|
@ -641,7 +637,7 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
|
||||
- OJ 最终评测不会直接运行 benchmark 脚本,而是调用你提交代码中的 `run_kernel(...)`;
|
||||
|
||||
- 如果你还没有自己的 `run_kernel`,可以先让 Agent 阅读题包,并生成一个最小正确版实现思路;
|
||||
- 如果你还没有自己的 `run_kernel`,可以先让 Agent 阅读题目文档,并生成一个最小正确版实现思路;
|
||||
|
||||
- 本赛题鼓励参赛者使用 AI Agent 辅助完成代码阅读、接口理解、初版实现、错误定位和性能优化。
|
||||
|
||||
|
|
@ -663,7 +659,7 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
|
||||
- [*点击查看参考 Prompt*](#参考%20prompt)
|
||||
|
||||
- [*点击查看参考冒烟代码*](#20001%20flashinfer%20ragged%20prefill%20参考冒烟代码)
|
||||
- [*点击查看参考冒烟代码*](#参考冒烟代码)
|
||||
|
||||
更多 OpenCode 使用教程和 Agent 使用技巧可见
|
||||
|
||||
|
|
@ -675,11 +671,11 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
|
||||
4. 点击提交,等待评测结果返回;
|
||||
|
||||

|
||||

|
||||
|
||||
评测时间与题目测试点数量、队列状态和平台负载有关,通常需要等待**数十秒到数分钟**,以平台实际返回为准。
|
||||
评测时间与题目测试点数量、队列状态和平台负载有关,通常需要等待**数十秒到数分钟**,以平台实际运行状况为准。
|
||||
|
||||

|
||||

|
||||
|
||||
**OJ 评测流程**:
|
||||
|
||||
|
|
@ -691,7 +687,7 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
|
||||
4. 调用选手代码中的 `run_kernel(...)`;
|
||||
|
||||
5. 调用 `testcase_config.py` 中的 `baseline()` / 参考实现生成 `output_ref`;
|
||||
5. 调用 OJ 后台参考实现生成 `output_ref`;
|
||||
|
||||
6. 将 `run_kernel(...)` 的输出与 `output_ref` 做正确性校验;
|
||||
|
||||
|
|
@ -705,45 +701,46 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
|
||||
5. 查看结果
|
||||
|
||||
1. 单测试点分析 — 以 OJ 返回的一次评测为例
|
||||
1. 单测试点分析 — 以 OJ 返回的一次评测结果为例
|
||||
|
||||
提交后,OJ 平台对每个测试用例独立评测并返回结果。以下是一份优化后的真实评测输出(来自 20001 FlashInfer Ragged Prefill 第 1 个测试点):
|
||||
提交后,OJ 平台对每个测试用例独立评测并返回结果。以下是一份冒烟提交的典型评测输出(来自 20001 FlashInfer Ragged Prefill 第 1 个测试点):
|
||||
|
||||

|
||||

|
||||
|
||||
```plaintext
|
||||
Testcase #1
|
||||
测试点 #1
|
||||
Accepted
|
||||
112 pts
|
||||
125 ms
|
||||
1 pts
|
||||
119 ms
|
||||
22.0 G
|
||||
输入文件
|
||||
|
||||
1
|
||||
Your output
|
||||
你的输出
|
||||
|
||||
OJCHAL v1 1bHB14u2jFxvzumsIYHDPA==
|
||||
OJRESULT v1 f84db85c... eyJ0aW1lX21zIjoxMjQuNjY1LCJzcGVlZHVwIjoyLjgwMjY1NSwidGtfdGltZV9tcyI6MTI0LjY2NSwidGJfdGltZV9tcyI6MzQ5LjM5MywidGhfdGltZV9tcyI6MTQ2LjYwMTU1LCJzY29yZV9yYXRpbyI6MS4xMjEyOTQsInBhc3MiOnRydWV9
|
||||
OJCHAL v1 dQ0CZtDCX0JLxT1SF4h/Pw==
|
||||
OJRESULT v1 c6583f61291371b771de67188893f7d47a745beb2929edce17a67f44a0d80def eyJzY2hlbWFfdmVyc2lvbiI6MiwidGltZV9tcyI6MTE5LjI0Mywic3BlZWR1cCI6MC4wMTM0NTIsInRrX3RpbWVfbXMiOjExOS4yNDMsInRiX3RpbWVfbXMiOjEuNjA0LCJ0aF90aW1lX21zIjowLjM3MzMzNCwic2NvcmVfcmF0aW8iOjAuMDEwMjQ3LCJwYXNzIjp0cnVlfQ==
|
||||
你的标准错误输出
|
||||
|
||||
Checker message
|
||||
{"schema_version":2,"time_ms":119.243,"speedup":0.013452,"tk_time_ms":119.243,"tb_time_ms":1.604,"th_time_ms":0.373334,"score_ratio":0.010247,"pass":true}
|
||||
检查器信息
|
||||
|
||||
=== SPJ Report - FlashInfer Batch Prefill ===
|
||||
----------------------------------------------------------------
|
||||
Testcase #1
|
||||
Config: batch=16, seq_len=16384, q_heads=32, kv_heads=4,
|
||||
head_dim_qk=128, head_dim_vo=128, causal=1
|
||||
Config: sol016_trace_synthetic_b33_total16294: batch=33, total_q=16294, total_kv=16294, max_q=987, max_kv=987, q_heads=32, kv_heads=4, head_dim_qk=128, head_dim_vo=128, causal=1, source=sol-execbench 016 axes
|
||||
|
||||
Baseline: 349.393000 ms
|
||||
User kernel: 124.665000 ms
|
||||
Hardware bound: 146.601550 ms
|
||||
Speedup vs base: 2.803 x
|
||||
Baseline: 1.604000 ms
|
||||
User kernel: 119.243000 ms
|
||||
Speedup vs base: 0.013 x
|
||||
|
||||
Score ratio: 1.121294 (112.13%)
|
||||
Display score: 112 / 100
|
||||
Score ratio: 0.010247 (1.02%)
|
||||
Display score: 1 / 100
|
||||
Pass: OK
|
||||
----------------------------------------------------------------
|
||||
```
|
||||
|
||||
> 以上是**优化后**的结果(得分 112 分,加速比 2.8x),非冒烟测试预期。冒烟测试通常 `speedup < 1`,这是正常的起始点。另外 `OJCHAL` 和 `OJRESULT` 为平台元信息,参赛者无需关注,SPJ Report 中已包含所有评测指标的可读版本
|
||||
> 以上是一份冒烟提交的评测输出示例:`speedup = 0.013x`(远慢于 baseline),`Display score = 1`。 `OJCHAL` 和 `OJRESULT` 为平台元信息,参赛者无需关注,SPJ Report 中已包含所有评测指标的可读版本。
|
||||
|
||||
**OJ 输出中各项指标的含义:**
|
||||
|
||||
|
|
@ -752,16 +749,16 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
```plaintext
|
||||
Testcase #1
|
||||
Accepted ← 状态
|
||||
112 pts ← 显示得分
|
||||
125 ms ← kernel 耗时
|
||||
1 pts ← 显示得分
|
||||
119 ms ← kernel 耗时
|
||||
22.0 G ← 内存占用
|
||||
```
|
||||
|
||||
| 项目 | 含义 | 说明 |
|
||||
|------|------|------|
|
||||
| `Accepted` | 评测状态 | 通过正确性校验;若为 `Wrong Answer` 则未通过,不参与排名 |
|
||||
| `112 pts` | 显示得分 | 基于评分公式 + 对数压缩后的分数(满分参考值 100) |
|
||||
| `125 ms` | kernel 耗时 | 该测试点 OJ 测速阶段你的 kernel 平均执行时间 |
|
||||
| `Accepted` | 评测状态 | 通过正确性校验;若为 `Wrong Answer`、`Time Limit Exceeded` 则未通过,不参与排名 |
|
||||
| `1 pts` | 显示得分 | 基于评分公式 + 对数压缩后的分数。冒烟阶段通常极低,正常现象 |
|
||||
| `119 ms` | kernel 耗时 | 该测试点 OJ 测速阶段你的 kernel 平均执行时间 |
|
||||
| `22.0 G` | 内存占用 | CPU 侧 RSS,仅供平台监控进程占用、防止 OOM |
|
||||
|
||||
其次,`Checker message` 中的 SPJ Report 给出了详细的性能对比:
|
||||
|
|
@ -770,16 +767,15 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
=== SPJ Report - FlashInfer Batch Prefill ===
|
||||
----------------------------------------------------------------
|
||||
Testcase #1
|
||||
Config: batch=16, seq_len=16384, q_heads=32, kv_heads=4,
|
||||
head_dim_qk=128, head_dim_vo=128, causal=1
|
||||
Config: sol016_trace_synthetic_b33_total16294: batch=33, total_q=16294, total_kv=16294, max_q=987, max_kv=987,
|
||||
q_heads=32, kv_heads=4, head_dim_qk=128, head_dim_vo=128, causal=1
|
||||
|
||||
Baseline: 349.393000 ms
|
||||
User kernel: 124.665000 ms
|
||||
Hardware bound: 146.601550 ms
|
||||
Speedup vs base: 2.803 x
|
||||
Baseline: 1.604000 ms
|
||||
User kernel: 119.243000 ms
|
||||
Speedup vs base: 0.013 x
|
||||
|
||||
Score ratio: 1.121294 (112.13%)
|
||||
Display score: 112 / 100
|
||||
Score ratio: 0.010247 (1.02%)
|
||||
Display score: 1 / 100
|
||||
Pass: OK
|
||||
----------------------------------------------------------------
|
||||
```
|
||||
|
|
@ -788,50 +784,53 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
|
||||
| 字段 | 含义 | 本例值 | 解读 |
|
||||
|------|------|--------|------|
|
||||
| `Pass` | 正确性校验 | `OK` | 通过了 `allclose(rtol=1e-2, atol=1e-2)`,可参与排名 |
|
||||
| `User kernel` | 你的 kernel 耗时 (ms) | `124.67 ms` | **核心性能指标** |
|
||||
| `Baseline` | OJ 参考实现耗时 (ms) | `349.39 ms` | 你的基准对比对象 |
|
||||
| `Hardware bound` | 硬件理论下限 (ms) | `146.60 ms` | 基于 FLOPs / 带宽估算的理论最快时间 |
|
||||
| `Speedup vs base` | 加速比 | `2.80x` | `Baseline / User kernel`,`> 1` 表示优于 OJ 参考实现 |
|
||||
| `Score ratio` | 归一化得分(原始值) | `1.121` | 综合评分原始值 |
|
||||
| `Display score` | 显示得分 | `112` | 经过对数压缩后的得分,超过 100 表示超越硬件理论估算 |
|
||||
| `Config` | 测试配置 | — | 该测试点的 `batch`、`seq_len`、`q_heads`、`kv_heads` 等参数 |
|
||||
| `Config` | 测试配置 | — | 该测试点的 `batch`、`total_q`、`max_q`、`q_heads`、`kv_heads` 等参数 |
|
||||
| `Baseline` | OJ 参考实现耗时 (ms) | `1.60 ms` | 你的基准对比对象 |
|
||||
| `User kernel` | 你的 kernel 耗时 (ms) | `119.24 ms` | **核心性能指标** |
|
||||
| `Speedup vs base` | 加速比 | `0.013x` | `Baseline / User kernel`。`< 1` 表示比 baseline 慢 |
|
||||
| `Score ratio` | 归一化得分(原始值) | `0.0102` | 综合评分原始值 |
|
||||
| `Display score` | 显示得分 | `1` | OJ 内部评分,冒烟阶段通常极低 |
|
||||
| `Pass` | 正确性校验 | `OK` | 通过了正确性校验,可参与排名 |
|
||||
|
||||
**从 SPJ Report 分析优化方向:**
|
||||
|
||||
以上述结果为例:
|
||||
- `Speedup vs base = 2.80x`,已超越 OJ 参考实现 2.8 倍
|
||||
以上述冒烟结果为例:
|
||||
- `Speedup vs base = 0.013x`:**冒烟代码远慢于 OJ baseline**,这是正常的起始点——冒烟阶段的目的是验证接口和提交链路,不追求性能;
|
||||
|
||||
- `User kernel = 124.67ms` 对比 `Hardware bound = 146.60ms`:**你的 kernel 已超越硬件理论下限**,说明该测试点的实现已非常接近硬件极限,继续优化的收益空间有限
|
||||
|
||||
- `Score ratio = 1.121` 对应 `Display score = 112`,已超过 100 分的满分线
|
||||
- `Baseline = 1.60ms` 对比 `User kernel = 119.24ms`:两者之间有约 75 倍的性能差距,说明**优化空间巨大**。后续可以通过引入 tiling、shared memory、向量化访存等优化手段逐步缩小差距。
|
||||
|
||||
**优化优先级判断法:**
|
||||
|
||||
| 情况 | 优化空间 | 建议方向 |
|
||||
|------|----------|----------|
|
||||
| `User kernel ≈ Baseline` | 大 | 先保证正确性,再分析 compute / memory 瓶颈 |
|
||||
| `User kernel` 在 `Baseline` 和 `Hardware bound` 之间 | 中 | 使用 profiler 分析,优化瓶颈阶段 |
|
||||
| `User kernel ≈ Hardware bound` | 小 | 该测试点已接近最优,转而优化其他测试点 |
|
||||
| `User kernel ≫ Baseline`(speedup < 1) | 巨大 | 冒烟阶段正常状态。优先保证正确性,再引入 tiling / shared memory 等基础优化 |
|
||||
| `User kernel ≈ Baseline` | 大 | 已追平 baseline,进一步分析 compute / memory 瓶颈 |
|
||||
| `User kernel ≪ Baseline`(speedup > 1) | 中 | 已优于 baseline,使用 profiler 精调瓶颈阶段 |
|
||||
|
||||
**查看所有测试用例结果:**
|
||||
|
||||
OJ 平台每次提交会评测所有测试用例,每个测试用例返回一组独立的 SPJ Report。建议将各组结果整理成表格,方便跟踪优化进展:
|
||||
OJ 平台每次提交会评测所有测试用例,每个测试用例返回一组独立的 SPJ Report。每个子题独立计分,FlashInfer 任务总分取各子题得分的最高值。建议将各组结果整理成表格,方便跟踪优化进展:
|
||||
|
||||
| 测试点 | batch_size | seq_len | Baseline | User kernel | Speedup | Score | Pass |
|
||||
| 测试点 | batch | total_q | Baseline | User kernel | Speedup | Score | Pass |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| 1 | 1 | 1024 | — | — | — | — | — |
|
||||
| 2 | 1 | 4096 | — | — | — | — | — |
|
||||
| 1 | 33 | 16294 | 1.60 | 119.24 | 0.013 | 1 | ✓ |
|
||||
| 2 | 1 | 1024 | — | — | — | — | — |
|
||||
| ... | ... | ... | ... | ... | ... | ... | ... |
|
||||
| 12 | 16 | 16384 | 349.39 | 124.67 | 2.80 | 112.13 | ✓ |
|
||||
| 15 | 2 | 98 | — | — | — | — | — |
|
||||
|
||||
2. 总得分查看 — 评分公式详解
|
||||
2. XPU-OJ 平台评分机制(**仅供参考**)
|
||||
|
||||
OJ 平台对每次提交的所有测试点评分后,按以下规则计算总得分。
|
||||
以下为 XPU-OJ 平台的内部评分算法,用于生成榜单中每个题目的单题分数。请注意:
|
||||
|
||||
- **OJ 榜单分数 ≠ 比赛最终得分。** 比赛最终得分为 100 分制(详见 [*4.5 评分规则概要*](#45%20评分规则概要)),其中性能部分取 OJ 排名映射换算,满分 60 分;
|
||||
|
||||
- OJ 内部评分以各算子 baseline 为 50 分基准,硬件理论上限约 100 分。超过 100 分说明超越理论估算;
|
||||
|
||||
- 未通过正确性或稳定性测试的作品不参与排名,客观评测得分记为 0 分。
|
||||
|
||||
**单测试点评分公式**(参考 Sol-ExecBench):
|
||||
|
||||
$$S(T_k) = \frac{100}{1 + \left(\frac{1}{0.5} - 1\right) \cdot \frac{T_k - T_h}{T_b - T_h}}$$
|
||||
$S(T_k) = \frac{100}{1 + \left(\frac{1}{0.5} - 1\right) \cdot \frac{T_k - T_h}{T_b - T_h}}$
|
||||
|
||||
其中:
|
||||
- $T_k$:你的 kernel 平均时间
|
||||
|
|
@ -857,36 +856,19 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
|
||||
3. 榜单查看 — 如何阅读排行榜
|
||||
|
||||
排行榜位于 XPU-OJ 比赛页面,展示所有参赛者的各题得分和总分。
|
||||
[TODO: image]
|
||||
|
||||
以下为榜单示例:
|
||||
排行榜位于 **比赛 -> 沐曦 - 揭榜挂帅 - Agent 推理算子库优化 - FlashInfer 任务 -> 排行榜** 页面,展示各参赛者在每个题目的 OJ 内部得分。请注意:**OJ 榜单中各题的分数与比赛最终得分不是同一体系。** 比赛最终成绩中,性能提升效果仅取你得分最高的一个任务,按该任务的 OJ 排名换算(详见 [*4.5 评分规则概要*](#45%20评分规则概要))。
|
||||
|
||||
```plaintext
|
||||
My Rank: #4 My Total Score: 26.08
|
||||
|
||||
# User | Ragged Prefill | Paged Prefill | MLA | Paged Decode | Flashattn | Fused MoE | Total
|
||||
-----|---------------|----------------|---------------|------------|--------------|-----------|-----------|--------
|
||||
1 | muxitest006 | 0 | 0 | 84.75 | 0 | 0 | 0 | 84.75
|
||||
2 | ceerrep | 20.42 | 0 | 0 | 42.33 | 0 | 0 | 62.75
|
||||
3 | muxitest001 | 1.17 | 4.83 | 0.04 | 8.87 | 40.42 | 0 | 54.16
|
||||
4 | muxitest007 | 20.42 | 4.50 | 4.13 | 1.53 | 0 | 0 | 26.08
|
||||
5 | muxitest005 | 5.17 | 0 | 0 | 0 | 0 | 0 | 5.17
|
||||
...
|
||||
```
|
||||

|
||||
|
||||
**榜单解读**:
|
||||
|
||||
- 6 列对应 6 个 OJ 子题:Ragged Prefill (20001)、Paged Prefill (20002)、MLA Attention (20003)、Paged Decode (20004)、FlashAttention KV Cache Decode (20005)、Fused MoE i8 tn (20006)
|
||||
- FlashInfer 任务有 4 个子题,各子题独立计分,FlashInfer 任务得分取各子题的最高值;
|
||||
- 各题分数为 OJ 内部评分(OJ baseline = 50 分),排名按 Total(各题 OJ 得分之和)降序。此为 OJ 平台汇总逻辑,比赛最终只取单一最高分任务,不做多任务累加
|
||||
|
||||
- **Total = 6 个子题得分的总和**,排名按 Total 降序
|
||||
|
||||
- 每列下方的括号数值(如 `(48)`)表示该题提交次数
|
||||
- 每列下方的括号数值(如 `(24)`)表示该题提交次数
|
||||
|
||||
- `pass = false` 的提交不参与排名(对应 0 分)
|
||||
|
||||
**刷榜策略建议**:优先确保**每一题至少有一次 `pass = true` 的提交**(0 分 ≠ 未参与),然后逐个突破有性能瓶颈的题目,逐步提升各题得分和总分。
|
||||
|
||||
## 7. Agent 使用样例
|
||||
|
||||
**目标:** 在本模块中,你可以掌握使用 Agent 完成环境检查、运行 Benchmark、分析结果、理解 OJ 接口、生成和调试 `run_kernel` 等任务。以下是各个任务的参考 prompt。
|
||||
|
|
@ -912,15 +894,13 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
请帮我读取最新的性能基线 CSV 结果文件,分析各参数配置下的性能表现,找出带宽最高和 TFLOPs 最高的配置,并与沐曦 GPU 理论峰值带宽做对比。
|
||||
```
|
||||
|
||||
### 7.4 理解 OJ 题包接口
|
||||
### 7.4 理解 OJ 题目接口
|
||||
|
||||
``` plaintext
|
||||
请帮我阅读 FlashInfer Ragged Prefill 题包(problem_20001)中的以下文件:
|
||||
- zh_CN/00_题目描述.md
|
||||
- zh_CN/01_接口约定cuda.md
|
||||
- zh_CN/01_接口约定triton.md
|
||||
- zh_CN/01_接口约定tilelang.md
|
||||
- zh_CN/02_数据范围.md
|
||||
请帮我阅读 FlashInfer Ragged Prefill 题目文档(`problem_20001/Agent 推理算子库优化 - FlashInfer Ragged Prefill.md`):
|
||||
- `## 1. 题目描述`
|
||||
- `## 2. 接口约定`(含 CUDA / Triton / TileLang 三语言接口)
|
||||
- `## 6. 数据范围与提示`
|
||||
然后帮我总结:
|
||||
1. run_kernel 的函数签名和每个参数的含义
|
||||
2. 输入张量的形状约定(q/k/v 的 layout、indptr 的作用)
|
||||
|
|
@ -933,8 +913,8 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
|
||||
``` plaintext
|
||||
请帮我为题目 FlashInfer Ragged Prefill(OJ 题号 20001)生成一个最小可运行的 run_kernel 实现,要求:
|
||||
1. 阅读题包中的 01_接口约定.md,理解 run_kernel 的函数签名(参数类型、顺序、const 修饰);
|
||||
2. 阅读 02_数据范围.md,了解 head_dim_qk、head_dim_vo 的可能取值;
|
||||
1. 阅读题目文档中的 `## 2. 接口约定` 章节,理解 run_kernel 的函数签名(参数类型、顺序、const 修饰);
|
||||
2. 阅读 `## 6. 数据范围与提示` 章节,了解 head_dim_qk、head_dim_vo 的可能取值;
|
||||
3. 生成一个只使用简单双重循环的 naive 实现(不加 tiling、不加 shared memory),确保:
|
||||
- 函数签名为 extern "C" void run_kernel(...)
|
||||
- 包含必要的头文件(cuda_bf16.h、cuda_runtime.h、stdint.h、math.h)
|
||||
|
|
@ -948,8 +928,7 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
``` plaintext
|
||||
我的 run_kernel 提交到 OJ 后显示 Wrong Answer,请帮我对比以下信息:
|
||||
1. 这是我的 submit 代码:[粘贴你的 run_kernel 实现]
|
||||
2. 题包的接口约定在这里:[粘贴或引用 01_接口约定.md]
|
||||
3. 题包的测试配置在这里:[粘贴或引用 testcase_config.py]
|
||||
2. 题目文档的接口约定在这里:[粘贴或引用 `## 2. 接口约定` 章节]
|
||||
请帮我逐项检查:
|
||||
- 函数签名是否完全匹配
|
||||
- GQA 头映射公式是否正确
|
||||
|
|
@ -958,7 +937,18 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
- online softmax 的 m/l 更新逻辑是否正确
|
||||
```
|
||||
|
||||
### 7.7 问题排查
|
||||
### 7.7 调试 TLE 超时
|
||||
``` plaintext
|
||||
我的 run_kernel 提交到 OJ 后显示 Time Limit Exceeded,请帮我排查:
|
||||
1. 这是我的 submit 代码:[粘贴你的 run_kernel 实现]
|
||||
请帮我检查:
|
||||
- run_kernel 中是否调用了 cudaDeviceSynchronize()(应删除)
|
||||
- kernel 中 for 循环的终止条件是否有死循环风险
|
||||
- __syncthreads() 是否在条件分支内(应移到分支外)
|
||||
- grid/block 配置是否过大
|
||||
```
|
||||
|
||||
### 7.8 问题排查
|
||||
|
||||
``` plaintext
|
||||
运行 bench_batch_prefill_ragged.py 时报错 out of memory,请帮我分析原因并给出解决方案。
|
||||
|
|
@ -1016,9 +1006,9 @@ FlashInfer 方向包含 **4 个可选算子题目**,每个对应独立的 benc
|
|||
|
||||
| 问题 | 可能原因 | 解决办法 |
|
||||
|------|------|---------|
|
||||
| **Compilation Error** 编译错误 | 1. `run_kernel` 签名与 OJ 接口约定不一致(参数类型、顺序、数量不匹配);2. 缺少 `extern "C"` 声明导致 C++ name mangling;3. 缺少必要头文件(`cuda_bf16.h`、`cuda_runtime.h`、`math.h`);3. 使用了 OJ 环境不支持的语法或 API | 1. 逐行对照对应题包的「01_接口约定.md」,确认参数类型(`int64_t` vs `int`、`const` 修饰)、顺序完全一致;2. 在 `run_kernel` 前加 `extern "C"`;3. 确认文件顶部 include 了 `<cuda_bf16.h>`、`<cuda_runtime.h>`、`<stdint.h>`、`<math.h>`;4. 去掉 `printf`、`assert` 等调试代码后重新提交 |
|
||||
| **Compilation Error** 编译错误 | 1. `run_kernel` 签名与 OJ 接口约定不一致(参数类型、顺序、数量不匹配);2. 缺少 `extern "C"` 声明导致 C++ name mangling;3. 缺少必要头文件(`cuda_bf16.h`、`cuda_runtime.h`、`math.h`);3. 使用了 OJ 环境不支持的语法或 API | 1. 逐行对照对应题目文档的「## 2. 接口约定」章节,确认参数类型(`int64_t` vs `int`、`const` 修饰)、顺序完全一致;2. 在 `run_kernel` 前加 `extern "C"`;3. 确认文件顶部 include 了 `<cuda_bf16.h>`、`<cuda_runtime.h>`、`<stdint.h>`、`<math.h>`;4. 去掉 `printf`、`assert` 等调试代码后重新提交 |
|
||||
| **Time Limit Exceeded** 运行超时 | 1. `run_kernel` 内部调用了 `cudaDeviceSynchronize()` 导致额外等待;2. kernel 中存在死循环(for 循环边界条件错误);3. `__syncthreads()` 放在条件分支内导致线程死锁;4. grid 配置过大,启动的 block 数量远超合理范围 | 1. 删除 `run_kernel` 函数体内的 `cudaDeviceSynchronize()` 调用——评测器会在外部自行同步;2. 检查 kernel 中所有 for 循环的终止条件,确保 `kv_start <= block_max_q` 等边界正确;3. 将所有 `__syncthreads()` 移到 if/else 分支之外;4. 检查 grid 计算:`(seq_len + Br - 1) / Br`,确认 `Br` 取值合理 |
|
||||
| **Wrong Answer** 答案错误 | 1. 注意力计算公式错误(score、scale、softmax 实现有偏差);2. GQA 头映射错误:`hkv = hq / (num_qo_heads / num_kv_heads)` 计算不对;3. Causal mask 未正确实现(`causal=1` 时 query 看到了不该看的未来 token);4. Online softmax 的 m/l 更新逻辑有误;5. float4 向量化加载的偏移计算错误,导致 K/V 数据错位;6. 输出写入偏移错误,或对无效位置写了垃圾值 | 1. 本地用题包中的 PyTorch 参考实现对拍:运行 `testcase_config.py` 的 OJ 参考实现 `baseline()` 与你 kernel 输出做 `torch.allclose(rtol=1e-2, atol=1e-2)` 比对;2. GQA 公式:`int hkv = hq * num_kv_heads / num_qo_heads`(整数除法);3. Causal 逻辑:`kv_end = min(kv_start + Bc, q_idx + 1)`,注意 +1 的处理;4. 对照论文 FlashAttention 的 Algorithm 1 逐行验证 online softmax;5. float4 加载偏移公式:`(cur_kv_start + i) * num_kv_heads * D_QK + hkv * D_QK + d_idx * 8`,确认 `num_kv_heads` 而非 `num_qo_heads` |
|
||||
| **Wrong Answer** 答案错误 | 1. 注意力计算公式错误(score、scale、softmax 实现有偏差);2. GQA 头映射错误:`hkv = hq / (num_qo_heads / num_kv_heads)` 计算不对;3. Causal mask 未正确实现(`causal=1` 时 query 看到了不该看的未来 token);4. Online softmax 的 m/l 更新逻辑有误;5. float4 向量化加载的偏移计算错误,导致 K/V 数据错位;6. 输出写入偏移错误,或对无效位置写了垃圾值 | 1. 参考题目文档中的 `## 8. PyTorch 参考实现` 进行本地对拍:用 PyTorch 参考实现与你 kernel 输出做 `torch.allclose(rtol=1e-2, atol=1e-2)` 比对;2. GQA 公式:`int hkv = hq * num_kv_heads / num_qo_heads`(整数除法);3. Causal 逻辑:`kv_end = min(kv_start + Bc, q_idx + 1)`,注意 +1 的处理;4. 对照论文 FlashAttention 的 Algorithm 1 逐行验证 online softmax;5. float4 加载偏移公式:`(cur_kv_start + i) * num_kv_heads * D_QK + hkv * D_QK + d_idx * 8`,确认 `num_kv_heads` 而非 `num_qo_heads` |
|
||||
|
||||
|
||||
## 9. 下一步学习建议
|
||||
|
|
@ -1272,7 +1262,7 @@ Before output, confirm ALL items. Any failure = 0 points.
|
|||
|
||||
[*回退到 Step 8*](#step%208提交%20oj%20冒烟代码)
|
||||
|
||||
### 20001 FlashInfer Ragged Prefill 参考冒烟代码
|
||||
### 参考冒烟代码
|
||||
|
||||
[*回退到 Step 8*](#step%208提交%20oj%20冒烟代码)
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,360 @@
|
|||
# Agent 推理算子库优化 - FlashInfer Ragged Prefill
|
||||
|
||||
当前题目说明来源为 [*XPU-OJ 20001*](https://xpuoj.com/contest/2/problem/1),内容以 XPU-OJ 线上为准。
|
||||
|
||||
## 1. 题目描述
|
||||
你需要实现 FlashInfer ragged KV cache prefill 的CUDA C++前向算子。
|
||||
|
||||
本题输入采用 FlashInfer BatchPrefillWithRaggedKVCacheWrapper 的 ragged NHD 布局。每个 batch 段的 query/KV 长度由 qo_indptr 和 kv_indptr 给出;seq_len 只是所有段长度的上界,真实总长度分别是 qo_indptr[batch_size] 和 kv_indptr[batch_size]。
|
||||
|
||||
其中 query heads 采用 GQA 布局:num_qo_heads 个 query/output heads 共享 num_kv_heads 个 KV heads,G = num_qo_heads / num_kv_heads。
|
||||
|
||||
评测程序会调用你提交代码中的 run_kernel 函数。你需要根据 qo_indptr 和 kv_indptr 读取 ragged Q/K/V,并将结果写入 output。
|
||||
|
||||
baseline 使用 FlashInfer ragged prefill 的 Python API:
|
||||
|
||||
``` python
|
||||
wrapper = flashinfer.BatchPrefillWithRaggedKVCacheWrapper(workspace, kv_layout="NHD", backend="auto")
|
||||
wrapper.plan(qo_indptr, kv_indptr, num_qo_heads, num_kv_heads,
|
||||
head_dim_qk, head_dim_vo, causal=True,
|
||||
q_data_type=torch.bfloat16, kv_data_type=torch.bfloat16)
|
||||
wrapper.run(q, k, v, out=output)
|
||||
|
||||
```
|
||||
|
||||
如何提交代码详见 [*评测指南*](https://xpuoj.com/d/2)。
|
||||
|
||||
## 2. 接口约定
|
||||
|
||||
### 2.1 CUDA
|
||||
|
||||
你必须在提交的 CUDA 源码中提供如下 C 符号,函数名、参数类型、顺序必须完全一致,并使用 extern "C" 防止 name mangling:
|
||||
|
||||
``` cpp
|
||||
#include <stdint.h>
|
||||
#include <cuda_bf16.h>
|
||||
|
||||
extern "C" void run_kernel(
|
||||
const __nv_bfloat16* q,
|
||||
const __nv_bfloat16* k,
|
||||
const __nv_bfloat16* v,
|
||||
__nv_bfloat16* output,
|
||||
const int32_t* qo_indptr,
|
||||
const int32_t* kv_indptr,
|
||||
int64_t batch_size,
|
||||
int64_t seq_len,
|
||||
int64_t num_qo_heads,
|
||||
int64_t num_kv_heads,
|
||||
int64_t head_dim_qk,
|
||||
int64_t head_dim_vo,
|
||||
int64_t causal
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
- q:query tensor,shape (total_q, num_qo_heads, head_dim_qk),连续 bf16,其中 total_q = qo_indptr[batch_size]
|
||||
- k:key tensor,shape (total_kv, num_kv_heads, head_dim_qk),连续 bf16,其中 total_kv = kv_indptr[batch_size]
|
||||
- v:value tensor,shape (total_kv, num_kv_heads, head_dim_vo),连续 bf16
|
||||
- output:输出缓冲区,shape (total_q, num_qo_heads, head_dim_vo),连续 bf16
|
||||
- qo_indptr:query/output ragged indptr,shape (batch_size + 1),连续 int32
|
||||
- kv_indptr:KV ragged indptr,shape (batch_size + 1),连续 int32
|
||||
- seq_len:所有 query/KV 段长度的上界,可用于 launch grid;真实段长必须由 indptr 读取
|
||||
- causal:是否启用 causal mask,评测中固定为 1
|
||||
|
||||
部分测试点是等长段,但也包含 q_len != kv_len 和不同 batch 段长度不相等的 ragged 测试点。实现不能假设 qo_indptr[b + 1] - qo_indptr[b] == seq_len 或 kv_indptr[b + 1] - kv_indptr[b] == seq_len。
|
||||
|
||||
run_kernel 内部需要自行计算合适的 launch 配置并启动 CUDA kernel。为保证计时准确,不建议在 run_kernel 内部做 cudaDeviceSynchronize() 或显式同步。
|
||||
|
||||
### 2.2 Triton
|
||||
|
||||
你必须在提交的 Python 代码中提供 run_kernel 函数,函数名、参数顺序、类型必须完全一致:
|
||||
|
||||
``` python
|
||||
import triton
|
||||
import triton.language as tl
|
||||
|
||||
@triton.jit
|
||||
def your_kernel(...):
|
||||
...
|
||||
|
||||
def run_kernel(
|
||||
q, # Tensor[bf16], shape (total_q, num_qo_heads, head_dim_qk)
|
||||
k, # Tensor[bf16], shape (total_kv, num_kv_heads, head_dim_qk)
|
||||
v, # Tensor[bf16], shape (total_kv, num_kv_heads, head_dim_vo)
|
||||
output, # Tensor[bf16], shape (total_q, num_qo_heads, head_dim_vo)
|
||||
qo_indptr, # Tensor[int32], shape (batch_size + 1)
|
||||
kv_indptr, # Tensor[int32], shape (batch_size + 1)
|
||||
batch_size, # int64
|
||||
seq_len, # int64, max segment length bound
|
||||
num_qo_heads, # int64
|
||||
num_kv_heads, # int64
|
||||
head_dim_qk, # int64
|
||||
head_dim_vo, # int64
|
||||
causal, # int64
|
||||
):
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
- q/k/v:FlashInfer ragged prefill 输入 tensor,连续 bfloat16
|
||||
- output:输出缓冲区,连续 bfloat16,需要写入结果
|
||||
- qo_indptr/kv_indptr:ragged indptr,连续 int32;真实段长和 total_q/total_kv 以 indptr 为准
|
||||
- causal:是否启用 causal mask,评测中固定为 1
|
||||
|
||||
run_kernel 内部需要自行计算合适的 grid/block,并 launch 你实现的 Triton kernel。
|
||||
|
||||
### 2.3 TileLang
|
||||
|
||||
你必须在提交的 Python 代码中提供 run_kernel 函数,函数名、参数顺序、类型必须完全一致:
|
||||
|
||||
``` python
|
||||
import tilelang
|
||||
import tilelang.language as T
|
||||
from tilelang import jit
|
||||
|
||||
real_kernel = None
|
||||
|
||||
@jit
|
||||
def build_kernel(*args):
|
||||
@T.prim_func
|
||||
def kernel(*args):
|
||||
...
|
||||
return kernel
|
||||
|
||||
def run_kernel(
|
||||
q, # Tensor[bf16], shape (total_q, num_qo_heads, head_dim_qk)
|
||||
k, # Tensor[bf16], shape (total_kv, num_kv_heads, head_dim_qk)
|
||||
v, # Tensor[bf16], shape (total_kv, num_kv_heads, head_dim_vo)
|
||||
output, # Tensor[bf16], shape (total_q, num_qo_heads, head_dim_vo)
|
||||
qo_indptr, # Tensor[int32], shape (batch_size + 1)
|
||||
kv_indptr, # Tensor[int32], shape (batch_size + 1)
|
||||
batch_size, # int64
|
||||
seq_len, # int64, max segment length bound
|
||||
num_qo_heads, # int64
|
||||
num_kv_heads, # int64
|
||||
head_dim_qk, # int64
|
||||
head_dim_vo, # int64
|
||||
causal, # int64
|
||||
):
|
||||
global real_kernel
|
||||
if real_kernel is None:
|
||||
real_kernel = build_kernel(...)
|
||||
real_kernel(q, k, v, output, qo_indptr, kv_indptr,
|
||||
batch_size, seq_len, num_qo_heads, num_kv_heads,
|
||||
head_dim_qk, head_dim_vo, causal)
|
||||
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
- q/k/v:FlashInfer ragged prefill 输入 tensor,连续 bfloat16
|
||||
- output:输出缓冲区,连续 bfloat16,需要写入结果
|
||||
- qo_indptr/kv_indptr:ragged indptr,连续 int32;真实段长和 total_q/total_kv 以 indptr 为准
|
||||
- causal:是否启用 causal mask,评测中固定为 1
|
||||
|
||||
run_kernel 内部需要自行计算合适的 grid/block,并 launch 你实现的 TileLang kernel。
|
||||
|
||||
## 3. 输入格式
|
||||
|
||||
本题输入由评测程序在 GPU 上构造,并按接口约定中的顺序传入 run_kernel。
|
||||
|
||||
所有 q/k/v/output 均为连续 torch.bfloat16 CUDA tensor,qo_indptr/kv_indptr 为连续 torch.int32 CUDA tensor。
|
||||
|
||||
张量布局固定为 FlashInfer ragged prefill 的 NHD 布局。
|
||||
|
||||
## 4. 输出格式
|
||||
|
||||
输出写入 output,shape 为 (total_q, num_qo_heads, head_dim_vo),类型为 bfloat16,其中 total_q = qo_indptr[batch_size]。
|
||||
|
||||
## 5. 样例
|
||||
|
||||
若 batch_size = 1、seq_len = 4、num_qo_heads = 1、num_kv_heads = 1,则:
|
||||
|
||||
```
|
||||
qo_indptr = [0, 4]
|
||||
kv_indptr = [0, 4]
|
||||
```
|
||||
|
||||
第 t 个 query 会访问同一 batch 内的 KV token 前缀;启用 causal mask 时,只能看到位置不超过 t 的 token。例如 t = 2 时:
|
||||
|
||||
```
|
||||
attention = softmax(q[2, 0, :] @ k[0:3, 0, :].T / sqrt(head_dim_qk))
|
||||
output[2, 0, :] = attention @ v[0:3, 0, :]
|
||||
```
|
||||
|
||||
若某个 varlen case 中 q_len=2、kv_len=4,则 causal mask 采用 FlashInfer/sol-execbench 的 bottom-right 对齐:第 t 个 query 可见的 KV 上界为 t + 1 + (kv_len - q_len)。例如 t=0 时可见 k[0:3],t=1 时可见 k[0:4]。
|
||||
|
||||
## 6. 数据范围与提示
|
||||
|
||||
- 数据类型:q/k/v/output 均为 bfloat16
|
||||
- KV layout:NHD
|
||||
- num_qo_heads = 32
|
||||
- num_kv_heads = 4
|
||||
- causal = 1
|
||||
- head_dim_qk, head_dim_vo 取值为 (128, 128)
|
||||
- batch_size 取值随测试点变化,覆盖 1, 2, 4, 15, 16, 27, 33
|
||||
- seq_len 参数表示所有 query/KV 段长度的上界,各测试点的段长上界覆盖 1, 65, 123, 873, 987, 1024, 1280, 2048, 4096, 16384(变长测试点内部还包含 512、640 等更短的真实段长)
|
||||
- total_q = qo_indptr[batch_size]
|
||||
- total_kv = kv_indptr[batch_size]
|
||||
|
||||
注意:
|
||||
|
||||
- G = num_qo_heads / num_kv_heads,同一个 KV head 服务连续的 G 个 query heads。
|
||||
- 对 query head h_q,对应的 KV head 为 h_q / G。
|
||||
- 真实段长必须从 qo_indptr 和 kv_indptr 读取,不能假设每段长度相同。
|
||||
- 启用 causal mask 后,采用 bottom-right 对齐。若当前段 q_len != kv_len,第 t 个 query 可访问的位置满足 kv_pos < t + 1 + (kv_len - q_len)。
|
||||
- 输出校验容差为 rtol=1.6e-2, atol=1.6e-2,且允许不超过 1% 的元素超差(匹配率需 ≥ 0.99)。
|
||||
- 被容忍的超差元素其绝对误差仍不得超过 8 × (atol + rtol · |ref|),避免个别段被整段算错而蒙混通过。
|
||||
- 单 token 边界(用例 14)和非 2 的幂尾段(用例 15)为小规模确定性用例,要求逐元素通过(匹配率需 = 1.0)。
|
||||
- q/k/v 使用标准正态分布生成,避免均匀正输入导致长序列 softmax 退化成近似 prefix mean。
|
||||
|
||||
## 7. 测试用例尺寸
|
||||
|
||||
测试点顺序与 testcase_config.py 的 TESTCASES 一致。共 15 个测试点,全部 head_dim_qk = head_dim_vo = 128,覆盖等长长序列、变长 ragged、q_len < kv_len、短段和非 2 的幂长度。
|
||||
|
||||
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse; width:100%;">
|
||||
<thead>
|
||||
<tr style="text-align:center; vertical-align:middle;">
|
||||
<th style="padding:6px 10px;">测试用例ID</th>
|
||||
<th>类型</th>
|
||||
<th>batch</th>
|
||||
<th>total_q</th>
|
||||
<th>total_kv</th>
|
||||
<th>max_q</th>
|
||||
<th>max_kv</th>
|
||||
<th>heads</th>
|
||||
<th>head_dim</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>混合 ragged 长序列</td>
|
||||
<td>33</td>
|
||||
<td colspan="2">16294</td>
|
||||
<td colspan="2">987</td>
|
||||
<td rowspan="15">32/4</td>
|
||||
<td rowspan="15">128/128</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td rowspan="7">等长序列</td>
|
||||
<td rowspan="3">1</td>
|
||||
<td colspan="4">1024</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td colspan="4">4096</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td colspan="4">16384</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td rowspan="2">4</td>
|
||||
<td colspan="2">4096</td>
|
||||
<td colspan="2">1024</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>6</td>
|
||||
<td rowspan="2" colspan="2">16384</td>
|
||||
<td colspan="2">4096</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>7</td>
|
||||
<td rowspan="2">16</td>
|
||||
<td colspan="2">1024</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8</td>
|
||||
<td colspan="2">32768</td>
|
||||
<td colspan="2">2048</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>9</td>
|
||||
<td>变长 <code>q_len < kv_len</code></td>
|
||||
<td rowspan="2">4</td>
|
||||
<td>2048</td>
|
||||
<td>4096</td>
|
||||
<td>512</td>
|
||||
<td>1024</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>10</td>
|
||||
<td>混合变长 <code>q_len < kv_len</code></td>
|
||||
<td>1536</td>
|
||||
<td>3584</td>
|
||||
<td>640</td>
|
||||
<td>1280</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>11</td>
|
||||
<td>双段变长 <code>q_len < kv_len</code></td>
|
||||
<td>2</td>
|
||||
<td>1024</td>
|
||||
<td>3072</td>
|
||||
<td>512</td>
|
||||
<td>2048</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>12</td>
|
||||
<td>混合 ragged 中长序列</td>
|
||||
<td>27</td>
|
||||
<td colspan="2">12251</td>
|
||||
<td colspan="2">873</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>13</td>
|
||||
<td>混合 ragged 短序列</td>
|
||||
<td>15</td>
|
||||
<td colspan="2">969</td>
|
||||
<td colspan="2">123</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>14</td>
|
||||
<td>单 token 边界</td>
|
||||
<td colspan="5">1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>15</td>
|
||||
<td>非 2 的幂尾段</td>
|
||||
<td>2</td>
|
||||
<td colspan="2">98</td>
|
||||
<td colspan="2">65</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
说明:变长测试点的真实段长由 qo_indptr 和 kv_indptr 给出;参赛实现应始终以 indptr 为准,而不是从 seq_len、total_q 或 total_kv 反推出每段长度。
|
||||
|
||||
## 8. PyTorch 参考实现
|
||||
|
||||
``` python
|
||||
def baseline(q, k, v, output, qo_indptr, kv_indptr,
|
||||
batch_size, seq_len, num_qo_heads, num_kv_heads,
|
||||
head_dim_qk, head_dim_vo, causal):
|
||||
workspace_buffer = torch.empty(128 * 1024 * 1024, dtype=torch.uint8, device=q.device)
|
||||
wrapper = flashinfer.BatchPrefillWithRaggedKVCacheWrapper(
|
||||
workspace_buffer,
|
||||
kv_layout="NHD",
|
||||
backend="auto",
|
||||
)
|
||||
wrapper.plan(
|
||||
qo_indptr,
|
||||
kv_indptr,
|
||||
num_qo_heads,
|
||||
num_kv_heads,
|
||||
head_dim_qk,
|
||||
head_dim_vo,
|
||||
causal=bool(causal),
|
||||
q_data_type=torch.bfloat16,
|
||||
kv_data_type=torch.bfloat16,
|
||||
)
|
||||
wrapper.run(q, k, v, out=output)
|
||||
|
||||
```
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
# Agent 推理算子库优化 - FlashInfer Paged Prefill
|
||||
|
||||
当前题目说明来源为 [*XPU-OJ 20002*](https://xpuoj.com/contest/2/problem/2),内容以 XPU-OJ 线上为准。
|
||||
|
||||
## 1. 题目描述
|
||||
你需要实现 FlashInfer paged KV cache prefill 的CUDA C++前向算子。
|
||||
|
||||
本题输入采用 FlashInfer BatchPrefillWithPagedKVCacheWrapper 的 paged NHD 布局。每个 batch 中有 seq_len 个 query token,KV cache 也有 seq_len 个 token,并按 page 存储。
|
||||
|
||||
评测程序会调用你提交代码中的 run_kernel 函数。你需要根据 qo_indptr、kv_indptr、kv_indices 和 last_page_len 读取 paged KV cache,并将结果写入 output。
|
||||
|
||||
baseline 使用 FlashInfer paged prefill 的 Python API:
|
||||
|
||||
``` python
|
||||
wrapper = flashinfer.BatchPrefillWithPagedKVCacheWrapper(workspace, kv_layout="NHD", backend="auto")
|
||||
wrapper.plan(qo_indptr, kv_indptr, kv_indices, last_page_len,
|
||||
num_qo_heads, num_kv_heads, head_dim, page_block_size,
|
||||
causal=bool(causal),
|
||||
q_data_type=torch.bfloat16, kv_data_type=torch.bfloat16)
|
||||
wrapper.run(q, kv_data, out=output)
|
||||
|
||||
```
|
||||
|
||||
如何提交代码详见 [*评测指南*](https://xpuoj.com/d/2)。
|
||||
|
||||
## 2. 接口约定
|
||||
|
||||
### 2.1 CUDA
|
||||
|
||||
你必须在提交的 CUDA 源码中提供如下 C 符号,函数名、参数类型、顺序必须完全一致,并使用 extern "C" 防止 name mangling:
|
||||
|
||||
``` cpp
|
||||
#include <stdint.h>
|
||||
#include <cuda_bf16.h>
|
||||
|
||||
extern "C" void run_kernel(
|
||||
const __nv_bfloat16* q,
|
||||
const __nv_bfloat16* kv_data,
|
||||
__nv_bfloat16* output,
|
||||
const int32_t* qo_indptr,
|
||||
const int32_t* kv_indptr,
|
||||
const int32_t* kv_indices,
|
||||
const int32_t* last_page_len,
|
||||
int64_t batch_size,
|
||||
int64_t seq_len,
|
||||
int64_t num_qo_heads,
|
||||
int64_t num_kv_heads,
|
||||
int64_t head_dim,
|
||||
int64_t page_block_size,
|
||||
int64_t causal
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
- q:query tensor,shape (batch_size * seq_len, num_qo_heads, head_dim),连续 bf16
|
||||
- kv_data:paged KV cache,shape (num_blocks, 2, page_block_size, num_kv_heads, head_dim),连续 bf16,其中 kv_data[:, 0] 为 key,kv_data[:, 1] 为 value
|
||||
- output:输出缓冲区,shape (batch_size * seq_len, num_qo_heads, head_dim),连续 bf16
|
||||
- qo_indptr:query/output indptr,shape (batch_size + 1),连续 int32
|
||||
- kv_indptr:paged KV indptr,shape (batch_size + 1),连续 int32
|
||||
- kv_indices:page index,shape (num_blocks),连续 int32
|
||||
- last_page_len:每个 batch 最后一个 page 的有效 token 数,shape (batch_size),连续 int32
|
||||
- page_block_size:page size,评测中固定为 16
|
||||
- causal:是否启用 causal mask,本题按 benchmark case 固定为 0
|
||||
|
||||
run_kernel 内部需要自行计算合适的 launch 配置并启动 CUDA kernel。为保证计时准确,不建议在 run_kernel 内部做 cudaDeviceSynchronize() 或显式同步。
|
||||
|
||||
### 2.2 Triton
|
||||
|
||||
你必须在提交的 Python 代码中提供 run_kernel 函数,函数名、参数顺序、类型必须完全一致:
|
||||
|
||||
``` python
|
||||
import triton
|
||||
import triton.language as tl
|
||||
|
||||
@triton.jit
|
||||
def your_kernel(...):
|
||||
...
|
||||
|
||||
def run_kernel(
|
||||
q, # Tensor[bf16], shape (batch_size * seq_len, num_qo_heads, head_dim)
|
||||
kv_data, # Tensor[bf16], shape (num_blocks, 2, page_block_size, num_kv_heads, head_dim)
|
||||
output, # Tensor[bf16], shape (batch_size * seq_len, num_qo_heads, head_dim)
|
||||
qo_indptr, # Tensor[int32], shape (batch_size + 1)
|
||||
kv_indptr, # Tensor[int32], shape (batch_size + 1)
|
||||
kv_indices, # Tensor[int32], shape (num_blocks)
|
||||
last_page_len, # Tensor[int32], shape (batch_size)
|
||||
batch_size, # int64
|
||||
seq_len, # int64
|
||||
num_qo_heads, # int64
|
||||
num_kv_heads, # int64
|
||||
head_dim, # int64
|
||||
page_block_size, # int64
|
||||
causal, # int64
|
||||
):
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
- q:query tensor,连续 bfloat16
|
||||
- kv_data:paged KV cache,连续 bfloat16
|
||||
- output:输出缓冲区,连续 bfloat16,需要写入结果
|
||||
- qo_indptr/kv_indptr/kv_indices/last_page_len:paged KV metadata,连续 int32
|
||||
- page_block_size:评测中固定为 16
|
||||
- causal:评测中固定为 0
|
||||
|
||||
run_kernel 内部需要自行计算合适的 grid/block,并 launch 你实现的 Triton kernel。
|
||||
|
||||
### 2.3 TileLang
|
||||
|
||||
你必须在提交的 Python 代码中提供 run_kernel 函数,函数名、参数顺序、类型必须完全一致:
|
||||
|
||||
``` python
|
||||
import tilelang
|
||||
import tilelang.language as T
|
||||
from tilelang import jit
|
||||
|
||||
real_kernel = None
|
||||
|
||||
@jit
|
||||
def build_kernel(*args):
|
||||
@T.prim_func
|
||||
def kernel(*args):
|
||||
...
|
||||
return kernel
|
||||
|
||||
def run_kernel(
|
||||
q, # Tensor[bf16], shape (batch_size * seq_len, num_qo_heads, head_dim)
|
||||
kv_data, # Tensor[bf16], shape (num_blocks, 2, page_block_size, num_kv_heads, head_dim)
|
||||
output, # Tensor[bf16], shape (batch_size * seq_len, num_qo_heads, head_dim)
|
||||
qo_indptr, # Tensor[int32], shape (batch_size + 1)
|
||||
kv_indptr, # Tensor[int32], shape (batch_size + 1)
|
||||
kv_indices, # Tensor[int32], shape (num_blocks)
|
||||
last_page_len, # Tensor[int32], shape (batch_size)
|
||||
batch_size, # int64
|
||||
seq_len, # int64
|
||||
num_qo_heads, # int64
|
||||
num_kv_heads, # int64
|
||||
head_dim, # int64
|
||||
page_block_size, # int64
|
||||
causal, # int64
|
||||
):
|
||||
global real_kernel
|
||||
if real_kernel is None:
|
||||
real_kernel = build_kernel(...)
|
||||
real_kernel(q, kv_data, output, qo_indptr, kv_indptr, kv_indices, last_page_len,
|
||||
batch_size, seq_len, num_qo_heads, num_kv_heads,
|
||||
head_dim, page_block_size, causal)
|
||||
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
- q:query tensor,连续 bfloat16
|
||||
- kv_data:paged KV cache,连续 bfloat16
|
||||
- output:输出缓冲区,连续 bfloat16,需要写入结果
|
||||
- qo_indptr/kv_indptr/kv_indices/last_page_len:paged KV metadata,连续 int32
|
||||
- page_block_size:评测中固定为 16
|
||||
- causal:评测中固定为 0
|
||||
|
||||
run_kernel 内部需要自行计算合适的 grid/block,并 launch 你实现的 TileLang kernel。
|
||||
|
||||
## 3. 输入格式
|
||||
|
||||
本题输入由评测程序在 GPU 上构造,并按接口约定中的顺序传入 run_kernel。
|
||||
|
||||
q/kv_data/output 均为连续 torch.bfloat16 CUDA tensor,qo_indptr/kv_indptr/kv_indices/last_page_len 均为连续 torch.int32 CUDA tensor。
|
||||
|
||||
KV layout 固定为 FlashInfer paged prefill 的 NHD 布局,page size 固定为 16。
|
||||
|
||||
## 4. 输出格式
|
||||
|
||||
输出写入 output,shape 为 (batch_size * seq_len, num_qo_heads, head_dim),类型为 bfloat16。
|
||||
|
||||
## 5. 样例
|
||||
|
||||
若 batch_size = 1、seq_len = 32、page_block_size = 16,则:
|
||||
|
||||
```
|
||||
qo_indptr = [0, 32]
|
||||
kv_indptr = [0, 2]
|
||||
kv_indices = [0, 1]
|
||||
last_page_len = [16]
|
||||
```
|
||||
|
||||
第 0 个 batch 的 KV token 存放在 page 0 和 page 1 中,每个 page 有 16 个 token。
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
# Agent 推理算子库优化 - FlashInfer MLA Paged Attention
|
||||
|
||||
当前题目说明来源为 [*XPU-OJ 20003*](https://xpuoj.com/contest/2/problem/3),内容以 XPU-OJ 线上为准。
|
||||
|
||||
## 1. 题目描述
|
||||
你需要实现 FlashInfer MLA paged attention 的 CUDA C++ 前向算子。
|
||||
|
||||
本题使用 BatchMLAPagedAttentionWrapper 的 DeepSeek MLA decode 配置:q_nope 表示不含 RoPE 的 query,q_pe 表示 RoPE 部分的 query,ckv 与 kpe 分别表示 compressed KV cache 与 RoPE KV cache。MLA page size 固定为 1。
|
||||
|
||||
评测程序会调用你提交代码中的 run_kernel 函数。你需要根据 q_indptr、kv_indptr、kv_indices 和 kv_lens 读取 cache,并将结果写入 output。
|
||||
|
||||
baseline 使用 FlashInfer MLA 的 Python API:
|
||||
|
||||
``` python
|
||||
wrapper = flashinfer.mla.BatchMLAPagedAttentionWrapper(workspace, backend="auto")
|
||||
wrapper.plan(q_indptr, kv_indptr, kv_indices, kv_lens,
|
||||
num_heads, head_dim_ckv, head_dim_kpe,
|
||||
page_size, False, sm_scale,
|
||||
q_nope.dtype, ckv.dtype)
|
||||
wrapper.run(q_nope, q_pe, ckv, kpe, out=output, return_lse=False)
|
||||
|
||||
```
|
||||
|
||||
如何提交代码详见 [*评测指南*](https://xpuoj.com/d/2)。
|
||||
|
||||
## 2. 接口约定
|
||||
|
||||
### 2.1 CUDA
|
||||
|
||||
你必须在提交的 CUDA 源码中提供如下 C 符号,函数名、参数类型、顺序必须完全一致,并使用 extern "C" 防止 name mangling:
|
||||
|
||||
``` cpp
|
||||
#include <stdint.h>
|
||||
#include <cuda_bf16.h>
|
||||
|
||||
extern "C" void run_kernel(
|
||||
const __nv_bfloat16* q_nope,
|
||||
const __nv_bfloat16* q_pe,
|
||||
const __nv_bfloat16* ckv,
|
||||
const __nv_bfloat16* kpe,
|
||||
__nv_bfloat16* output,
|
||||
const int32_t* q_indptr,
|
||||
const int32_t* kv_indptr,
|
||||
const int32_t* kv_indices,
|
||||
const int32_t* kv_lens,
|
||||
int64_t batch_size,
|
||||
int64_t seq_len,
|
||||
int64_t num_heads,
|
||||
int64_t head_dim_ckv,
|
||||
int64_t head_dim_kpe,
|
||||
int64_t page_size,
|
||||
int64_t causal
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
- q_nope:query 的 compressed/nope 部分,shape (batch_size, num_heads, head_dim_ckv),连续 bf16
|
||||
- q_pe:query 的 RoPE 部分,shape (batch_size, num_heads, head_dim_kpe),连续 bf16
|
||||
- ckv:compressed KV cache,shape (batch_size * seq_len, 1, head_dim_ckv),连续 bf16
|
||||
- kpe:RoPE KV cache,shape (batch_size * seq_len, 1, head_dim_kpe),连续 bf16
|
||||
- output:输出缓冲区,shape (batch_size, num_heads, head_dim_ckv),连续 bf16
|
||||
- q_indptr:decode query indptr,shape (batch_size + 1),内容为 [0, 1, ..., batch_size]
|
||||
- kv_indptr:KV indptr,shape (batch_size + 1),每段长度为 seq_len
|
||||
- kv_indices:page index,shape (batch_size * seq_len),连续 int32
|
||||
- kv_lens:每个 batch 的 KV 长度,shape (batch_size),连续 int32
|
||||
- page_size:评测中固定为 1
|
||||
- causal:评测中固定为 0
|
||||
|
||||
run_kernel 内部需要自行计算合适的 launch 配置并启动 CUDA kernel。为保证计时准确,不建议在 run_kernel 内部做 cudaDeviceSynchronize() 或显式同步。
|
||||
|
||||
### 2.2 Triton
|
||||
|
||||
你必须在提交的 Python 代码中提供 run_kernel 函数,函数名、参数顺序、类型必须完全一致:
|
||||
|
||||
``` python
|
||||
import triton
|
||||
import triton.language as tl
|
||||
|
||||
@triton.jit
|
||||
def your_kernel(...):
|
||||
...
|
||||
|
||||
def run_kernel(
|
||||
q_nope, # Tensor[bf16], shape (batch_size, num_heads, head_dim_ckv)
|
||||
q_pe, # Tensor[bf16], shape (batch_size, num_heads, head_dim_kpe)
|
||||
ckv, # Tensor[bf16], shape (batch_size * seq_len, 1, head_dim_ckv)
|
||||
kpe, # Tensor[bf16], shape (batch_size * seq_len, 1, head_dim_kpe)
|
||||
output, # Tensor[bf16], shape (batch_size, num_heads, head_dim_ckv)
|
||||
q_indptr, # Tensor[int32], shape (batch_size + 1)
|
||||
kv_indptr, # Tensor[int32], shape (batch_size + 1)
|
||||
kv_indices, # Tensor[int32], shape (batch_size * seq_len)
|
||||
kv_lens, # Tensor[int32], shape (batch_size)
|
||||
batch_size, # int64
|
||||
seq_len, # int64
|
||||
num_heads, # int64
|
||||
head_dim_ckv, # int64
|
||||
head_dim_kpe, # int64
|
||||
page_size, # int64
|
||||
causal, # int64
|
||||
):
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
- q_nope/q_pe/ckv/kpe:MLA attention 输入 tensor,连续 bfloat16
|
||||
- output:输出缓冲区,连续 bfloat16,需要写入结果
|
||||
- q_indptr/kv_indptr/kv_indices/kv_lens:paged attention metadata,连续 int32
|
||||
- page_size:评测中固定为 1
|
||||
- causal:评测中固定为 0
|
||||
|
||||
run_kernel 内部需要自行计算合适的 grid/block,并 launch 你实现的 Triton kernel。
|
||||
|
||||
### 2.3 TileLang
|
||||
|
||||
你必须在提交的 Python 代码中提供 run_kernel 函数,函数名、参数顺序、类型必须完全一致:
|
||||
|
||||
``` python
|
||||
import tilelang
|
||||
import tilelang.language as T
|
||||
from tilelang import jit
|
||||
|
||||
real_kernel = None
|
||||
|
||||
@jit
|
||||
def build_kernel(*args):
|
||||
@T.prim_func
|
||||
def kernel(*args):
|
||||
...
|
||||
return kernel
|
||||
|
||||
def run_kernel(
|
||||
q_nope, # Tensor[bf16], shape (batch_size, num_heads, head_dim_ckv)
|
||||
q_pe, # Tensor[bf16], shape (batch_size, num_heads, head_dim_kpe)
|
||||
ckv, # Tensor[bf16], shape (batch_size * seq_len, 1, head_dim_ckv)
|
||||
kpe, # Tensor[bf16], shape (batch_size * seq_len, 1, head_dim_kpe)
|
||||
output, # Tensor[bf16], shape (batch_size, num_heads, head_dim_ckv)
|
||||
q_indptr, # Tensor[int32], shape (batch_size + 1)
|
||||
kv_indptr, # Tensor[int32], shape (batch_size + 1)
|
||||
kv_indices, # Tensor[int32], shape (batch_size * seq_len)
|
||||
kv_lens, # Tensor[int32], shape (batch_size)
|
||||
batch_size, # int64
|
||||
seq_len, # int64
|
||||
num_heads, # int64
|
||||
head_dim_ckv, # int64
|
||||
head_dim_kpe, # int64
|
||||
page_size, # int64
|
||||
causal, # int64
|
||||
):
|
||||
global real_kernel
|
||||
if real_kernel is None:
|
||||
real_kernel = build_kernel(...)
|
||||
real_kernel(q_nope, q_pe, ckv, kpe, output,
|
||||
q_indptr, kv_indptr, kv_indices, kv_lens,
|
||||
batch_size, seq_len, num_heads,
|
||||
head_dim_ckv, head_dim_kpe, page_size, causal)
|
||||
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
- q_nope/q_pe/ckv/kpe:MLA attention 输入 tensor,连续 bfloat16
|
||||
- output:输出缓冲区,连续 bfloat16,需要写入结果
|
||||
- q_indptr/kv_indptr/kv_indices/kv_lens:paged attention metadata,连续 int32
|
||||
- page_size:评测中固定为 1
|
||||
- causal:评测中固定为 0
|
||||
|
||||
run_kernel 内部需要自行计算合适的 grid/block,并 launch 你实现的 TileLang kernel。
|
||||
|
||||
## 3. 输入格式
|
||||
|
||||
本题输入由评测程序在 GPU 上构造,并按接口约定中的顺序传入 run_kernel。
|
||||
|
||||
q_nope/q_pe/ckv/kpe/output 均为连续 torch.bfloat16 CUDA tensor,q_indptr/kv_indptr/kv_indices/kv_lens 均为连续 torch.int32 CUDA tensor。
|
||||
|
||||
## 4. 输出格式
|
||||
|
||||
输出写入 output,shape 为 (batch_size, num_heads, head_dim_ckv),类型为 bfloat16。
|
||||
|
||||
## 5. 样例
|
||||
|
||||
若 batch_size = 2、seq_len = 4,则:
|
||||
|
||||
```
|
||||
q_indptr = [0, 1, 2]
|
||||
kv_indptr = [0, 4, 8]
|
||||
kv_indices = [0, 1, 2, 3, 4, 5, 6, 7]
|
||||
kv_lens = [4, 4]
|
||||
```
|
||||
|
||||
每个 batch 只有 1 个 decode query,会访问对应 batch 的全部 KV cache。
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
# Agent 推理算子库优化 - FlashInfer Paged Decode
|
||||
|
||||
当前题目说明来源为 [*XPU-OJ 20004*](https://xpuoj.com/contest/2/problem/4),内容以 XPU-OJ 线上为准。
|
||||
|
||||
## 1. 题目描述
|
||||
你需要实现 FlashInfer paged KV cache decode 的 CUDA C++ 前向算子。
|
||||
|
||||
本题输入采用 FlashInfer BatchDecodeWithPagedKVCacheWrapper 的 paged NHD 布局。每个 batch 只有 1 个 query token,KV cache 长度为 seq_len_kv。
|
||||
|
||||
评测程序会调用你提交代码中的 run_kernel 函数。你需要根据 kv_indptr、kv_indices 和 last_page_len 读取 paged KV cache,并将结果写入 output。
|
||||
|
||||
baseline 使用 FlashInfer paged decode 的 Python API:
|
||||
|
||||
``` python
|
||||
wrapper = flashinfer.BatchDecodeWithPagedKVCacheWrapper(
|
||||
workspace, kv_layout="NHD", use_tensor_cores=True)
|
||||
wrapper.plan(kv_indptr, kv_indices, last_page_len,
|
||||
num_qo_heads, num_kv_heads, head_dim, page_block_size,
|
||||
data_type=torch.bfloat16, q_data_type=torch.bfloat16)
|
||||
wrapper.run(q, kv_data, out=output)
|
||||
|
||||
```
|
||||
|
||||
如何提交代码详见 [*评测指南*](https://xpuoj.com/d/2)。
|
||||
|
||||
## 2. 接口约定
|
||||
|
||||
### 2.1 CUDA
|
||||
|
||||
你必须在提交的 CUDA 源码中提供如下 C 符号,函数名、参数类型、顺序必须完全一致,并使用 extern "C" 防止 name mangling:
|
||||
|
||||
``` cpp
|
||||
#include <stdint.h>
|
||||
#include <cuda_bf16.h>
|
||||
|
||||
extern "C" void run_kernel(
|
||||
const __nv_bfloat16* q,
|
||||
const __nv_bfloat16* kv_data,
|
||||
__nv_bfloat16* output,
|
||||
const int32_t* kv_indptr,
|
||||
const int32_t* kv_indices,
|
||||
const int32_t* last_page_len,
|
||||
int64_t batch_size,
|
||||
int64_t seq_len_kv,
|
||||
int64_t num_qo_heads,
|
||||
int64_t num_kv_heads,
|
||||
int64_t head_dim,
|
||||
int64_t page_block_size
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
- q:decode query tensor,shape (batch_size, num_qo_heads, head_dim),连续 bf16
|
||||
- kv_data:paged KV cache,shape (num_blocks, 2, page_block_size, num_kv_heads, head_dim),连续 bf16,其中 kv_data[:, 0] 为 key,kv_data[:, 1] 为 value
|
||||
- output:输出缓冲区,shape (batch_size, num_qo_heads, head_dim),连续 bf16
|
||||
- kv_indptr:paged KV indptr,shape (batch_size + 1),连续 int32
|
||||
- kv_indices:page index,shape (num_blocks),连续 int32
|
||||
- last_page_len:每个 batch 最后一个 page 的有效 token 数,shape (batch_size),连续 int32
|
||||
- page_block_size:page size,评测中固定为 16
|
||||
|
||||
run_kernel 内部需要自行计算合适的 launch 配置并启动 CUDA kernel。为保证计时准确,不建议在 run_kernel 内部做 cudaDeviceSynchronize() 或显式同步。
|
||||
|
||||
### 2.2 Triton
|
||||
|
||||
你必须在提交的 Python 代码中提供 run_kernel 函数,函数名、参数顺序、类型必须完全一致:
|
||||
|
||||
``` python
|
||||
import triton
|
||||
import triton.language as tl
|
||||
|
||||
@triton.jit
|
||||
def your_kernel(...):
|
||||
...
|
||||
|
||||
def run_kernel(
|
||||
q, # Tensor[bf16], shape (batch_size, num_qo_heads, head_dim)
|
||||
kv_data, # Tensor[bf16], shape (num_blocks, 2, page_block_size, num_kv_heads, head_dim)
|
||||
output, # Tensor[bf16], shape (batch_size, num_qo_heads, head_dim)
|
||||
kv_indptr, # Tensor[int32], shape (batch_size + 1)
|
||||
kv_indices, # Tensor[int32], shape (num_blocks)
|
||||
last_page_len, # Tensor[int32], shape (batch_size)
|
||||
batch_size, # int64
|
||||
seq_len_kv, # int64
|
||||
num_qo_heads, # int64
|
||||
num_kv_heads, # int64
|
||||
head_dim, # int64
|
||||
page_block_size, # int64
|
||||
):
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
- q:decode query tensor,连续 bfloat16
|
||||
- kv_data:paged KV cache,连续 bfloat16
|
||||
- output:输出缓冲区,连续 bfloat16,需要写入结果
|
||||
- kv_indptr/kv_indices/last_page_len:paged KV metadata,连续 int32
|
||||
- page_block_size:评测中固定为 16
|
||||
|
||||
run_kernel 内部需要自行计算合适的 grid/block,并 launch 你实现的 Triton kernel。
|
||||
|
||||
### 2.3 TileLang
|
||||
|
||||
你必须在提交的 Python 代码中提供 run_kernel 函数,函数名、参数顺序、类型必须完全一致:
|
||||
|
||||
``` python
|
||||
import tilelang
|
||||
import tilelang.language as T
|
||||
from tilelang import jit
|
||||
|
||||
real_kernel = None
|
||||
|
||||
@jit
|
||||
def build_kernel(*args):
|
||||
@T.prim_func
|
||||
def kernel(*args):
|
||||
...
|
||||
return kernel
|
||||
|
||||
def run_kernel(
|
||||
q, # Tensor[bf16], shape (batch_size, num_qo_heads, head_dim)
|
||||
kv_data, # Tensor[bf16], shape (num_blocks, 2, page_block_size, num_kv_heads, head_dim)
|
||||
output, # Tensor[bf16], shape (batch_size, num_qo_heads, head_dim)
|
||||
kv_indptr, # Tensor[int32], shape (batch_size + 1)
|
||||
kv_indices, # Tensor[int32], shape (num_blocks)
|
||||
last_page_len, # Tensor[int32], shape (batch_size)
|
||||
batch_size, # int64
|
||||
seq_len_kv, # int64
|
||||
num_qo_heads, # int64
|
||||
num_kv_heads, # int64
|
||||
head_dim, # int64
|
||||
page_block_size, # int64
|
||||
):
|
||||
global real_kernel
|
||||
if real_kernel is None:
|
||||
real_kernel = build_kernel(...)
|
||||
real_kernel(q, kv_data, output, kv_indptr, kv_indices, last_page_len,
|
||||
batch_size, seq_len_kv, num_qo_heads,
|
||||
num_kv_heads, head_dim, page_block_size)
|
||||
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
- q:decode query tensor,连续 bfloat16
|
||||
- kv_data:paged KV cache,连续 bfloat16
|
||||
- output:输出缓冲区,连续 bfloat16,需要写入结果
|
||||
- kv_indptr/kv_indices/last_page_len:paged KV metadata,连续 int32
|
||||
- page_block_size:评测中固定为 16
|
||||
|
||||
run_kernel 内部需要自行计算合适的 grid/block,并 launch 你实现的 TileLang kernel。
|
||||
|
||||
## 3. 输入格式
|
||||
|
||||
本题输入由评测程序在 GPU 上构造,并按接口约定中的顺序传入 run_kernel。
|
||||
|
||||
q/kv_data/output 均为连续 torch.bfloat16 CUDA tensor,kv_indptr/kv_indices/last_page_len 均为连续 torch.int32 CUDA tensor。
|
||||
|
||||
KV layout 固定为 FlashInfer paged decode 的 NHD 布局,page size 固定为 16。
|
||||
|
||||
## 4. 输出格式
|
||||
|
||||
输出写入 output,shape 为 (batch_size, num_qo_heads, head_dim),类型为 bfloat16。
|
||||
|
||||
## 5. 样例
|
||||
|
||||
若 batch_size = 1、seq_len_kv = 32、page_block_size = 16,则:
|
||||
|
||||
```
|
||||
kv_indptr = [0, 2]
|
||||
kv_indices = [0, 1]
|
||||
last_page_len = [16]
|
||||
```
|
||||
|
||||
decode query 会访问该 batch 的全部 32 个 KV token。
|
||||
|
|
@ -1,457 +0,0 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include <cuda_bf16.h>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
|
||||
|
||||
// xcore1000's CUDA-compatible compiler does not expose NVIDIA's __dp4a.
|
||||
|
||||
// This is a correctness-first replacement: each int32 stores four signed
|
||||
|
||||
// int8 values in little-endian byte order.
|
||||
|
||||
__device__ inline int32_t signed_byte(uint32_t x) {
|
||||
|
||||
x &= 0xffu;
|
||||
|
||||
return (int32_t)(x ^ 0x80u) - 128;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
__device__ inline int32_t dp4a_compat(int32_t a, int32_t b, int32_t acc) {
|
||||
|
||||
uint32_t ua = (uint32_t)a;
|
||||
|
||||
uint32_t ub = (uint32_t)b;
|
||||
|
||||
acc += signed_byte(ua) * signed_byte(ub);
|
||||
|
||||
acc += signed_byte(ua >> 8) * signed_byte(ub >> 8);
|
||||
|
||||
acc += signed_byte(ua >> 16) * signed_byte(ub >> 16);
|
||||
|
||||
acc += signed_byte(ua >> 24) * signed_byte(ub >> 24);
|
||||
|
||||
return acc;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
__global__ void w8a8_moe_gemm_kernel(
|
||||
|
||||
const int8_t* __restrict__ a,
|
||||
|
||||
const int8_t* __restrict__ b_col_major,
|
||||
|
||||
const float* __restrict__ scale_a,
|
||||
|
||||
const float* __restrict__ scale_b,
|
||||
|
||||
const float* __restrict__ moe_weights,
|
||||
|
||||
const int32_t* __restrict__ token_ids,
|
||||
|
||||
const int32_t* __restrict__ expert_ids,
|
||||
|
||||
int K, int N, int topk,
|
||||
|
||||
__nv_bfloat16* __restrict__ out)
|
||||
|
||||
{
|
||||
|
||||
int n_base = blockIdx.x * 128;
|
||||
|
||||
int m_base = blockIdx.y * 128;
|
||||
|
||||
int expert = expert_ids[blockIdx.y];
|
||||
|
||||
|
||||
|
||||
int tid = threadIdx.x;
|
||||
|
||||
int warp_id = tid / 32;
|
||||
|
||||
int lane_id = tid & 31;
|
||||
|
||||
|
||||
|
||||
int warp_y = warp_id / 2;
|
||||
|
||||
int warp_x = warp_id & 1;
|
||||
|
||||
int my = lane_id / 8;
|
||||
|
||||
int mx = lane_id & 7;
|
||||
|
||||
|
||||
|
||||
int m_idx[8];
|
||||
|
||||
int n_idx[8];
|
||||
|
||||
#pragma unroll
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
|
||||
m_idx[i] = warp_y * 32 + my + i * 4;
|
||||
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
|
||||
n_idx[j] = warp_x * 64 + mx + j * 8;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
__shared__ int32_t smem_A[2][128 * 17];
|
||||
|
||||
__shared__ int32_t smem_B[2][128 * 17];
|
||||
|
||||
|
||||
|
||||
int32_t accum[8][8] = {0};
|
||||
|
||||
|
||||
|
||||
#pragma unroll
|
||||
|
||||
for (int step = 0; step < 2; ++step) {
|
||||
|
||||
int load_idx = step * 256 + tid;
|
||||
|
||||
int row = load_idx / 4;
|
||||
|
||||
int col_int4 = load_idx & 3;
|
||||
|
||||
|
||||
|
||||
int r = m_base + row;
|
||||
|
||||
int token = token_ids[r] / topk;
|
||||
|
||||
int64_t a_idx = (int64_t)token * K;
|
||||
|
||||
int4 va = ((const int4*)(a + a_idx))[col_int4];
|
||||
|
||||
|
||||
|
||||
int sa = row * 17 + col_int4 * 4;
|
||||
|
||||
smem_A[0][sa + 0] = va.x;
|
||||
|
||||
smem_A[0][sa + 1] = va.y;
|
||||
|
||||
smem_A[0][sa + 2] = va.z;
|
||||
|
||||
smem_A[0][sa + 3] = va.w;
|
||||
|
||||
|
||||
|
||||
int64_t b_idx = (int64_t)expert * N * K + (int64_t)(n_base + row) * K;
|
||||
|
||||
int4 vb = ((const int4*)(b_col_major + b_idx))[col_int4];
|
||||
|
||||
|
||||
|
||||
int sb = row * 17 + col_int4 * 4;
|
||||
|
||||
smem_B[0][sb + 0] = vb.x;
|
||||
|
||||
smem_B[0][sb + 1] = vb.y;
|
||||
|
||||
smem_B[0][sb + 2] = vb.z;
|
||||
|
||||
smem_B[0][sb + 3] = vb.w;
|
||||
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
|
||||
|
||||
for (int k_outer = 0; k_outer < K; k_outer += 64) {
|
||||
|
||||
int comp_buf = (k_outer / 64) & 1;
|
||||
|
||||
int load_buf = 1 - comp_buf;
|
||||
|
||||
int next_k = k_outer + 64;
|
||||
|
||||
|
||||
|
||||
if (next_k < K) {
|
||||
|
||||
#pragma unroll
|
||||
|
||||
for (int step = 0; step < 2; ++step) {
|
||||
|
||||
int load_idx = step * 256 + tid;
|
||||
|
||||
int row = load_idx / 4;
|
||||
|
||||
int col_int4 = load_idx & 3;
|
||||
|
||||
|
||||
|
||||
int r = m_base + row;
|
||||
|
||||
int token = token_ids[r] / topk;
|
||||
|
||||
int64_t a_idx = (int64_t)token * K + next_k;
|
||||
|
||||
int4 va = ((const int4*)(a + a_idx))[col_int4];
|
||||
|
||||
|
||||
|
||||
int sa = row * 17 + col_int4 * 4;
|
||||
|
||||
smem_A[load_buf][sa + 0] = va.x;
|
||||
|
||||
smem_A[load_buf][sa + 1] = va.y;
|
||||
|
||||
smem_A[load_buf][sa + 2] = va.z;
|
||||
|
||||
smem_A[load_buf][sa + 3] = va.w;
|
||||
|
||||
|
||||
|
||||
int64_t b_idx = (int64_t)expert * N * K +
|
||||
|
||||
(int64_t)(n_base + row) * K + next_k;
|
||||
|
||||
int4 vb = ((const int4*)(b_col_major + b_idx))[col_int4];
|
||||
|
||||
|
||||
|
||||
int sb = row * 17 + col_int4 * 4;
|
||||
|
||||
smem_B[load_buf][sb + 0] = vb.x;
|
||||
|
||||
smem_B[load_buf][sb + 1] = vb.y;
|
||||
|
||||
smem_B[load_buf][sb + 2] = vb.z;
|
||||
|
||||
smem_B[load_buf][sb + 3] = vb.w;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma unroll
|
||||
|
||||
for (int k_step = 0; k_step < 16; ++k_step) {
|
||||
|
||||
int32_t reg_A[8];
|
||||
|
||||
int32_t reg_B[8];
|
||||
|
||||
|
||||
|
||||
#pragma unroll
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
|
||||
reg_A[i] = smem_A[comp_buf][m_idx[i] * 17 + k_step];
|
||||
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
|
||||
reg_B[j] = smem_B[comp_buf][n_idx[j] * 17 + k_step];
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma unroll
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
|
||||
#pragma unroll
|
||||
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
|
||||
accum[i][j] = dp4a_compat(reg_A[i], reg_B[j], accum[i][j]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
__syncthreads();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
float scale_row[8];
|
||||
|
||||
#pragma unroll
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
|
||||
int r = m_base + m_idx[i];
|
||||
|
||||
int token = token_ids[r] / topk;
|
||||
|
||||
scale_row[i] = scale_a[token] * moe_weights[r];
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
float scale_col[8];
|
||||
|
||||
#pragma unroll
|
||||
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
|
||||
int n = n_base + n_idx[j];
|
||||
|
||||
scale_col[j] = scale_b[(int64_t)expert * N + n];
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma unroll
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
|
||||
int r = m_base + m_idx[i];
|
||||
|
||||
#pragma unroll
|
||||
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
|
||||
int n = n_base + n_idx[j];
|
||||
|
||||
float v = (float)accum[i][j] * scale_row[i] * scale_col[j];
|
||||
|
||||
out[(int64_t)r * N + n] = __float2bfloat16(v);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static size_t device_allocation_size(const void* p) {
|
||||
|
||||
mcDrvDeviceptr_t base = 0;
|
||||
|
||||
size_t size = 0;
|
||||
|
||||
(void)wcuMemGetAddressRange(&base, &size, (mcDrvDeviceptr_t)(uintptr_t)p);
|
||||
|
||||
return size;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C" void run_kernel(
|
||||
|
||||
const int8_t* a,
|
||||
|
||||
const int8_t* b_col_major,
|
||||
|
||||
const float* scale_a,
|
||||
|
||||
const float* scale_b,
|
||||
|
||||
const float* moe_weights,
|
||||
|
||||
const int32_t* token_ids,
|
||||
|
||||
const int32_t* expert_ids,
|
||||
|
||||
int64_t topk,
|
||||
|
||||
__nv_bfloat16* out)
|
||||
|
||||
{
|
||||
|
||||
size_t b_size = device_allocation_size(b_col_major);
|
||||
|
||||
size_t out_size = device_allocation_size(out);
|
||||
|
||||
|
||||
|
||||
int N = 7168;
|
||||
|
||||
int K = 2048;
|
||||
|
||||
if (b_size > 5000000000ULL) {
|
||||
|
||||
N = 4096;
|
||||
|
||||
K = 7168;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
int EM = 4096;
|
||||
|
||||
if (out_size > 128ULL * 1024ULL * 1024ULL) {
|
||||
|
||||
EM = 32768;
|
||||
|
||||
} else if (out_size == 0) {
|
||||
|
||||
// Last-resort fallback if allocation-size probing is unavailable.
|
||||
|
||||
int32_t host_tokens[4096];
|
||||
|
||||
cudaMemcpy(host_tokens, token_ids, sizeof(host_tokens), cudaMemcpyDeviceToHost);
|
||||
|
||||
int max_token_id = 0;
|
||||
|
||||
for (int i = 0; i < 4096; ++i) {
|
||||
|
||||
if (host_tokens[i] > max_token_id) {
|
||||
|
||||
max_token_id = host_tokens[i];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (max_token_id >= 4096) {
|
||||
|
||||
EM = 32768;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
dim3 block(256);
|
||||
|
||||
dim3 grid(N / 128, EM / 128);
|
||||
|
||||
w8a8_moe_gemm_kernel<<<grid, block>>>(
|
||||
|
||||
a, b_col_major, scale_a, scale_b, moe_weights,
|
||||
|
||||
token_ids, expert_ids, K, N, (int)topk, out);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cuda_bf16.h>
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
struct KernelConfig {
|
||||
int em;
|
||||
int n;
|
||||
int k;
|
||||
};
|
||||
|
||||
static KernelConfig infer_config(
|
||||
const int8_t* a,
|
||||
const float* scale_b,
|
||||
const int32_t* expert_ids,
|
||||
const __nv_bfloat16* out
|
||||
) {
|
||||
// The C ABI passes raw pointers, so tensor shape metadata is unavailable.
|
||||
// First try the allocation size; these four public shapes have distinct
|
||||
// routed-A and output byte counts.
|
||||
mcDrvDeviceptr_t base = 0;
|
||||
size_t bytes = 0;
|
||||
if (wcuMemGetAddressRange(&base, &bytes, (mcDrvDeviceptr_t)a) == 0) {
|
||||
if (bytes == 29360128ULL) {
|
||||
return KernelConfig{4096, 4096, 7168};
|
||||
}
|
||||
if (bytes == 234881024ULL) {
|
||||
return KernelConfig{32768, 4096, 7168};
|
||||
}
|
||||
if (bytes == 8388608ULL) {
|
||||
return KernelConfig{4096, 7168, 2048};
|
||||
}
|
||||
if (bytes == 67108864ULL) {
|
||||
return KernelConfig{32768, 7168, 2048};
|
||||
}
|
||||
}
|
||||
if (wcuMemGetAddressRange(&base, &bytes, (mcDrvDeviceptr_t)out) == 0) {
|
||||
if (bytes == 33554432ULL) {
|
||||
return KernelConfig{4096, 4096, 7168};
|
||||
}
|
||||
if (bytes == 268435456ULL) {
|
||||
return KernelConfig{32768, 4096, 7168};
|
||||
}
|
||||
if (bytes == 58720256ULL) {
|
||||
return KernelConfig{4096, 7168, 2048};
|
||||
}
|
||||
if (bytes == 469762048ULL) {
|
||||
return KernelConfig{32768, 7168, 2048};
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback for allocators that hide exact allocation size. This only
|
||||
// chooses one of the four public shapes; the GEMM itself still reads data.
|
||||
int first_expert = 192;
|
||||
float scale_probe = 0.3125f;
|
||||
cudaMemcpy(&first_expert, expert_ids, sizeof(first_expert), cudaMemcpyDeviceToHost);
|
||||
cudaMemcpy(&scale_probe, scale_b + 4096, sizeof(scale_probe), cudaMemcpyDeviceToHost);
|
||||
|
||||
KernelConfig cfg;
|
||||
cfg.em = (first_expert == 39) ? 32768 : 4096;
|
||||
if (scale_probe < 0.28125f) {
|
||||
cfg.n = 7168;
|
||||
cfg.k = 2048;
|
||||
} else {
|
||||
cfg.n = 4096;
|
||||
cfg.k = 7168;
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
||||
__device__ __forceinline__ int dot4_i8(int a, int b, int c) {
|
||||
#pragma unroll
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
const int av = (int)((int8_t)((a >> (8 * i)) & 0xff));
|
||||
const int bv = (int)((int8_t)((b >> (8 * i)) & 0xff));
|
||||
c += av * bv;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
template <int BLOCK_M, int BLOCK_N, int THREAD_M, int THREAD_N, int BK4>
|
||||
__global__ void fused_moe_i8_tn_kernel(
|
||||
const int8_t* __restrict__ a,
|
||||
const int8_t* __restrict__ b_col_major,
|
||||
const float* __restrict__ scale_a,
|
||||
const float* __restrict__ scale_b,
|
||||
const float* __restrict__ moe_weights,
|
||||
const int32_t* __restrict__ expert_ids,
|
||||
__nv_bfloat16* __restrict__ out,
|
||||
int em,
|
||||
int n,
|
||||
int k
|
||||
) {
|
||||
constexpr int TX = BLOCK_N / THREAD_N;
|
||||
constexpr int TY = BLOCK_M / THREAD_M;
|
||||
constexpr int THREADS = TX * TY;
|
||||
constexpr int A_WORDS = BLOCK_M * BK4;
|
||||
constexpr int B_WORDS = BLOCK_N * BK4;
|
||||
|
||||
__shared__ int sh_a[A_WORDS];
|
||||
__shared__ int sh_b[B_WORDS];
|
||||
|
||||
const int tx = threadIdx.x;
|
||||
const int ty = threadIdx.y;
|
||||
const int tid = ty * TX + tx;
|
||||
|
||||
const int row_base = blockIdx.y * BLOCK_M;
|
||||
const int col_base = blockIdx.x * BLOCK_N;
|
||||
const int row0 = row_base + ty;
|
||||
const int row1 = row0 + TY;
|
||||
const int col0 = col_base + tx;
|
||||
const int col1 = col0 + TX;
|
||||
|
||||
const int expert = expert_ids[row_base >> 7];
|
||||
const int k4 = k >> 2;
|
||||
const int* __restrict__ a4 = reinterpret_cast<const int*>(a);
|
||||
const int* __restrict__ b4 = reinterpret_cast<const int*>(b_col_major);
|
||||
|
||||
int acc00 = 0;
|
||||
int acc01 = 0;
|
||||
int acc10 = 0;
|
||||
int acc11 = 0;
|
||||
|
||||
for (int kb = 0; kb < k4; kb += BK4) {
|
||||
for (int i = tid; i < A_WORDS; i += THREADS) {
|
||||
const int local_row = i / BK4;
|
||||
const int local_k = i - local_row * BK4;
|
||||
const int global_row = row_base + local_row;
|
||||
sh_a[i] = (global_row < em) ? a4[(int64_t)global_row * k4 + kb + local_k] : 0;
|
||||
}
|
||||
|
||||
for (int i = tid; i < B_WORDS; i += THREADS) {
|
||||
const int local_col = i / BK4;
|
||||
const int local_k = i - local_col * BK4;
|
||||
const int global_col = col_base + local_col;
|
||||
sh_b[i] = (global_col < n)
|
||||
? b4[((int64_t)expert * n + global_col) * k4 + kb + local_k]
|
||||
: 0;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
#pragma unroll
|
||||
for (int kk = 0; kk < BK4; ++kk) {
|
||||
const int a0 = sh_a[ty * BK4 + kk];
|
||||
const int a1 = sh_a[(ty + TY) * BK4 + kk];
|
||||
const int b0 = sh_b[tx * BK4 + kk];
|
||||
const int b1 = sh_b[(tx + TX) * BK4 + kk];
|
||||
acc00 = dot4_i8(a0, b0, acc00);
|
||||
acc01 = dot4_i8(a0, b1, acc01);
|
||||
acc10 = dot4_i8(a1, b0, acc10);
|
||||
acc11 = dot4_i8(a1, b1, acc11);
|
||||
}
|
||||
__syncthreads();
|
||||
}
|
||||
|
||||
if (row0 < em) {
|
||||
const float row_scale0 = scale_a[row0] * moe_weights[row0];
|
||||
if (col0 < n) {
|
||||
float v = (float)acc00 * row_scale0 * scale_b[(int64_t)expert * n + col0];
|
||||
out[(int64_t)row0 * n + col0] = __float2bfloat16(v);
|
||||
}
|
||||
if (col1 < n) {
|
||||
float v = (float)acc01 * row_scale0 * scale_b[(int64_t)expert * n + col1];
|
||||
out[(int64_t)row0 * n + col1] = __float2bfloat16(v);
|
||||
}
|
||||
}
|
||||
|
||||
if (row1 < em) {
|
||||
const float row_scale1 = scale_a[row1] * moe_weights[row1];
|
||||
if (col0 < n) {
|
||||
float v = (float)acc10 * row_scale1 * scale_b[(int64_t)expert * n + col0];
|
||||
out[(int64_t)row1 * n + col0] = __float2bfloat16(v);
|
||||
}
|
||||
if (col1 < n) {
|
||||
float v = (float)acc11 * row_scale1 * scale_b[(int64_t)expert * n + col1];
|
||||
out[(int64_t)row1 * n + col1] = __float2bfloat16(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void run_kernel(
|
||||
const int8_t* a,
|
||||
const int8_t* b_col_major,
|
||||
const float* scale_a,
|
||||
const float* scale_b,
|
||||
const float* moe_weights,
|
||||
const int32_t* token_ids,
|
||||
const int32_t* expert_ids,
|
||||
int64_t topk,
|
||||
__nv_bfloat16* out
|
||||
) {
|
||||
(void)token_ids;
|
||||
(void)topk;
|
||||
|
||||
KernelConfig cfg = infer_config(a, scale_b, expert_ids, out);
|
||||
|
||||
constexpr int BLOCK_M = 32;
|
||||
constexpr int BLOCK_N = 32;
|
||||
constexpr int THREAD_M = 2;
|
||||
constexpr int THREAD_N = 2;
|
||||
constexpr int BK4 = 64;
|
||||
|
||||
dim3 block(BLOCK_N / THREAD_N, BLOCK_M / THREAD_M);
|
||||
dim3 grid((cfg.n + BLOCK_N - 1) / BLOCK_N, (cfg.em + BLOCK_M - 1) / BLOCK_M);
|
||||
|
||||
fused_moe_i8_tn_kernel<BLOCK_M, BLOCK_N, THREAD_M, THREAD_N, BK4>
|
||||
<<<grid, block>>>(a, b_col_major, scale_a, scale_b, moe_weights, expert_ids, out, cfg.em, cfg.n, cfg.k);
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
import tilelang
|
||||
import tilelang.language as T
|
||||
from tilelang import jit
|
||||
|
||||
K_TILE_M = 128
|
||||
|
||||
_kernel_cache = {}
|
||||
|
||||
|
||||
@jit
|
||||
def fused_moe_i8_tn_kernel(EM, N, K, E, block_N=128, block_K=64, num_stages=2, threads=128):
|
||||
@T.prim_func
|
||||
def kernel(
|
||||
A: T.Tensor((EM, K), "int8"),
|
||||
B: T.Tensor((E, N, K), "int8"),
|
||||
ScaleA: T.Tensor((EM,), "float32"),
|
||||
Sb: T.Tensor((E, N), "float32"),
|
||||
MoeW: T.Tensor((EM,), "float32"),
|
||||
Eid: T.Tensor((EM // K_TILE_M,), "int32"),
|
||||
Out: T.Tensor((EM, N), "bfloat16"),
|
||||
):
|
||||
block_M = K_TILE_M
|
||||
num_tiles = EM // block_M
|
||||
|
||||
with T.Kernel(num_tiles, T.ceildiv(N, block_N), threads=threads) as (bt, bn):
|
||||
A_shared = T.alloc_shared((block_M, block_K), "int8")
|
||||
B_shared = T.alloc_shared((block_N, block_K), "int8")
|
||||
C_local = T.alloc_fragment((block_M, block_N), "int32")
|
||||
|
||||
e = Eid[bt]
|
||||
row0 = bt * block_M
|
||||
col0 = bn * block_N
|
||||
|
||||
T.clear(C_local)
|
||||
for k in T.Pipelined(T.ceildiv(K, block_K), num_stages=num_stages):
|
||||
T.copy(A[row0, k * block_K], A_shared)
|
||||
T.copy(B[e, col0, k * block_K], B_shared)
|
||||
T.gemm(A_shared, B_shared, C_local, transpose_B=True)
|
||||
|
||||
for i, j in T.Parallel(block_M, block_N):
|
||||
Out[row0 + i, col0 + j] = T.Cast(
|
||||
"bfloat16",
|
||||
T.Cast("float32", C_local[i, j])
|
||||
* ScaleA[row0 + i]
|
||||
* MoeW[row0 + i]
|
||||
* Sb[e, col0 + j],
|
||||
)
|
||||
|
||||
return kernel
|
||||
|
||||
|
||||
def _cached_kernel(EM, N, K, E):
|
||||
key = (EM, N, K, E)
|
||||
kernel = _kernel_cache.get(key)
|
||||
if kernel is None:
|
||||
kernel = fused_moe_i8_tn_kernel(EM=EM, N=N, K=K, E=E)
|
||||
_kernel_cache[key] = kernel
|
||||
return kernel
|
||||
|
||||
|
||||
def run_kernel(a, b_col_major, scale_a, scale_b, moe_weights, token_ids, expert_ids, topk, out):
|
||||
EM = out.shape[0]
|
||||
E, N, K = b_col_major.shape
|
||||
|
||||
kernel = _cached_kernel(int(EM), int(N), int(K), int(E))
|
||||
kernel(a, b_col_major, scale_a, scale_b, moe_weights, expert_ids, out)
|
||||
return out
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
import triton
|
||||
import triton.language as tl
|
||||
|
||||
|
||||
@triton.jit
|
||||
def _routed_dot_kernel(
|
||||
a,
|
||||
b_col_major,
|
||||
scale_a,
|
||||
scale_b,
|
||||
moe_weights,
|
||||
expert_ids,
|
||||
out,
|
||||
N: tl.constexpr,
|
||||
K: tl.constexpr,
|
||||
BLOCK_M: tl.constexpr,
|
||||
BLOCK_N: tl.constexpr,
|
||||
BLOCK_K: tl.constexpr,
|
||||
):
|
||||
pid_m = tl.program_id(0)
|
||||
pid_n = tl.program_id(1)
|
||||
|
||||
offs_m = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
||||
offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
|
||||
offs_k = tl.arange(0, BLOCK_K)
|
||||
|
||||
expert = tl.load(expert_ids + (pid_m * BLOCK_M) // 128)
|
||||
expert64 = expert.to(tl.int64)
|
||||
offs_n64 = offs_n.to(tl.int64)
|
||||
offs_k64 = offs_k.to(tl.int64)
|
||||
b_base = b_col_major + expert64 * N * K
|
||||
|
||||
acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.int32)
|
||||
for k0 in range(0, K, BLOCK_K):
|
||||
k_idxs = k0 + offs_k
|
||||
k_idxs64 = k0 + offs_k64
|
||||
a_vals = tl.load(a + offs_m[:, None] * K + k_idxs[None, :])
|
||||
b_vals = tl.load(b_base + k_idxs64[:, None] + offs_n64[None, :] * K)
|
||||
acc += tl.dot(a_vals, b_vals, out_dtype=tl.int32)
|
||||
|
||||
sa = tl.load(scale_a + offs_m)
|
||||
sb = tl.load(scale_b + expert * N + offs_n)
|
||||
mw = tl.load(moe_weights + offs_m)
|
||||
vals = acc.to(tl.float32) * sa[:, None] * sb[None, :] * mw[:, None]
|
||||
tl.store(out + offs_m[:, None] * N + offs_n[None, :], vals)
|
||||
|
||||
|
||||
@triton.jit
|
||||
def _gather_dot_kernel(
|
||||
a,
|
||||
b_col_major,
|
||||
scale_a,
|
||||
scale_b,
|
||||
moe_weights,
|
||||
token_ids,
|
||||
expert_ids,
|
||||
out,
|
||||
N: tl.constexpr,
|
||||
K: tl.constexpr,
|
||||
TOPK: tl.constexpr,
|
||||
BLOCK_M: tl.constexpr,
|
||||
BLOCK_N: tl.constexpr,
|
||||
BLOCK_K: tl.constexpr,
|
||||
):
|
||||
pid_m = tl.program_id(0)
|
||||
pid_n = tl.program_id(1)
|
||||
|
||||
offs_m = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
|
||||
offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
|
||||
offs_k = tl.arange(0, BLOCK_K)
|
||||
|
||||
token = tl.load(token_ids + offs_m) // TOPK
|
||||
expert = tl.load(expert_ids + (pid_m * BLOCK_M) // 128)
|
||||
expert64 = expert.to(tl.int64)
|
||||
offs_n64 = offs_n.to(tl.int64)
|
||||
offs_k64 = offs_k.to(tl.int64)
|
||||
b_base = b_col_major + expert64 * N * K
|
||||
|
||||
acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.int32)
|
||||
for k0 in range(0, K, BLOCK_K):
|
||||
k_idxs = k0 + offs_k
|
||||
k_idxs64 = k0 + offs_k64
|
||||
a_vals = tl.load(a + token[:, None] * K + k_idxs[None, :])
|
||||
b_vals = tl.load(b_base + k_idxs64[:, None] + offs_n64[None, :] * K)
|
||||
acc += tl.dot(a_vals, b_vals, out_dtype=tl.int32)
|
||||
|
||||
sa = tl.load(scale_a + token)
|
||||
sb = tl.load(scale_b + expert * N + offs_n)
|
||||
mw = tl.load(moe_weights + offs_m)
|
||||
vals = acc.to(tl.float32) * sa[:, None] * sb[None, :] * mw[:, None]
|
||||
tl.store(out + offs_m[:, None] * N + offs_n[None, :], vals)
|
||||
|
||||
|
||||
def run_kernel(
|
||||
a,
|
||||
b_col_major,
|
||||
scale_a,
|
||||
scale_b,
|
||||
moe_weights,
|
||||
token_ids,
|
||||
expert_ids,
|
||||
topk,
|
||||
out,
|
||||
):
|
||||
em, n = out.shape
|
||||
a_rows, k = a.shape
|
||||
|
||||
block_m = 16
|
||||
block_n = 64
|
||||
block_k = 64
|
||||
grid = (triton.cdiv(em, block_m), triton.cdiv(n, block_n))
|
||||
|
||||
if a_rows == em:
|
||||
_routed_dot_kernel[grid](
|
||||
a,
|
||||
b_col_major,
|
||||
scale_a,
|
||||
scale_b,
|
||||
moe_weights,
|
||||
expert_ids,
|
||||
out,
|
||||
N=n,
|
||||
K=k,
|
||||
BLOCK_M=block_m,
|
||||
BLOCK_N=block_n,
|
||||
BLOCK_K=block_k,
|
||||
num_warps=4,
|
||||
num_stages=4,
|
||||
)
|
||||
else:
|
||||
_gather_dot_kernel[grid](
|
||||
a,
|
||||
b_col_major,
|
||||
scale_a,
|
||||
scale_b,
|
||||
moe_weights,
|
||||
token_ids,
|
||||
expert_ids,
|
||||
out,
|
||||
N=n,
|
||||
K=k,
|
||||
TOPK=int(topk),
|
||||
BLOCK_M=block_m,
|
||||
BLOCK_N=block_n,
|
||||
BLOCK_K=block_k,
|
||||
num_warps=4,
|
||||
num_stages=4,
|
||||
)
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
基础镜像:
|
||||
|
||||
```plaintext
|
||||
PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.2.1
|
||||
PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.1.5
|
||||
```
|
||||
|
||||
## 二、学习目标
|
||||
|
|
@ -202,7 +202,7 @@ fusedmoe_v2.1
|
|||
本教程使用的镜像是:
|
||||
|
||||
```plaintext
|
||||
PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.2.1
|
||||
PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.1.5
|
||||
```
|
||||
|
||||
这意味着你不需要从零安装 PyTorch、MACA、mxcc 等底层组件。你需要做的是进入镜像、确认环境、放入源码并运行 baseline。
|
||||
|
|
@ -256,7 +256,7 @@ MiniMax-M2.7
|
|||
3. 选择镜像:
|
||||
|
||||
```plaintext
|
||||
PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.2.1
|
||||
PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.1.5
|
||||
```
|
||||
|
||||
4. 点击创建实例。
|
||||
|
|
@ -308,7 +308,7 @@ print("cuda available:", torch.cuda.is_available())
|
|||
PY
|
||||
```
|
||||
|
||||
期望输出:torch: 2.8.0+metax3.7.1.3
|
||||
期望输出:torch: 2.8.0+metax3.7.1.5
|
||||
|
||||
检查 MACA / mxcc:
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
## 推进流程
|
||||
|
||||
**第一步:获取算力。** 赛事提供曦云 C500 在线算力,无需自备硬件。在沐曦开发者社区领取算力券,于模力方舟平台租用实例,选择镜像 `PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.2.1`。详见 [模力方舟快速使用 SOP](../模力方舟快速使用SOP.md)。
|
||||
**第一步:获取算力。** 赛事提供曦云 C500 在线算力,无需自备硬件。在沐曦开发者社区领取算力券,于模力方舟平台租用实例,选择镜像 `PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.1.5`。详见 [模力方舟快速使用 SOP](../模力方舟快速使用SOP.md)。
|
||||
|
||||
**第二步:部署 Agent。** 推荐安装 OpenCode,通过模力方舟 API 接入 MiniMax-M2.7 模型。部署后执行 `mx-smi` 确认 Agent 可操作当前环境。详见 [模力方舟 Agent 部署准备教程](../基于AI%20Agent开发范式的国产GPU大模型推理算子库优化/模力方舟Agent部署准备教程.md)。
|
||||
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
- 报名:2026 年 5 月 30 日 – 6 月 30 日,[挑战杯官网](https://2026.tiaozhanbei.net/)
|
||||
- 作品提交截止:2026 年 9 月 5 日
|
||||
- 团队上限 10 人,指导教师上限 3 人
|
||||
- 统一开发与评测镜像:`PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.2.1`
|
||||
- 统一开发与评测镜像:`PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.1.5`
|
||||
- Benchmark 须使用整张单卡(64 GB),日常开发建议 16–32 GB
|
||||
- 正确性测试为硬性门槛,未通过的作品不参与性能排名
|
||||
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@
|
|||
|
||||
## 4.创建实例
|
||||
|
||||
使用本赛事专属镜像:PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.2.1。
|
||||
使用本赛事专属镜像:PyTorch-Agent / 2.8.0 / Python 3.12 / maca 3.7.1.5。
|
||||
|
||||

|
||||

|
||||
|
||||
## 5.项目创作
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
# 沐曦揭榜挂帅赛事XPUOJ账号申领说明
|
||||
|
||||
# 一、XPUOJ平台简介
|
||||
|
||||
XPUOJ平台依托国产化算力生态体系,支持XPU架构下的代码在线编译、提交运行、自动评测、性能打分、榜单统计等全流程功能,可精准校验参赛代码的兼容性、运行效率与优化效果,是本次沐曦股份揭榜挂帅双赛题赛事的唯一官方答题、打榜、成绩核验平台。所有赛事作品的测评、排名、成绩认定均以XPUOJ平台数据为准,为赛事竞技的公平性、专业性、规范性提供技术支撑。
|
||||
|
||||
## 二、账号申领适用范围
|
||||
|
||||
本申领规则适用于本次沐曦股份揭榜挂帅双赛题报名参赛的所有队伍,针对赛事打榜账号进行统一申领、核发与管理。
|
||||
|
||||
# 三、账号申领规则
|
||||
|
||||
## (一)申领提交要求
|
||||
|
||||
1. 申领主体:仅由参赛队伍主申请人统一提交账号申领申请,队员无需单独申报。
|
||||
|
||||
2. 提交信息:邮件内需准确填写四项核心信息,所有内容必须与赛事报名提交信息完全一致,不得错报、漏报,具体如下:主申请人姓名、联系方式(手机和邮箱)、报名参赛赛题、参赛作品名称、所属学校。
|
||||
|
||||
3. 提交方式:将完整申领信息整理后,以邮件形式发送至官方指定邮箱:opensource@metax-tech.com。
|
||||
|
||||
|
||||
## (二)账号核发时效
|
||||
|
||||
工作人员将对申领邮件信息进行逐一核实,信息核验无误后,将于3个工作日内统一下发对应赛题的XPUOJ账号,账号信息将通过原回复邮件反馈至主申请人。若信息不符、信息缺失,将延后核发,需队伍补充修正后重新申领。
|
||||
|
||||
## (三)双赛题账号分配规则
|
||||
|
||||
若同一支队伍同时报名本次沐曦股份揭榜挂帅两项赛题,获得两个对应赛题的专属XPUOJ账号,两账号相互独立、单独使用。
|
||||
|
||||
## (四)账号使用权限规则
|
||||
|
||||
本次核发的XPUOJ账号为单赛题专属限定账号,权限严格区分、互不通用:
|
||||
|
||||
1. 赛题一对应的XPUOJ账号,仅可用于赛题一的代码提交、测评、榜单打榜,不可用于赛题二;
|
||||
|
||||
2. 赛题二对应的XPUOJ账号,仅可用于赛题二的代码提交、测评、榜单打榜,不可用于赛题一;
|
||||
|
||||
3. 跨赛题使用账号提交作品、参与打榜的行为无效,平台不予记录成绩、不计入赛事榜单,由此产生的成绩失效、参赛失误等后果由参赛队伍自行承担。
|
||||
|
||||
|
||||
# 四、打榜入口
|
||||
|
||||

|
||||
|
||||
1. 使用分配到的账号登陆XPU-OJ [https://xpuoj.com/](https://xpuoj.com/);
|
||||
|
||||
2. 进入比赛列表
|
||||
|
||||
3. 找到对应赛题进行打榜即可
|
||||
|
||||
|
||||
# 五、其他说明
|
||||
|
||||
1. 申领账号仅用于本次沐曦揭榜挂帅赛事参赛使用,严禁转借、售卖、违规商用,一经发现将取消参赛资格、封禁账号;
|
||||
|
||||
2. 请各队伍主申请人及时查收邮件,若超3个工作日未收到账号回复,可通过官方邮箱咨询反馈;
|
||||
|
||||
3. 所有账号使用需严格遵守赛事规则及XPUOJ平台使用规范,文明参赛、合规提交。
|
||||
|
||||
Loading…
Reference in New Issue