事情本身不复杂,线索都在日志里。自己写的推理脚本把一个 batch 内所有样本 pad 到最长 2048,短样本其实只有 80 token,实测吞吐比逐条跑还低 40%,GPU 利用率看着很高,其实都在算填充。

第一反应是「batch 内长度差异越大浪费越多」,验证下来不成立。

排查方向其实有限,先把可能性排一排。填充位置的注意力和前向计算照常执行。也可能是有效 token 占比低,利用率数字被撑高。

少数情况下是没有按长度分桶就直接组批。

动手时先统计一个批次里的长度分布

python -c "print([80,120,2048,96])"

随后按长度分桶再组批

python -c "print('buckets 128/256/512/1024/2048')"

接着用 attention_mask 屏蔽填充位

python -c "import torch;print(torch.ones(2,4,dtype=torch.long))"

末了对比分桶前后的每秒有效 token

python bench_throughput.py --bucketed --duration 60

那几天正好在做别的变更,先把时间线对齐才敢动手。

恢复的判定其实很直接。每秒有效 token 提升三成以上,总耗时缩短,输出内容与不分桶时逐字一致。

分桶会在桶边界引入等待,桶越细收益越大但调度越复杂,先粗分五档看效果。报错文案容易误导,真正的原因往往在相邻那一层的日志里。

作者 9823189