前段时间我给高并发内存池做收尾工作时,跑了一轮多线程benchmark。4 线程的时候数据还不错,但当我加到8个时,结果吞吐量不升反降。我第一反应是代码有bug,查了半天,发现没有,问了AI才知道真正的原因是线程太多,大家互相抢资源、同步开销上去了,多出来的算力全花在内耗上,资源在加,事情在变慢。
后来在老师课堂上了解到《人月神话》这本书,空闲时间就看了看,结果没读几页就看见了Brooks的名言:“往进度落后的软件项目里加人手,只会让它更落后”。我当场就乐了,这说的不就是我的情况吗,于是我就看完了整本书。下面我就聊聊它讲了什么,以及为什么我会被一本 20世纪的书感兴趣。
一、麻烦分两种
在我看来,书里最值钱的概念之一,就是把软件的复杂度劈成两半:本质复杂度和偶然复杂度。这两种复杂度说白了,一种是问题自带的,避免不了,而另一种是自己用的工具和自己的习惯带来的。我用自己的仿谷歌开源 tcmalloc 高并发内存池项目(https://gitee.com/friend-a188881041351/high_concurrency_memory_pool.git)来举例说明。
内存池分三层:每个线程自己有块缓存(Thread Cache),不够了它就找中心层(Central Cache)要,再不够就找页层(Page Cache)要。并发下的正确性:锁加在哪、伪共享怎么避、什么时候该批量搬内存,这些才是本质复杂度。后来我在找"8 个线程反而慢"的问题时,把嫌疑锁定在一个结构体的伪共享上(说实话我现在也不敢百分百确定,可能还混着锁竞争的成分),这种问题没有任何工具能替你想,只能自己思考。
而另一边:我给变量命名想了十分钟,把日志格式改了三版,还要跟刷屏的 cmake 报错搏斗,这些就是偶然复杂度,是拖累,但不是问题本身。Brooks 在书里说,高级语言、分时系统这些当年的新技术,砍掉的就是偶然复杂度这一半。放到今天,AI 辅助编程干的是同一件事。为什么这么说我后面会提到。
二、往落后项目里加人,为什么是火上浇油
Brooks 给了个公式:n 个人一起干活,沟通路径有 n(n-1)/2 条。3 个人是 3 条,5 个人就是 10 条。人越多,光"对齐"这件事吃掉的时间就越吓人。这个公式我是靠组队大作业读懂的。三个人改同一份建表脚本,A 在自己电脑上改了字段类型,B 在另一台机器上把默认值也改了,合并那晚三个人对着 git 记录大眼瞪小眼,谁也说不清哪版是对的。
书里给的解法是:搞小团队,让一个"主刀医生"负责守住设计,设计思想必须出自一两个人,其他人只负责实现。这话听着不太舒服,但我回想一下那些烂尾的项目或小组作业,发现往往不是谁不行,而是每个人都有一套合理的设计,但仨合理加在一起就是不合理。
三、第一版就是用来扔的
书里有句话我抄在了笔记第一页:Plan to throw one away; you will, anyhow. 翻译是:计划着扔掉第一个版本吧,反正你终归会扔。这话说得有点狠,但是是对的。
词序(Vocabulary Studio)是我一直在维护的PC端Windows下的背单词工具,现在挂着 Gitee 更新源(https://gitee.com/friend-a188881041351/cixu_vocabulary_studio.git)。它的第一版把词库格式直接写死在代码里,我当时想的是先跑起来再说。等真有同学开始用,需求就来了:他们要生词本、要更大的词库、要改记忆算法等等。每加一个功能,我都得去那些硬编码的代码库里刨一遍。第二版我干脆推倒重写。奇怪的是,重写花的时间不到第一版的三分之一。
所以第一版的价值根本不在于能用,而是一次性把自己设计里不合理的地方暴露完了。(大一写的贪吃蛇和通讯录我从没扔过,不是它们写得完美,是压根没人用,重写没有意义。现在想想,那会儿我只是把基础业务功能敲出来了,谈不上做了软件。)
四、写代码是整个项目里最舒服也最不占时间的部分
书里给了张进度表,我第一次看的时候愣住了:规划三分之一,编码六分之一,部件测试四分之一,系统测试四分之一。测试加规划占了四分之三,写代码只占六分之一。而我以前的安排刚好反过来:我兴致勃勃地把功能写完,上交的前一天晚上才草草点两下完事。
为什么我们改功能很快,验证起来就慢得要死?维护词序更新源之后,我算是有体感了。每次我发版,最耗时间的根本不是写新功能,而是确认旧功能没被改坏:词库解析还对不对、断网了下载更新会怎样、旧数据新版本还认不认。这些活儿琐碎,但耗的时间比写功能少不了多少。现在我信了:那张表不是写给 IBM 的,是写给所有低估测试的人的,包括我。
五、AI 时代,这本书过时了吗
在课程要求的提醒下,我思考了AI时代的变与不变,这也是我读完想得最多的部分。
先说不变的。Brooks在《没有银弹》里断言:十年之内找不到任何单一技术,能让软件生产率提高一个数量级。因为真正难的从来不是打字,是想清楚要做什么。AI 恰好是这个论断的最新注脚:它让生成代码快到近乎免费,可需求为什么这么定、模块边界画在哪、概念结构怎么切,一点都没变简单。AI 优化的还是偶然复杂度那本账,本质复杂度却原封不动。
再说变的,我有三点体感。一是瓶颈变了。AI一分钟生成我几天的代码量,于是读懂它、测出它哪里不对,成了新的耗时大头。。二是"加人"有了新概念。让几个 AI 助手同时改一个项目,不用发工资、不开无聊的会,但它们同样会各自理解需求、产出互相打架的代码。沟通成本平方定律没有失效,只是从会议室搬进了上下文窗口。三是概念完整性更贵了。代码便宜到随手可得之后,"整个系统像一个人设计出来的"反而成了珍贵之物。当所有人(和所有 AI)都能写代码,能拍板"到底做成什么样"的人才是瓶颈。
所以我的判断是(个人观点):AI 时代复杂度控制的重心,正在从"控制代码量"转向"控制概念数量"。代码可以无限生成,概念的一致性不能,它还是只能装在一两个人的脑子里。Brooks说的"守门人",五十年后不但没下岗,反而更忙了。
写在最后
这本书最厉害的地方在于:讲的全是六七十年代 IBM 的大型机项目,我却总能在自己身上找到对应。读完后打算给词序下一版先写设计说明再动手,不会一来就用编辑器写代码了。估工期的时候,测试时间按编码的两倍来准备。
谢谢大家。