0.64 秒开口,还不等于流畅对话
一次本地语音实验,把我们从内存保护带到了完整的声音,也让另一个瓶颈变得清楚。
今天有一个很容易被写成好消息的数字:0.64 秒。
在小米 15T Pro 上,已经预热的语音引擎大约这么久就能交出第一段音频。但如果就此写下“iris 的实时语音做好了”,那会是一个错误的结论。
这篇记录的是实验程序,不是手机上已经安装的 iris。我们没有把这套引擎接入应用,也没有替换当前的声音。
先让一句话完整地说完
我们保留了选择的 Qwen3-TTS 1.7B CustomVoice / Serena,没有换成更小的模型,也没有把推理搬到服务器。Luna 负责构建,实际推理发生在手机上。
此前,模型已经能够加载,第一次生成却一直停在内存保护上。继续放宽限制并不能告诉我们问题在哪里。这次,我们开始记录预处理缓存和临时分配。
对照组在第一次文本预处理期间,已经积累了约 2.28 GiB 的 BF16 打包权重缓存,随后再次触发原定的 3.5 GiB 匿名内存加换页上限。那是主动保护停止,不是一次系统内存崩溃。
这些打包结果不会被修改。于是我们保留原来的打包计算,把结果放进只读文件映射,逐字节核对,再释放原来的堆内存副本。
这次,中英文终于都完整生成了。采样到的匿名内存加换页峰值约 1.49 GiB,没有触发原来的保护。
这里有一个必须留下的注脚:这不代表整个模型只占 1.49 GiB。 总驻留内存仍在约 6 GiB 的量级。文件映射不是免费内存,而且每个新进程还多了约 2.66 GiB 的缓存写入。
实验工具也会说错话
交叉检查英文日志时,我们发现了另一个问题:上游的“设置语言”函数,不仅设置语言,还会选择那个语言的默认音色。
探针先指定 Serena,再设置英文,实际生成的却变成了 Ryan。更隐蔽的是,探针打印的是自己请求的音色,而不是引擎真正使用的音色。
于是,第一轮英文结果被保留下来,但明确排除在 Serena 的验证之外。我们调整了调用顺序,又加上实际请求日志的检查,重新跑了中英文。
这件小事值得记下来:测试代码也是产品的一部分。它不仅要能给出数字,还要证明自己测到的是我们想测的东西。
快速开口和持续说话,是两件事
修正后的结果是:
- 中文热状态:3.12 秒的音频,生成用了约 6.14 秒;首段音频约 0.64 秒。
- 英文热状态:2.64 秒的音频,生成用了约 5.18 秒;首段音频约 0.64 秒。
- 冷状态的首段音频仍要等约 24 秒,此外还有约 10 秒的模型加载。
这里的热状态,是同一进程里重复同一句短语,已经命中了缓存;它不是任意长句或持续对话的承诺。我们检查了完整输出、非静音和无削波,没有把这些机器检查当作人工听感通过。
开口已经可以很快,后面的声音却供应不上。生成一秒声音仍大约需要两秒,仅靠增加播放缓冲,只会把问题变成更长的等待。
这次真正得到的进展
我们从“生成之前被保护机制叫停”,走到了“能够完整测量生成过程”。内存问题有了具体的改动,剩下的速度问题也有了真实的数字。
下一步要看逐帧生成和解码。持久化预处理缓存或许能改善冷启动,却不会自动解决持续生成太慢的问题。
今天的决定是:保留这次实验,不急着装进 iris。声音有特色很重要,但它也得在你愿意等的时间里接住一句话。