Skip to content

refactor(kernel): register-dispatch architecture for kernelize (registry/config/ops 三层拆分) - #255

Draft
werwrewe wants to merge 2 commits into
modelscope:mainfrom
werwrewe:pr/twinkle
Draft

refactor(kernel): register-dispatch architecture for kernelize (registry/config/ops 三层拆分)#255
werwrewe wants to merge 2 commits into
modelscope:mainfrom
werwrewe:pr/twinkle

Conversation

@werwrewe

@werwrewe werwrewe commented Jul 30, 2026

Copy link
Copy Markdown

主要工作

将 kernelize 重构为 register-dispatch 模型,替换掉硬编码的builtin/liger 双模块:

  • registry(注册层):各 op 自我注册,每个 backend 实现携带惰性加载 + 可用性检查;KernelChoice 按优先级链 dispatch 到第一个可用 backend
  • config(声明层):DEFAULT_KERNEL_CONFIG 为纯数据(target 全是点路径字符串)——mapping 只声明替换什么(WHAT)、用哪个 op(WHICH);不 import transformers/torch_npu/liger_kernel
  • core(分发层):kernelize() 遍历 mapping,把每条 entry dispatch 给 installer——怎么安装(HOW)由优先级 KernelChoice.installer → OpDefinition.installer → default_installer 决定(nn.Module 目标做类掉包,函数/方法做 setattr);transformers.* 家族缺失跳过在默认配置路径记 DEBUG,显式 mapping 升 WARNING(带 typo 提示)
  • ops:swiglu / geglu / rms_norm / rotary / moe / sdpa_attention / fla 从 npu_impls 与 liger_impls 迁入按 op 组织的包;新增 ep op(NPU grouped-matmul),替换 expert_parallel.py 中内联的 EP 专家循环
  • moe_experts 默认链为 ('npu',)——LigerExperts 仅显式 opt-in,吸收旧_prefer_cann_on_npu 的 drop 语义
  • hub():HubRef 作为 mapping value,经可选的 kernels 包惰性解析

cookbook:kernelize 仅由 torch-baseline 逃生门门控;--enable-liger 语义收窄为只门控 fused-linear-CE loss。EP utils:用高级索引替代 index_select(部分 torch_npu/CANN 版本 aclnnIndexAdd 损坏),split sizes 改为同步 D2H 拷贝。

docs:中英文档同步。

