延伸向远方的铁轨与两侧的树林

一个打包任务卡了半年

2026 年 2 月,社区维护的 Debian 13 龙架构移植(loong13)在打包数学软件 normaliz 时遇到一件说不通的事:软件自带的测试跑不完,卡在一个自己走不出来的死循环里,整个打包超时。维护者王淼顺着代码往下查,问题指向一个再普通不过的操作——OpenMP 的 #pragma omp atomic,多个线程往同一个共享计数器上累加。

这段代码的意图很简单:并行处理一批数据点,每处理一个就把计数器加一,某一轮结束后计数器等于总数就退出。可实际上,计数器最后的值总比实际处理的个数少一点,少多少每次都不一样。程序认定自己没干完,就再来一轮。用调试器看,列表里所有点其实都已标记为处理完成——那个少掉的增量凭空消失了。

把找最小复现这件事交给 AI

第一轮排查卡在最关键的一步:没法把问题缩小成一段人能看懂的最小代码。他们先怀疑内存序(龙架构是弱内存模型,这类问题以前出现过),反汇编确认编译器确实生成了 LoongArch64 的 amadd.d 原子加指令,又加了两组计数器做对照——四个计数器的值各自随机对不上。线索指向 "原子加指令偶发地不原子",但一个只做原子加的简单程序怎么写都复现不出来。

半年后重新捡起来,方法换了:不让人去找,让 AI 去找最小复现,人负责指方向。AI 第一次读代码没有结论,提示 "问题只在龙架构上出现" 也仍然没有定论;直到把 "我们已经把问题定位到原子加指令" 这个事实直接告诉它,它才注意到处理函数里的 memcpy——glibc 会按当前硬件能力挑选最快的实现,龙架构支持 LSX/LASX 向量指令,正是这段向量化的内存拷贝触发了丢失更新。大约两天后,一份能稳定复现的最小程序交了出来;顺带确认 CAS 指令 amcas 在同样条件下也出问题。

修复只用两周,代价是一个手册里没有的位

8 月 26 日邮件报告龙芯,9 月 9 日收到测试固件——两周。3A6000 与 3C6000/S 在测试中恢复正常,龙芯方面称固件预计国庆(10 月 1 日)前发布。修复方式出人意料地简单:把 MCSR24 的第 13 位置 1。MCSR24 是手册中没有描述的内部寄存器。测试显示性能损失很小,单核不受影响,多核略降。暂时无法刷固件的用户,也可以在 Linux 内核里直接写这一位,效果等同。

软件层面要规避,只能让编译器改用带数据屏障的原子指令或 LL/SC 循环,而对已有二进制来说,这意味着全部重新编译。

三点值得记住的

第一,CPU 勘误不是哪一家独有的问题:各家厂商都有一长串勘误列表,多数无害,少数致命,区别只在于有没有人在它炸掉真实系统之前把触发条件钉死。第二,半年里真正推动进展的只有两天,其余时间都在确认现象、缩小条件、扩大测试范围、等固件——这是硬件常态,不是效率问题。

第三,AI 在这里的角色很具体:它不是靠 "更聪明" 破局,而是在人被复杂代码绕晕、又被明确指到范围之后去试错;人负责判断方向、核对事实、承担责任。

对做企业数字化的启发

如果系统跑在国产化环境里,出现过 "偶发、不稳定、复现不了" 的故障,这条新闻的流程值得抄:先把现象做成可重复的测试(哪怕缩不到最小),再做对照实验,最后才猜根因。没有对照物就下结论,往往白改一轮代码。

另一层启发是怎么用 AI:这次它收到的不是 "帮我修个 bug",而是一句被反复收窄的陈述——"问题只在这一种 CPU 上,已经定位到这一条指令,请给我最小复现"。输入收得越具体,命中率越高;扔一句 "帮我看看为什么卡住",多半只能换回一篇看不出问题的长分析。