工程
我们如何让 AI beatmap 生成快到可以即时互动
一整套 osu! mania mapset 只需约 20 秒 GPU 时间,而不是一分钟甚至更久:一次 ManiaGAI 请求的时间花在哪里,以及从浏览器到批量生成难度、省下这些时间的各项决定。
要等好几分钟的生成器,只是一个批处理任务。你提交一首歌,去做别的事,回来再看它做出了什么——如果不满意,换一种风格或换一组难度就得再等一轮。几秒内就能给出结果的生成器,才是一件工具:你试一试、听一听、调一调,再试一次。
正是因为这个差别,在 ManiaGAI 第 2 版中,我们把延迟当作贯穿整个系统的设计约束,而不是留到最后才去优化的东西。第 1 版生成一个 mapset 要一分钟甚至更久。第 2 版在一首普通的歌上生成一整套 mapset,只需约 20 秒的 GPU 时间——通常在 13 到 25 秒之间,取决于歌曲和所要求的难度。下面是这些时间花在了哪里,以及我们靠哪些决定省掉了其余的时间。
一次请求的剖析
- 你的浏览器
- timing ∥ 音乐特征
- Planner
- Noter
- 校验
- 下载
v2 上线时实测的一次生产环境运行:一首 2:50 的歌,5 个难度,在 NVIDIA T4 GPU 上——总共 12.7 秒。
| 阶段 | 耗时 |
|---|---|
| timing + 音乐特征(并行运行) | 3.8 s |
| 规划(所有难度) | 1.0 s |
| 音符生成(全部 5 个难度) | 6.1 s |
| 整次运行 | 12.7 s |
v2 上线系统(2026 年 10 月 1 日)上处理一首歌的 GPU 计算时间,不含上传、排队和下载。
1. 在任何人按下“创建”之前,先把音频处理好
每首上传的歌,在模型能使用之前,都必须先解码、做响度标准化,再重新编码成统一的格式。放在服务器上,这是几秒钟的工作,GPU 只能干等。在 ManiaGAI 中,这一步在你的浏览器里完成,你一选好文件就开始——这时你还在挑键数和难度。等你按下“创建”时,这项工作通常已经做完了。如果在较慢的设备上准备歌曲超过 20 秒,就改由服务器接手,所以这项优化永远不会拖慢任何人。
2. timing 和音乐理解同时进行
在规划开始之前,需要知道歌曲的两件事:它的 timing 网格(来自开源的 Beat This 模型,加上我们自己的网格拟合),以及它的音乐特征(来自 MERT 音乐模型)。两者互不依赖,所以也不必互相等待:GPU 运行 Beat This 和 MERT 的同时,CPU 也在进行网格拟合。在上面那次运行中,整个理解阶段用了 3.8 秒。
3. 一次生成所有难度
一个 ManiaGAI mapset 最多有十个难度。一个接一个地生成,耗时就会乘以难度的数量。所以,所有要求的难度都会在同一次批量 GPU 运算中一起规划和生成——它们共享同一首歌和同一组音乐特征,只有目标不同。这就是上面那次运行中,五个难度的音符生成能在 6.1 秒内完成的原因。
4. 细化整张谱面,而不是逐个音符地写
音符模型是非自回归的:它在固定轮数的细化中产出整张谱面,而不是从歌曲开头到结尾一次一个 token——所以无论谱面最终有多少音符,模型的步数都是固定的。(模型为什么这样工作,是另一个故事:Planner + Noter 一文。)
5. 从快照启动,而不是从零开始
生成运行在 serverless GPU worker 上,这意味着请求到达时,worker 不一定正在运行。从零开始加载模型、完成预热,会让第一个请求多出好几秒。所以,worker 会从一个内存快照中恢复——这个快照是在所有东西加载完毕并预热之后拍下的——让新的 worker 在约一秒内就绪,而不是把这些时间花在加载模型上。
6. 别在终点线前等待
最后几步过去也会带来额外的延迟。现在,谱面一准备好,可下载的 .osz 就立刻打包完成;生成一结束,完成的谱面就会马上保存到“我的谱面”——即使你已经离开了页面。生成器上的进度显示的是每次生成实际所处的阶段,所以等待从来不是一个谜。
再次提速,谱面完全相同
2026 年 10 月 8 日,我们上线了生成系统的更快实现:谱面的生成速度提升到约两倍,而谱面完全相同——相同的输入,相同的输出。我们为这次改动定下了一条硬性规则:任何改变了模型输出内容的提速,都算是模型改动,必须按模型改动来评估。变短的只有等待时间。
速度带来了什么
速度改变了 ManiaGAI 的使用方式。它让尝试第二种风格或另一组难度只需几秒钟,让“先预览再下载”的流程变得自然,也让免费方案得以持续运行。这也是为什么我们优化的是整个请求——从浏览器到下载——而不只是模型本身:玩家感受到的是等待,而不是 GPU 时间。