特性

  1. 保留 kernelize(model, mapping) 的核心模型:key 表示目标,value 表示如何替换
  2. 继续支持用户直接传入 class、function 或 HubRef 作为最终实现
  3. 增加按 op 名称和 backend 优先级自动选择实现的能力(KernelChoice
  4. 将同一算子的 NPU、Liger 等实现集中在 ops/<op>/ 下管理
  5. 支持普通 class/function 替换之外的安装方式(如 transformers SDPA 全局注册、FLA 逐实例 patch)
  6. 保持 backend 实现惰性加载:未选择某个 backend 时不导入其可选依赖
  7. 默认配置和用户配置使用同一套 mapping 协议,用户可以复制、合并和覆盖默认配置

分发流程(register-dispatch)

kernelize(model, mapping)
  └─ 逐条 (target, replacement):
       ├─ replacement 是 KernelChoice ──► get_op(op) ──► resolve_impl(op, backends, warn, target)
       │      按 backends 顺序五步回退:未注册→顺延 / available() False(含原因)→顺延 /
       │      load(target) 异常→顺延 / 首个成功→(impl, backend) / 全失败→保留原始,不调 installer
       │      installer = choice.installer → op.installer → default_installer(优先级)
       └─ 否则(直接 impl / HubRef)──► resolve_direct_value(HubRef 惰性下载,其余直通)
                                          installer = default_installer
  └─ installer(model, target, impl)      # 失败上抛,不留半安装状态

kernelize 接口变化

新接口用法举例

from twinkle.kernel import kernelize, hub, KernelChoice, DEFAULT_KERNEL_CONFIG

# ① 默认配置:NPU 上打 CANN 实现;CUDA 上 npu 链自动落 liger(装了才生效)
model = kernelize(model)

# ② 在默认配置基础上定制(复制 + 覆盖)
model = kernelize(model, {**DEFAULT_KERNEL_CONFIG,
    # 沿 backend 链自动选择(liger 优先,不行退 npu)
    'transformers.models.qwen3.modeling_qwen3.Qwen3RMSNorm':
        KernelChoice(op='rms_norm', backends=('liger', 'npu')),
    # CUDA 上为 MoE experts 显式 opt-in LigerExperts(默认链 ('npu',) 不启用)
    'transformers.models.qwen3_moe.modeling_qwen3_moe.Qwen3MoeExperts.forward':
        KernelChoice(op='moe_experts', backends=('liger',)),
    # 直接指定实现,绕过选择(旧协议,继续支持)
    Qwen2RMSNorm: MyRMSNorm,
    # HF Hub kernel,执行时惰性下载(需 pip install kernels)
    'transformers.models.qwen3.modeling_qwen3.Qwen3MLP.forward':
        hub('kernels-community/activation:SiluAndMul', version=1),
})

目录结构

src/twinkle/kernel/
├── __init__.py          # 公开面精确 4 符号:kernelize / hub / KernelChoice / DEFAULT_KERNEL_CONFIG
├── core.py              # 分发层:kernelize()、default_installer、_resolve_dotted、HubRef
├── registry.py          # 注册层:register_op / KernelImpl / OpDefinition / KernelChoice / resolve_impl
├── config.py            # 声明层:DEFAULT_KERNEL_CONFIG(62 条纯数据,按家族分组构建)
└── ops/                 # impl 本体 + op 注册(每个 op 一个包)
    ├── __init__.py      # 触发全部内置 op 注册
    ├── swiglu/          # __init__.py(注册 npu+liger) + npu.py + liger.py
    ├── rms_norm/        # 注册 rms_norm + gated_rms_norm;liger 按家族分派变体
    ├── rotary/          # 注册 rotary + multimodal_rotary
    ├── geglu/           # 注册 geglu(只 liger,gemma 系)
    ├── moe/             # 注册 moe_experts + moe_block
    ├── sdpa_attention/  # 注册 sdpa_attention + 自定义 installer(写 transformers 全局注册表)
    ├── fla/             # 注册 fla + 自定义 installer(逐实例 patch,需要 model)
    └── ep/              # EP experts GMM(原有,不动)

builtin.py / liger.py / npu_impls/ / liger_impls/ 全部删除(impl 本体 git mv 进 ops/<op>/<backend>.py,内容不改)。

关键设计点

  • 注册层只登记 op → backend 实现,替换目标不注册——收进 config.py 一张纯数据 mapping;新增家族 = 家族表加一行 tuple
  • 没有 Backend 类:backend 只是 implementations dict 的字符串 key;平台/依赖判断统一收进 KernelImpl.available(),返回 (bool, reason) 进日志
  • 惰性加载:注册阶段只有 lazy_import('pkg.mod:attr') 字符串工厂和 find_spec 探测,import twinkle.kernel 不触碰 torch_npu / liger / transformers
  • "注册有什么"与"默认选什么"正交moe_experts 两个 backend 都注册,但默认链只写 ('npu',)(liger 类替换会挡住 CANN grouped-matmul 快路径,吸收旧 _prefer_cann_on_npu drop 语义);qwen3_5 的 rope 数值不兼容 liger,由 config 链只写 ('npu',) 排除
  • KernelImpl.load(target):liger RMSNorm 是一族实现(gemma/gemma4/qwen3_5 变体,选错 → NaN),load 接收 mapping target 按家族分派

行为变化

  1. llama/gemma 家族在 NPU 上:装了 liger 时默认配置现在会生效(旧版 kernelize(model) 对这些家族不动);Qwen 家族替换结果与旧版完全一致(golden 证明)
  2. cookbook --enable-liger 语义收窄:不再切逐层 kernel,现在只门控 fused-CE loss;强制 Liger 逐层需自建 ('liger',...) 链(见上面例 ②)
  3. CUDA 上 kernelize(model):从 no-op 变为应用默认配置(装了 liger 即生效,未装 ≈ no-op 且全 DEBUG 无噪音);cookbook 入口仅保留 TWINKLE_TORCH_BASELINE=1 逃生门

Test plan

验证对象 结果
pytest 选择/回退/日志分级/installer 协议 + impl 数值对齐 59 passed
真实训练 35B MoE EP+FSDP2+LoRA 全链路 EXIT=0,loss 2.99→1.62,WARNING=0

…try/config/ops split)

Rework kernelize into a register-dispatch model, replacing the
hard-coded builtin/liger module pair:

- registry: ops REGISTER themselves with per-backend impls (lazy load +
  availability check); KernelChoice DISPATCHES to the first available
  backend in the priority chain
- config: DEFAULT_KERNEL_CONFIG as pure data (dotted-path targets only)
  — the mapping declares WHAT to replace and WHICH op; never imports
  transformers/torch_npu/liger_kernel
- core: kernelize() walks the mapping and dispatches each entry to an
  installer — HOW to install is decided by priority
  KernelChoice.installer -> OpDefinition.installer -> default_installer
  (class swap for nn.Module targets, setattr for functions/methods);
  transformers.* family-missing skips are DEBUG on the default path and
  WARNING (typo hint) for explicit mappings
- ops: swiglu / geglu / rms_norm / rotary / moe / sdpa_attention / fla
  moved from npu_impls & liger_impls into per-op packages; new ep op
  (NPU grouped-matmul) replaces the inlined EP expert loop in
  expert_parallel.py
- moe_experts default chain is ('npu',) — LigerExperts is opt-in only,
  absorbing the old _prefer_cann_on_npu drop semantics
- hub(): HubRef values resolve lazily via the optional kernels package

Cookbooks: kernelize is gated only by the torch-baseline escape hatch;
--enable-liger is narrowed to gating the fused-linear-CE loss. EP utils:
advanced indexing instead of index_select (aclnnIndexAdd broken on some
torch_npu/CANN), sync D2H copies for split sizes.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@werwrewe
werwrewe marked this pull request as draft July 30, 2026 08:27
Formatting only, no behavior change: yapf + isort + double-quote-string
-fixer per .pre-commit-config.yaml; fixes E501 in core.py.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant