开源社区的活跃度不只靠日常 PR,还需要有组织的竞赛活动。cann-competitions 是 CANN 社区的竞赛管理仓库:组织开发者围绕具体算子做性能优化,优胜方案合入社区主干。一场竞赛从策划到颁奖,经过赛题设计 → 平台搭建 → 基线模型 → 评测系统 → 开源审查,全套流程标准化。

一场竞赛的五个阶段

阶段 时长 核心产出
赛题设计 2-3 周 赛题说明书、基线代码、评测 harness
平台搭建 1-2 周 报名系统、提交系统、实时排行榜
竞赛进行 4-8 周 参赛者提交、每周 office hour、bug 修复
评审 & 答辩 1-2 周 最终排名、代码审查、现场答辩
开源合入 2-4 周 优胜方案 upstream、博客总结、下一届预告

赛题设计:选对算子

不是所有算子都适合当赛题。选算子的标准:

选算子 Checklist(赛题设计阶段)

必须:
  [ ] 有明确的性能优化空间(不是「实现功能」)
  [ ] 有公认的性能基线(可以对比 CPU/GPU/已有 NPU 实现)
  [ ] 有清晰的评测指标(TFLOPS、延迟、带宽利用率)
  [ ] 实现复杂度适中(选手 4-8 周能出结果)

禁止:
  [ ] 涉及未开源的华为内部算子
  [ ] 依赖特定硬件特性(只跑在 950PR,910 跑不了)
  [ ] 优化空间过于狭窄(改一处 tiling 就到顶)

实战案例:第二届 CANN 竞赛的赛题是 ops-transformer/flash_attention。优化空间明确(分块策略、双缓冲、Cube/Vector 并行),基线用社区已有的实现(~40% HBM 带宽利用率),评测指标用 TFLOPS 和延迟。

基线代码和评测 harness

赛题发布时,必须提供可运行的基线代码和自动评测 harness。选手在这个基础上优化,评测系统自动跑分。

# cann-competitions/2025-ops-optimization/baseline/bench_flashattn.py

import torch
import time
import numpy as np

def benchmark_flash_attention(
    B, H, N, D,
    dtype=torch.float16,
    warmup=10,
    repeat=100
):
    """
    基线 benchmark:调用 ops-transformer 的 FlashAttention 实现
    输出:latency (ms), TFLOPS, HBM 带宽利用率
    """
    q = torch.randn(B, H, N, D, dtype=dtype, device=torch.device("npu"))
    k = torch.randn(B, H, N, D, dtype=dtype, device=torch.device("npu"))
    v = torch.randn(B, H, N, D, dtype=dtype, device=torch.device("npu"))

    # 预热
    for _ in range(warmup):
        o = torch_npu.flash_attention(q, k, v)

    torch.npu.synchronize()

    # 正式计时
    start = time.perf_counter()
    for _ in range(repeat):
        o = torch_npu.flash_attention(q, k, v)
    torch.npu.synchronize()
    end = time.perf_counter()

    latency_ms = (end - start) / repeat * 1000

    # TFLOPS 计算:FlashAttention 的 FLOPs = 4 * B * H * N * D
    flops = 4 * B * H * N * D
    tflops = flops / (latency_ms / 1000) / 1e12

    # HBM 带宽利用率:理论峰值 1.2 TB/s,实测搬运量 / 时间
    hbm_accessed = B * H * N * D * 2  # Q,K 读 + O 写(简化)
    hbm_bw_util = (hbm_accessed / (latency_ms / 1000)) / 1.2e12

    return {
        "latency_ms": latency_ms,
        "tflops": tflops,
        "hbm_bw_util": hbm_bw_util,
    }

评测 harness 自动跑这个脚本,把结果写到排行榜。

提交系统和实时排行榜

选手通过 GitHub PR 提交优化后的算子实现。提交后 CI 自动跑评测 harness,把结果推到实时排行榜。

# .github/workflows/competition-score.yml
name: Competition Scoring

on:
  pull_request:
    paths:
      - "submissions/**/*.py"
      - "submissions/**/*.cpp"

jobs:
  score:
    runs-on: asc-p6000-8x910  # 固定硬件,消除硬件差异
    steps:
      - name: Check out
        uses: actions/checkout@v3

      - name: Build & Install Candidate
        run: |
          cd submissions/${{ github.actor }}
          mkdir build && cd build
          cmake .. && make -j

      - name: Run Benchmark
        run: |
          python baseline/bench_flashattn.py \
            --candidate=submissions/${{ github.actor }}/build/libfa.so \
            --output=result.json

      - name: Update Leaderboard
        run: |
          python scripts/update_leaderboard.py \
            --pr=${{ github.event.number }} \
            --result=result.json \
            --leaderboard=leaderboard.json

      - name: Push Leaderboard
        run: |
          git config user.name "competition-bot"
          git add leaderboard.json
          git commit -m "Update leaderboard: PR#${{ github.event.number }}"
          git push

排行榜实时更新,所有选手能看到自己当前的排名。

踩坑一:硬件异构导致成绩不可比

第一届竞赛用公有云上的随机 NPU 实例跑分——不同实例的固件版本不一样,同份代码跑两次结果差 5%。选手投诉「成绩不稳定」。

根因:NPU 固件版本影响算子性能(尤其是 Cube 单元的调度策略)。随机分配实例 → 固件版本随机 → 成绩不可比。

正确做法:竞赛用固定硬件池(同一批 910,同一固件版本),在 CI 的 runs-on 里写死。

# 错误:runs-on: ubuntu-latest + 动态申请 NPU 实例
# 正确:
runs-on: asc-p6000-8x910  # 固定硬件池,固件版本一致

踩坑二:基线代码有 bug 导致全体成绩无效

基线代码里 torch.npu.synchronize() 写成了 pass——没有等 NPU 算完就计时,所有人的成绩都比实际快 10×。

发现方式:一个选手优化后的代码反而比基线慢——review 代码发现计时不正确。

正确做法:基线代码发布前必须经过「反向验证」——故意写一个慢的基线(加 sleep),验证评测 harness 能正确捕捉到延迟。

# 基线代码发布前的反向验证
def test_benchmark_correctness():
    # 注入一个人为变慢的版本
    slow_kernel = make_slow_kernel(base_kernel, slowdown_factor=10)
    result = benchmark_flash_attention(slow_kernel)
    assert result["latency_ms"] > baseline_latency_ms * 9  # 应该慢 ~10x

踩坑三:优胜方案的 License 不清晰

第二届竞赛第一名的方案里用了 CUDA 代码(作为参考实现)——但竞赛要求「全部代码必须能在 NPU 上运行」。选手辩解说「CUDA 代码只是参考,不影响 NPU 实现」。

正确做法:竞赛报名时要求签署附加条款——「提交代码中的所有源文件必须能在昇腾 NPU 上编译运行,引用第三方代码必须附 License 声明」。

## 参赛条款(报名时勾选)

- [ ] 我确认提交代码中的所有源文件均为本人/本人队伍编写,
      或已获得原作者的开源授权(附 License 文件)。
- [ ] 我确认提交代码不含任何闭源组件、不含任何只能跑在
      GPU/其他厂商硬件上的代码。
- [ ] 我同意优胜方案的代码以 Apache 2.0 协议合入
      CANN 社区主干分支。

cann-competitions 的价值不只是办比赛——它把「算子优化」这件门槛很高的事,用标准化赛题 + 自动评测 + 实时排行榜的方式降低了参与门槛。第一届竞赛收到的 60+ 份提交里,有 3 份的性能超过了社区基线——这 3 份后来都合入了 ops-transformer 仓库。

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