Perf moe input generation - #714
Merged
Merged
Conversation
generate_rowmap_input_value 原先在 (token, expert) 上开双层 python 循环逐格赋值, 迭代次数为 seqlen x num_experts,1M 级 case 达 3000 万次。 改为两步向量化: - present 矩阵按 topk 列 scatter 标记 (token, expert) 是否有效,循环次数与 seqlen 无关 - expert 内的流水号等于 present 的列向 exclusive cumsum,替代原来手工维护的 per-expert 计数器 输出与原实现逐元素一致(含 topk > num_experts、unzipped == seqlen 等边界)。 最大 case 该步骤 numpy 后端 92.5 s -> 1.2 s;paddle GPU 后端由外推 2.2 h -> 0.09 s, 使 --use_gpu_mode 对 1M 级 moe_unpermute case 变得可用。
generate_symmetric_input_value 的 (rng.random(shape) - 0.5) * (2 * max_abs) 在大 Tensor 上会连续物化多份整块 float64 eager 临时:bfloat16 的 [1966080, 1024] 输入 目标只有 3.8 GiB,实际要写入约 52 GiB 主机内存,且全程单线程。 元素数超过 1<<24 时改走 _chunked_symmetric_numpy,按首维切块生成 + 原地运算, float64 临时峰值从 3 x 15 GiB 降到 32 MiB,同时消掉 1M 级 case 的主机 OOM 风险。 NumPy backend 的随机流是顺序流,一次 random(shape) 与按首维切块的多次调用消费同一 串数,因此输出与原表达式 bit-exact,不影响 --retest 复现、已调好的容差配置和历史 bug 的 bisect。gate 限定 numpy backend,torch/paddle backend 行为不变。 单 case 默认 accuracy 路径 121.95 s -> 47.3 s(含上一个 commit 的收益)。
feixi139
force-pushed
the
perf-moe-input-generation
branch
2 times, most recently
from
August 20, 2026 09:27
21b4093 to
010dfa7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
改动一:向量化 rowmap 输入生成
涉及文件:
tester/input_generation/generation_rules.pygenerate_rowmap_input_valuezipped_expertwise_rowmap的构造原先在(token, expert)上开双层 Python 循环逐格赋值,对每个格子单独nonzero判断该 token 是否路由到该 expert,再手工维护num_experts个计数器发号。迭代次数为seqlen × num_experts,本文件最大 case 达 3000 万次。改为两步向量化:
present矩阵按 topk 列 scatter 标记(token, expert)是否有效,循环次数为 topk,与 seqlen 无关;present的列向 exclusive cumsum(cumsum(present, axis=0) - 1),等价于原来“按 row 升序、每遇到一个有效格子自增一次”的语义。索引用
rule.ops.nonzero(column >= 0)[0]而不是布尔掩码,避免依赖 torch/paddle backend 的布尔高级索引;全部走rule.ops.*,三个 backend 通用。seqlen=3759104, num_experts=8, topk=4)该步骤GPU 那一栏是这个改动的关键:
旧实现每次内层迭代都有一次
nonzero+ 一次取值判空导致的 device sync + 一次 setitem,实测 2095 us/行。这是
--use_gpu_mode=True之前对 1M 级moe_unpermutecase 不可用的直接原因(主机侧构造阶段跑不完 / 被 OOM kill)。2.2 h 是按前 300 行线性外推,未实跑全量;NumPy 那一栏两个数字均为全量实测。
一处需要 Reviewer 注意的语义变化
expert_counts从:改为:
即由“计入一行内重复出现的同一 expert”改为“去重”。
当前
generate_expert_routemap_input_value填充的值为:且各 column 互不相同,因此一行内不会出现重复 expert,两者对所有现有配置结果相同(已在
topk > num_experts的边界上验证)。去重后也与“每个
(row, expert)只发一个号”的实际写入行为更自洽——原实现在假想的重复场景下算出的 offset 会与实际发号数量不匹配。改动二:大 Tensor symmetric 输入改为分块生成
涉及文件:
tester/input_generation/value_generators.pygenerate_symmetric_input_value原表达式:
在大 Tensor 上存在四层内存放大。
以
bfloat16 [1966080, 1024](20.1 亿元素,目标 3.8 GiB)为例:rng.random(shape),未传 dtype →storage_dtype为 None → 跳过 astype- 0.5eager 临时* (2 * max_abs)eager 临时_INPUT_INTERMEDIATE_DTYPES映射为 float32)全程单线程(MT19937 串行 + NumPy elementwise 无并行)。
backend.py:_direct_float_dtype的规避快路径只在:时生效。
由于 bfloat16 映射到 float32,因此永远不会命中该优化路径。
当元素数超过:
时,改走新增的:
_chunked_symmetric_numpy实现。
具体策略:
这样可将 float64 临时峰值从:
降低到:
同时消除 1M 级 case 在非 GPU mode 下的主机 OOM 风险。
随机流保持不变
本次改动不改变随机流。
NumPy backend 使用的是
RandomState(MT19937)顺序流。random_sample(size):因此:
与:
调用 N/k 次后再拼接,
得到的随机序列完全一致(bit-exact)。
这是有意保持的行为:
保证同一条 config 永远生成 bit-identical 输入。
虽然改成:
并直接生成 float32(实测约 10.1 s)会更快,但会导致:
--retest无法复现历史失败;accuracy_manual_threshold_config已调好的阈值失效;paddle_bitwise_knows基线失效;本质上等价于一次全局 baseline reset,因此未采用。
Backend 兼容性
启用条件:
其中:
InputNumPyRandomState无name属性;InputConfigRandomState无name属性;因此默认按 NumPy 路径处理。
而:
因此:
torch / paddle backend 行为完全不变。
由于:
会形成循环依赖,
因此这里采用 duck-typing,而非
isinstance判断。