代码、算力与抽象:三十年软件工程实践的回忆与感悟
导言
在参加北京智源人工智能大会时,听到发言者提及操作系统中的“原语(Primitive)”概念。这一术语促使我联想到在数学与计算机领域同样高频出现的“算子(Operator)”。
在计算机体系中,原语侧重于底层的原子性与系统稳定性,是不可分割的原子操作;而算子在数学中代表映射与变换规则,在深度学习框架中则具象化为多维张量计算的基本单元。两者的共同点在于,它们都是构建复杂系统时的基本操作单元。
当前大模型技术的快速迭代,不仅依赖上层算法的创新,更取决于底层操作系统原语的支撑以及算子在不同硬件架构上的执行效率。作为一名自1995年起便在一线从事研发工作的技术人员,从传统的底层开发到如今的AI系统搭建,这些概念的交织引发了我对过往工程实践的系统性梳理。
回望三十年的技术演进,工具、语言和平台不断更迭,但其底层的工程规律却始终保持着惊人的连续性。如果我们拉长视阈,会发现整部软件工程史实际上沿着一条清晰的轴线在演进:抽象层次的不断上升。
在这个演进过程中,优秀的工程师并不是那些记住了最多API的人,而是掌握了跨越时代而依然成立的工程思想、并能在新的抽象层上驾驭复杂性的系统思考者。
第一章:面向硬件编程与“时空折敛”的哲学
1995年,我毕业于大连理工大学计算机软件专业。当时,大连理工大学在计算机软件及应用领域具备扎实的教学与科研基础。然而,受限于当时的区域产业结构,毕业生的就业去向多是国有大中型企业。三十年过去,当年计算机系的100多名同学中,至今仍在一线从事技术研发工作的已寥寥无几。
九十年代中期的上机环境具有鲜明的时代特征。由于当时微机设备造价高昂,学校机房为了防尘与防静电,要求上机人员必须更换拖鞋并穿着特定的白大褂。
学生使用的多为无硬盘的IBM PC/XT或AT兼容机,依赖5寸软盘启动系统并存储代码。在640KB常规内存限制和单色显示器的硬件条件下,资源的精细化管理是每一项编程任务的前提。
学校的课程设置侧重于计算机底层原理与系统化训练。基础课程涵盖了8086/8088汇编、Z80汇编以及Pascal语言。在C语言的学习上,我主要通过自学研读了Dennis Ritchie与Brian Kernighan合著的经典著作 The C Programming Language(K&R),奠定了我往后几十年编程的思维底色。
在汇编语言课程实践中,我曾编写过一个万年历程序。在8086处理器时钟频率较低、除法指令执行周期较长的限制下,我采取了空间换时间的策略:
通过翻阅图书馆的历法资料,整理出400年内的日历变化规律,将其抽象并静态编码为一张查找表,运行时通过基址变址寻址直接获取结果,规避了高开销的除法与模运算。
从技术哲学的高度审视,“查表法”的本质,是把运行时的计算转换为编译期或开发期的预计算,即用存储资源换取计算资源。
这一“时空折敛”的规律,贯穿了整个计算机演进史:
三十多年前在绿屏单色显示器前手写查找表的下午,与如今硅谷工程师盯着Loss曲线等待模型收敛的夜晚,其背后的物理本质与工程直觉完全同构。
第二章:面向网络编程与数据边界的确定性追求
随着个人计算机的普及与互联网技术的萌芽,二十世纪初的软件开发步入了“面向网络编程”的阶段。在这个阶段,如何建立不同计算实体之间的契约,成为了核心的技术命题。
2003年,在设计一个跨平台的C/S架构系统时,由于面临紧迫的工期,且行业内SOAP(简单对象访问协议)尚无成熟的开源实现,我独自在1个月时间内完成了SOAP协议的底层自主实现,累计编写了约40,000行C和Java代码,保障了C客户端与J2EE服务端之间的无缝通信。
在解决跨网络数据传输的可靠性问题时,我利用原生Socket接口设计了自定义的应用层协议以实现大文件的断点续传。
在进行这项底层协议设计时,我当时在知识体系里其实并没有网络编程中所谓的“粘包”这个概念。 面对不可靠的网络流传输,我完全是凭着最朴素的工程直觉去寻找解法:既然网络是一股无边界的“水流”,那我就必须在流中设计出边界明确的“水桶”。
于是,我设计了一套基于实际数据包计数、根据实际结果精准控制读取边界的机制:
发送端在发送数据时,先老老实实算出Payload的字节数,并将其作为4字节的包头写在最前面;接收端启动后,雷打不动地先读取这4个字节,拿到长度 后,再不多不少地从缓冲区中读入 个字节。
直到很多年后,在与同行进行技术复盘和行业交流时,我才豁然开朗:原来我当年为了解决网络不确定性而手搓出来的这套“计数机制”,就是行业标准的长度前缀机制(Length-Prefixed Framing),并且它在设计源头就以最优雅的方式,直接规避了让无数后辈开发者头疼不已的“TCP粘包”问题。
这不仅是一个具体的编程技巧,更揭示了软件工程中一条颠扑不破的底层哲学:好的设计自然带来好的结果与高品质;如果系统在设计期就留下了缺陷,企图在后期依靠繁重的测试、高昂的运维和售后服务去补偿,几乎是不可能的。
“质量”是设计出来的,而不是“测试”和“服务”出来的。如果协议底层的物理边界在架构阶段就是模糊或错误的,那么后续无论编写多么庞大的测试用例,或是提供多么频繁的售后运维和修复补丁,都只能是治标不治本、拆东墙补西墙。优秀的架构师应该在设计源头确立高品质的基础,而不是指望后期的工程环节去给坏设计买单。
进一步来说,数学已经证明,没有任何程序或软件是绝对不存在缺陷(Bug/Defect)的。无论是经典的图灵停机问题还是莱斯定理(Rice's Theorem),都从理论上判定了“零缺陷软件”在物理世界的无解性。
然而,我们三十年的工程实践却向我们展示了另一个维度的实践哲学:一个好的软件,即使它体内潜伏着某种缺陷,但在它的整个生命周期内、直到它最终下线退役,这些缺陷都与用户“从未相遇”。
这正是防御性设计与边界设计的魅力所在。我们无法消灭软件世界里的全部熵增,但我们可以通过清晰的模块边界、异常熔断和高内聚的设计,将潜在的Bug死死限制在极难被触发的冷代码分支、死代码路径或自愈容错层中,确保核心主干运行路径的绝对光滑。这种“不遇”的工程艺术,才是系统架构师追求的终极价值。
在软件工程中,“粘包”本质上是一个数据边界问题(Data Boundary Problem)。而在不确定的连续流中定义确定性的边界,不仅是通信物理层的问题,更是贯穿系统软件层、应用层、乃至今天大模型智能应用层最核心的共性挑战:
无论是底层的Socket字节流,还是高层的智能体API调用,工程思想的内核始终如一:如何在不确定、连续的信息流中,定义一个“可确认、可回滚、可验证”的边界,从而为系统提供可预测的安全保障。
此后,在对Lotus Domino/Notes办公自动化系统进行重构时,面对复杂的上下级组织架构及跨域通讯需求,我利用系统内置的“连接文档(Connection Document)”机制重新规划了邮件路由拓扑。这一设计在打通上下级跨域通讯的同时,利用其域隔离机制卡死了安全边界,实现了“四两拨千斤”的隔离控制效果。系统设计的优劣,往往取决于边界划分的清晰度。
在那个时期,技术栈的跨度极广,从BASIC、Fortran、dBase、FoxBase/Pro,到VB、VC、Delphi。这种“啥都得自己干”的经历虽然繁杂,但回过头来看,它避免了开发人员受限于某一门特定的语言或框架。只要底层逻辑相通,语言和框架只是招式的改变。 这种全局系统观,使我们在面对复杂的系统性瓶颈时,能快速定位出问题究竟出在网卡配置、数据库瓶颈、网络规划还是代码逻辑上。
第三章:大学计算机专业培养路线的论争与隐忧
在大学计算机专业的建设中,长期存在着一个核心论争:大学应该培养“造计算机的人”,还是“用计算机的人”?
这一争论的本质,已经超出了单纯的教育范畴,它对应着:
• 科学(Science)与 工程(Engineering) 的张力 • 创造工具(Creator)与 使用工具(User) 的角色划分 • 第一性原理(First Principles)与 应用经验(Empirical Experience) 的思想分野
在早期的精英教育阶段,课程设置几乎完全向“造工具”倾斜。编译原理、操作系统内核、微机接口、计算机体系结构等硬核课程是绝对的主角。
然而,随着互联网和信息化浪潮的爆发,为了迎合就业市场,大部分高校的教学重心逐渐向“用工具”偏移。课程表里充斥着主流的Web框架、应用级中间件以及API调用。这种向“用工具”倾斜的培养模式,在产业高速扩张期带来了巨大的人口红利,支撑起了中国繁荣的互联网应用和商业生态。然而,当面临技术底层“卡脖子”和国产算力生态重建等历史性任务时,这种模式的弊端便暴露无遗。
事实上,一个成熟的产业结构并不是非此即彼的二元对立,它应该是一个层层递进的阶梯:
真正的分歧,并不在于我们是在“造”还是在“用”,而在于我们是否培养了“理解并构建抽象层的人”。
无论是写编译器、写数据库、写核心框架,还是今天在大模型之上写AI Agent,其本质上都在做同一件事:在庞杂的系统复杂度之上,构建一层新的抽象,从而对上层提供简单、一致且健壮的接口,同时屏蔽下层的琐碎、多变与脆弱。
一个国家的软件产业能够繁荣,依靠大量在现有抽象层上“造应用”的工程师;而一个国家的软件技术能够突破天花板,则必须拥有一批能看透抽象、并能不断“构建新抽象层”的工程师。如果我们的教育仅仅教会学生在现成的抽象屏障(如调用API、调库)之上打转,而没有教会他们去击穿这层屏障、甚至向上升起一层新的屏障,那么我们在面临算力体系重构等生态挑战时,就会被困在底层硬件与高层应用的巨大鸿沟之间。
第四章:从手工业到工业化迁移——专家体系与组织能力的沉淀
在当前的AI算力国产化进程中,将大模型生态从英伟达(NVIDIA)的CUDA平台迁移至国产GPU/NPU平台(如昇腾CANN架构),是一项前所未有的工程战役。
这项工作的难点在于,大模型所依赖的高性能算子数量极其庞大,且需要针对国产卡独特的硬件拓扑进行底层的适配。在这一现实场景下,我们面临着一个典型的人才结构矛盾:一方面是数量极其有限的、懂芯片体系结构和编译器的顶尖系统级专家;另一方面则是规模庞大、但对底层硬件缺乏深刻理解的普通工程技术人员队伍。
回顾软件工程三十年的发展史,其主线之一,就是如何将个体能力转化为组织能力:
在2003年,面对技术空白,我能够靠着极致的个人毅力,独自在一个月内写完4万行C++与Java代码去硬搓一个复杂的SOAP通信协议。那是一个属于“个人英雄”的时代,生产力高度绑定在少数极客程序员的智力水平上。
但是,面对今天大模型生态成百上千个底层核心算子、几十万行硬件级深度重构的迁移任务时,再强大的个人英雄主义都会在工程复杂性的汪洋大海中失效。依靠普通工程师去逐个手工敲代码的“手工业编程”模式已无法走通。软件工程在这一历史时期表现出的高级形态,在于将“手工业开发”升级为“工业化迁移”:
在这套工业化体系中,极少数的系统级专家不再直接去手写每一个具体的业务算子,而是负责“画图纸和修脚手架”。他们将最复杂的硬件寻址、Vector-Cube流水线编排和双缓冲管理等硬件级微观控制,封装进编译器、领域专用语言(DSL)或高性能算子模板库(如catlass)中。
工程经理则在此之上建立起严密的自动化测试和精度对齐流水线,将复杂的移植工作拆解为可量化、可验证的标准工序。在专家铺设好的标准模具内,庞大的中端工程师队伍通过规范化的“人海战术”开展流水线作业。
优秀软件工程的本质,就是把原本无法复制的个人天赋与专家内功,通过工具化和流程设计,沉淀为可以规模复制的组织能力。 这才是国产算力突围的工程学正道。
结语:驾驭复杂性的技术哲学
回顾三十年的技术演进,从汇编语言到大模型,从软盘启动到万卡集群,看似变化的是工具、语言和平台,真正未曾改变的,是人类不断通过构建更高层次的抽象来驾驭复杂性的过程。
软件工程三十年的历史,并不是不断发明新工具的历史,而是不断发明新抽象层的历史。
我们当年用万年历查表法将“时空折敛”抽象为数据,用SOAP将“分布式通信边界”抽象为协议,用长度前缀将“不确定的字节流”抽象为结构体,用Domino路由将“复杂网络拓扑”抽象为连接文档。
如今,芯片专家用编译器和CUDA将晶体管抽象为算子,大模型专家用参数权重将人类世界的海量知识抽象为网络模型,而智能应用开发人员则正在用Prompt、提示工程和Workflow将大模型抽象为能够自主调用工具的智能体(Agent)。
这一部技术演进史,就是人类在混乱的熵增世界中,不断筑起一层层“抽象盾牌”的历史。每筑起新的一层,人类就可以站在更高的安全边界之上,去眺望、探索更远处的物理世界与逻辑疆域。
酸、甜、苦、辣、咸,五味杂陈,不仅是复杂的系统工程实践的常态,也是生活的本色。三十年代码与岁月的沉淀,最终都化作了面对新技术浪潮时,不畏难、不退缩,带领团队流水线渡江的从容与底气。在智能时代的门槛前,我依然会守在代码的一线,去探寻下一个抽象层上的工程之美。
夜雨聆风