EP.012 / TRANSCRIPT

“冯·诺依曼架构”:从 EDVAC 到大语言模型逐字稿

程序、状态、控制与执行的关系

FINAL MASTER / release-approved

冷开场:工位已经坐满,工作却还在路上

0:00

二〇一七年,谷歌公布第一代张量处理器的性能分析。这块为神经网络计算设计的芯片里,六万多个计算单元整齐排开,可以同时完成大量乘法和加法。

听上去,它最不缺的就是速度。像一间刚扩建完的工厂,工位、机器和电力都已准备妥当,只要订单一到,产量就该直线上升。

可论文记录了另一幅画面:这一轮要用的权重还没有送到,或者上一轮的中间结果还没准备好,整片计算阵列就会停下来。权重是模型训练后保存、生成时反复使用的大量数值;中间结果则是前一步必须交给后一步的数值。它们不能靠猜,也不能临时拿一份相近数据顶替。

于是,六万多个计算单元同时面对同一件事:不是不会算,而是还不能算。机器最强的部分已经就位,决定下一秒有没有结果的,却是一份尚未抵达的依赖。

这也提醒我们区分两种常被混在一起的速度。峰值能力回答“条件理想时一秒最多能算多少”,实际完成时间回答“从请求进来到结果出来究竟等了多久”。前者可以靠增加工位迅速变大,后者还要把取数据、等待前序结果和交回输出全部算进去。本期关心的是第二种速度。

[001]

引用 [001]

supports

冷开场只保留计算阵列、权重、中间结果与等待,不展开内部队列和存储器名称。

定位 PDF pp. 3-5 and §4: systolic matrix unit, weight / activation paths and weight stalls

查看完整证据关系

做过应用服务的人,大概见过相似的假繁忙:线程池还有空闲线程,请求却全卡在同一条数据库查询上。再加一倍线程,只会多出一批等待者;查询没有返回,后面的业务仍然不能开始。

这幅图能解释“执行者空闲,依赖未到”,却不能解释芯片内部怎样工作。计算单元没有网络连接,权重读取也不是数据库查询。两边共有的只是依赖关系:后一步必须拿到前一步的产物,执行者数量不会取消这个先后条件。

本期要追的,正是这份等待如何形成。答案不只藏在二〇一七年的芯片里。它会把我们带回七十多年前:那时电子线路已经把计算变快,改变任务的速度,却还牢牢握在人手里。

[002]

引用 [002]

context

线程池类比只解释共同依赖未到,正文立即排除服务器与芯片机制等同。

定位 PDF pp. 4-5 and §4: matrix unit waits when required input or weight data is unavailable

查看完整证据关系

第一章:把下一步移进机器

2:11

一九四三年,美国宾夕法尼亚大学摩尔电气工程学院开始建造 ENIAC。它要用电子线路完成原本由人和机械设备承担的大量计算。机器一旦进入运行,速度非常快;可要换成另一类问题,操作人员往往要重新设置大量开关和电缆,安排各部分先做什么、把结果交给谁。

这有点像一座剧场每换一出戏,不只替换剧本,还要拆掉布景、挪动舞台、重新安排演员从哪扇门进出。演出本身可以很快,换戏却成了另一项工程。

ENIAC 并非没有程序,它有自己的程序控制方式。昂贵之处在于,任务步骤有很大一部分仍留在机器外面,凝固在接线、开关状态和人的准备劳动里。电子速度解决了“算得慢”,却把“改得慢”照得更加明显。

[003]

引用 [003]

supports

保留 ENIAC 有程序控制方式的边界,剧场类比只解释换题劳动。

定位 progress report scan pp. 2-3; First Draft §§1.1-2.9 on electronic speed, organs and automatic control

查看完整证据关系

摩尔电气工程学院的团队正在设计下一台机器 EDVAC。J. Presper Eckert 和 John Mauchly 已经讨论新的存储器、数字表示和自动安排步骤。John von Neumann 加入以后,参与逻辑控制、指令编码、用具体问题检验方案,并把此前讨论整理进报告。

他们的工程选择可以压缩成一句话:不仅把要计算的数值存进机器,也把告诉机器“下一步做什么”的指令存进去。换任务时,人不必主要依靠重新接线,而可以修改和装入另一串指令。

剧场也就从“每换一出戏重搭舞台”,变成“在同一座舞台上换剧本”。这个类比到这里为止:机器指令不是自然语言剧本,控制部分不会理解人物和情节。它只会按编码取出动作。类比真正说明的是,改变工作顺序的东西终于可以被保存和替换。

这个变化还让修改变得可以积累。接线方案当然也能记录,但程序一旦成为内容,就能像其他内容一样被复制、比较、修订和再次装入。一次改进不必只留在某次机器布置里,它可以进入下一份程序。计算机的通用性因此不只表现在“能做很多题”,也表现在换题的方法开始具有软件的可重复性。

[004]

引用 [004]

supports

同段固定团队贡献与存储指令动作,剧本类比明确排除理解语义。

定位 progress report scan pp. 2-5; First Draft §§2.2-2.9 and §§15.1-15.6

查看完整证据关系

一条指令进入机器以后,发生的事情并不神秘。控制部分从存储器取出指令,按照它指出的位置取得数值,运算部分完成计算,再把结果写回;控制部分接着取下一条,机器继续向前。

指令和数值能够使用同一套保存与传送能力,不表示它们作用相同。可以想象一间仓库既保存物料,也保存作业单:两者都占货位、都能被取出,工位拿到物料时用它加工,调度员拿到作业单时按它安排动作。机器里没有调度员理解文字,这个类比只解释“做什么”和“拿什么来做”可以一起成为可定位的内容。

如果把视线停在零件名称上,这很容易变成一张教材框图。更值得记住的是四种关系:程序保存了可以更换的安排,状态保存了当前工作走到哪里,控制决定下一步,执行部分完成眼前动作。输入把新内容送进来,输出把结果交出去。

存储器不会理解题目,运算部分也不会自己决定今天算天气还是工资。改变机器用途的力量,来自可替换程序和这几种角色之间的交接。所谓架构,首先是这份分工怎样成立,而不是机箱里每根线必须画在什么位置。

[005]

引用 [005]

context

用程序、状态、控制与执行重述最小动作,不口播原始字母缩写或位格式。

定位 First Draft §§2.2-2.8 and §§15.1-15.6; IAS report §§1.2-1.4

查看完整证据关系

后来,人们用“存储程序”概括这种做法,又用“冯·诺伊曼架构”指向一组更宽泛的关系。这两个名称都带着后见视角:《EDVAC 报告初稿》没有使用“存储程序”这个词,标题页也只列了 John von Neumann。

标题页的一人署名不能抹去 Eckert、Mauchly 和团队的前置工程,也不能抹去 von Neumann 在逻辑组织、检验与写作中的具体参与。把这段历史讲成单人灵光一现,或者反过来讲成一个人只是抄写,都把协作压扁了。

更关键的是,后来的机器很快采用不同的存储器、指令和并行方式。真正延续下来的不是一九四五年的统一施工图,而是一项承诺:程序可以成为可保存、可复制、可修改的对象,同一套硬件因而能在不重搭整座舞台的情况下改变用途。

这项承诺也改变了投入的去向。人们可以把更多时间花在编写、检查和改进程序,而不是为每次任务重新布置机器;同一份程序还可以在同类机器上重复运行。它没有让程序开发变得容易,却让开发劳动第一次能够稳定地沉淀在一个可修改对象里。

[006]

引用 [006]

corrects

后见名称只承担关系框架,明确拒绝单人发明和统一物理蓝图。

定位 First Draft title page and §§15.1-15.6; progress report scan pp. 2-5; IAS report title / Preface; Haigh / Priestley / Rope pp. 4-13

查看完整证据关系

第二章:同一组关系,换到不同尺度

7:02

先把电脑缩小,放进一把智能门锁。芯片里长期保存着一份控制程序:怎样读取按键,怎样核对密码,什么时候驱动锁舌。门锁运行以后,还会产生另一批内容:刚输入的数字、传感器读数、失败次数和开门记录。

程序断电以后仍要保留,平时很少改;临时状态变化频繁,外设读数还要随时进入。两类内容需求不同,一款微控制器便让程序和运行数据使用分开的区域和通路。处理器执行眼前指令时,还能准备下一条。

这看上去不再是“指令和数值共用一块存储器”,却没有放弃存储程序。门锁仍由可保存、可更新的程序决定行为,同一块硬件可以通过更新规则修复漏洞。变化的是道路,保留的是程序、状态、控制和执行之间的关系。具体手册只证明这一款实现,不代表所有嵌入式设备。

为什么要分路,不是一场概念之争,而是需求不同。验证程序一旦意外损坏,门锁可能彻底失效;临时输入却必须快速变化,断电后也未必需要保留;传感器还要在外部事件发生时及时送来数据。把它们分开安排,是在掉电保存、更新风险、成本和响应时间之间做选择,不是在决定自己属于哪一种教材分类。

[007]

引用 [007]

supports

门锁是编辑场景,程序与数据分路只绑定具体微控制器手册。

定位 Microchip PDF p. 28, §6.3 and pp. 39-40, §§7.1-7.5; Haigh / Priestley / Rope pp. 9-13

查看完整证据关系

再把镜头反过来放大。你在购物应用里打开一件商品,屏幕上只有一个页面,背后却可能有不同服务读取账户、库存、价格和推荐。入口接住请求,调度系统选择机器,网络传递问题,存储交回数据,多个结果最后汇合。

对用户来说,这是一项服务;对数据中心来说,它是许多程序、服务器和数据副本的一次合作。Barroso、Holzle 和 Ranganathan 因此建议把仓库规模的系统当成一个完整设计单位,因为性能、成本和可靠性已经取决于整体协作。

但数据中心不是一颗仓库大小的中央处理器。每台服务器仍有自己的处理器、内存和程序,彼此通过网络交换消息。跨过单机边界以后,系统还要面对消息迟到、局部故障、重新调度和状态暂时不同步。这里复现的是角色之间需要配合,不是一条统一指令流被放大了几千倍。

单机控制部分可以假定下一条指令就在既定位置,云端服务却不能假定每台机器始终可达。一次库存查询超时,入口可能重试,也可能返回降级结果;重试又可能把同一动作执行两次。系统不仅要安排“下一步做什么”,还要说明“我不知道上一步是否成功时怎么办”。尺度变大以后,控制从确定动作变成带着不确定性的协调。

[008]

引用 [008]

context

购物请求是编辑场景;明确排除巨型中央处理器和统一指令流。

定位 PDF pp. 22-24, printed pp. 2-4, §§1.1-1.3: interacting programs, thousands of nodes, network, storage and warehouse-scale design

查看完整证据关系

软件公司也能借来做一次更大胆、也更危险的类比。共享文档和业务系统保存项目状态,管理层或项目负责人决定一部分优先级,各团队执行任务,客户反馈与监控不断形成输入,产品和服务成为输出。所有决定如果都要经过一个审批点,很多人就会一起等待。

这个类比只能帮助我们看见信息通道,不能把公司解释成计算机。Herbert Simon 讨论复杂系统时指出,层级关系不只等于组织图上的上下级。真实团队里,人会解释、协商、拒绝和创造,也会越过正式路径核对信息;公司没有机器式的唯一控制器、统一时钟和确定指令。

因此,不能从“冯·诺伊曼架构”推出集中管理或分权管理必然更好。我们只借它提出工程问题:状态放在哪里,决定经过几道门,现场的人能否处理局部变化,哪个审批点正在让所有执行者等待?

[009]

引用 [009]

corrects

组织是有边界的编辑类比,不把层级等同正式权威,也不推出管理定律。

定位 Simon scan p. 2 / printed p. 468 and scan p. 10 / printed p. 476; Barroso et al. PDF p. 22

查看完整证据关系

门锁、云端和公司摆在一起,外形几乎没有共同点。它们也不是 EDVAC 在不同尺度上的物理复刻。门锁可以把程序与运行数据分路;云端把控制和状态分散到许多机器;公司里的人甚至会重新解释目标。

它们仍然值得并置,因为同一组问题能暴露不同系统的选择:什么内容可以被保存和修改,谁根据这些内容决定下一步,谁真正执行,依赖经过多远才能抵达,失败以后由谁恢复。

这就是本期采用“架构”一词的边界。它不是一张祖传蓝图,而是一份关系检查表。蓝图要求尺寸和接线相同;关系检查表允许实现完全不同,却逼我们说清每一种安排把速度、可靠性和复杂度交给了谁。

用这份检查表看门锁,我们会追问程序更新失败后怎样恢复;看云端,我们会追问状态分散以后谁能做决定;看组织,我们会追问哪些信息必须上报、哪些选择可以留在现场。问题相似,答案却受材料、网络和人的能力限制。关系框架的价值正是在比较差异,而不是抹平差异。

[010]

引用 [010]

context

综合段只收束关系观察法,不声称三个尺度共享同一物理架构。

定位 Haigh / Priestley / Rope pp. 9-13; Microchip PDF p. 28; Barroso et al. pp. 22-24; Simon printed pp. 468 and 476

查看完整证据关系

第三章:语言把劳动移给谁

12:22

存储程序让换任务更多变成换指令,新的困难随即落到程序员身上:怎样写出数量越来越多、又不能出错的指令?

可以把早期编程想成安排一批投递。机器语言要求你亲手写下每一次停车、转弯、装卸和下一个地址,机器可以直接照做,但任何路线变化都可能牵动后面的编号。汇编语言先给道路、站点和动作起了名字,再由汇编器翻译成数字指令。编号负担减轻了,路线仍主要由人逐步安排,也仍紧贴一台具体机器。

高级语言再往前走一步:程序员说明要把哪批货送到哪些目的地、满足哪些条件,工具负责规划大量局部路线。三者不是严格的高低等级,同一项目也可以混用。投递类比更不能解释语言的全部能力,它只揭示一件事:描述越接近目标,越多重复安排就有机会交给工具。

被交出去的不只是敲键盘的数量。地址变化以后哪些跳转要调整、一个表达式怎样拆成机器支持的动作、同一段循环里哪些结果可以复用,这些原本需要人逐项维护的关系,也开始由工具统一处理。程序规模越大,这种可重复安排越有价值,因为人的注意力可以留给问题本身和工具无法知道的约束。

[011]

引用 [011]

context

投递类比统一解释数字指令、符号名字和较高层目标,并排除语言等级表。

定位 Backus et al. PDF p. 11 / printed p. 197 compares machine language, assembly program language and FORTRAN; Ritchie PDF pp. 2-3

查看完整证据关系

编译器因此更像路线规划者,而不是逐字抄写员。开发者给出目的地、顺序约束和可以重复的计算,编译器查看表达式与循环,选择机器指令,避免一部分重复工作,再把局部选择拼成完整程序。

这份分工之所以重要,是因为局部路线会随着机器改变。让工具承担可重复的翻译和检查,同一份意图才更容易修改,也更可能被带到另一台机器。程序员放弃逐条指定的权力,换回了开发速度、可读性和工具可以反复执行的安排。

但编译器也不知道没有写进程序的业务约束。它不知道某项结果能否延迟,不知道某个外部调用其实可以合并,更不能保证规划出的每条路线都优于了解全部现场的专家。抽象的价值不是消灭判断,而是把普遍、重复的局部劳动交给工具,把真正依赖业务和算法的选择留给人。

这也解释了为什么“更高级”不等于“更慢”。如果工具看见了更完整的循环和数据关系,它有时反而能做出单看一条指令时做不到的安排。反过来,如果重要意图被藏在工具看不懂的写法里,再聪明的编译器也只能保守处理。运行效率既取决于抽象层,也取决于抽象是否把有用信息表达出来。

[012]

引用 [012]

context

路线规划是有边界类比;编译器能力只绑定已表达约束,不承诺普遍最优。

定位 PDF p. 1 / printed p. 188 on project goals; PDF p. 11 / printed p. 197 on coding effort, output and translation

查看完整证据关系

一九五〇年代,John Backus 带领的团队开发 FORTRAN 时,这笔交换已经非常明确。科学和工程程序的大量时间耗在规划指令、编码和找错上;团队希望程序员用接近数学公式的方式描述计算,再由编译器生成高效的机器程序。

这不是先追求“好写”,后来才补救“跑得慢”。项目从一开始就必须同时证明两件事:人的编码劳动能显著减少,生成程序的运行效率又足以让使用者接受。早期报告中,一条 FORTRAN 语句可以变成多条机器指令,一些样例的准备时间明显下降,手工重写也没有获得值得一提的提速。

这些结果只属于当时那台机器和那些样例,不能证明编译器永远追平最好的人工优化。它更深的意义在于改变了责任边界:程序员开始用更大的计算意图工作,工具则接过大量容易重复、也容易出错的机器安排。

当时真正需要跨过的还有信任门槛。机器时间昂贵,如果方便书写的程序运行代价大幅上升,节省下来的开发时间可能很快被计算成本吃掉。FORTRAN 团队必须让“少写很多指令”和“机器仍然跑得足够好”同时成立,高级语言才不只是更友好的记号,而会成为可投入生产的工具。

[013]

引用 [013]

supports

开发与性能结果严格绑定早期具体机器和样例,不外推为所有高级语言。

定位 PDF p. 1 / printed p. 188 on goals and case history; PDF p. 11 / printed p. 197 on conciseness, coding time and output length

查看完整证据关系

十多年后,Dennis Ritchie 和同事们在资源紧张的小型计算机上开发 Unix。最初的系统用汇编语言写成,他们却需要一种更容易编写和理解、又能处理操作系统工作的语言。

C 给出了另一种平衡。它的类型和操作能落到真实机器提供的能力,让程序员在必要时关心内存和数据表示;同时又不把每段程序锁死在某台机器的指令名字上,使 Unix 可以向不同机器迁移。系统软件既要接近硬件,也要不断增长、修改和移植,这两项压力缺一不可。

这里的关键不是把所有机器差异藏掉,而是把差异压缩到少数必须面对的位置。大部分系统逻辑可以继续用同一种语言表达,真正依赖设备的部分再单独处理。于是换机器不再等于从第一条指令开始重写,维护者也能把注意力集中到那些确实无法通用的边界上。

这不表示任何 C 程序都比其他语言快,也不表示越靠近机器越专业。FORTRAN 和 C 共同说明,高级表达与运行效率并非只能二选一。语言和编译器真正改变的,是获得一份可接受性能所需付出的人力,以及程序换机器、换团队以后还要承担多少维护成本。

[014]

引用 [014]

supports

C 只作为历史平衡案例,不获得跨场景永久性能优先权。

定位 PDF pp. 2-3 on assembler, writing ease and machine-grounded abstractions; p. 14 on efficiency, abstraction and portability

查看完整证据关系

第四章:性能压力,让被隐藏的细节重新出现

17:47

图形处理器把大量计算单元放到同一块设备上以后,新的问题出现了。只告诉机器“把这段算完”,并不能保证所有计算单元都有活干,也不能保证它们需要的数据已经在近处。

可以把 CUDA 这样的编程方式想成管理一座分区仓库。开发者要把大任务切成许多可以同时处理的小批次,决定哪些工人在同一作业区,哪些物料放在区内共享,什么时候必须等同组工作完成,还要安排普通处理器和图形处理器之间怎样交接任务与数据。

分区的价值,是让同一批近处物料服务多次计算,减少每个工位都回中央库房取货。边界也很清楚:计算单元不会像真正的工人那样协商、换岗或发现业务目标。任务切错、共享安排错误,它们只会精确地低效,或者精确地给出错误结果。

[015]

引用 [015]

supports

仓库作业区解释任务分组、近处共享、同步和设备交接,并排除工人自主性。

定位 PDF p. 23, chapter 3; pp. 33-34, §§5.3-5.4; pp. 39-40, §6.2.2

查看完整证据关系

这些选择会直接影响速度。假如每做一点计算,就把数据在普通处理器和图形处理器之间来回搬运,路上的时间可能超过真正计算的时间;假如不同小组频繁互相等待,任务切得再碎,也不会自然变快。

这并不是退回机器语言。开发者不需要亲手写每个数字指令,编译器和运行系统仍然安排大量细节。变化只是抽象层向下开了一扇窗:为了让并行执行者持续工作,开发者重新接手“工作怎样分组、哪些数据留在近处、何时发生同步”这些会改变算法表现的选择。

为什么这些选择难以完全自动隐藏?因为最合适的分组常常取决于算法里哪些数据会被共同使用,哪些步骤真的互不依赖。硬件看得见一次读取和一次计算,却未必知道开发者接下来还会怎样使用同一份数据。语言把这部分意图暴露出来,是为了让人的算法知识与机器的执行能力在同一处相遇。

更多控制也意味着更多责任。代码更依赖特定设备,测试组合增加,硬件换代时旧安排可能失效。看见底层并不自动带来性能;只有看见的恰好是真实瓶颈,新增复杂度才可能换回收益。

[016]

引用 [016]

context

控制权与复杂度的交换是证据约束下综合,不声称显式控制必然更快。

定位 PDF pp. 23 and 33-40: heterogeneous execution, work grouping, synchronization, data transfer and memory hierarchy

查看完整证据关系

近几年,Triton 又把一部分劳动交还给编译器。它面对的处境是:人工智能研究者提出一种新计算,现成库未必支持;请少数专家手写高度优化的底层程序,成本高、迁移难;只用很高层的表达,又可能隐藏影响性能的数据复用。

它的做法可以类比成处理一张巨大的表格。开发者不再指定每个格子由哪个计算单元读取,而是说明怎样把表格分成一块一块,每块要完成什么计算、哪些数据会在块内反复使用;编译器再把这些块分给具体执行小组,处理更细的机器安排。

这个类比只解释两层分工,表格本身不会执行程序。分块方法仍然要由开发者选对,数据块之间的关系也必须符合算法。块太大、太小或复用不足,编译器不能把任意算法自动变快。

数据块之所以成为合适的交界面,是因为它比单个计算动作更接近算法,又比完整模型更接近硬件。开发者可以表达“这一块数据会一起参与计算”,编译器则根据具体设备决定怎样分派、怎样使用近处存储。双方都不必接管对方最擅长的全部知识。

[017]

引用 [017]

supports

表格分块解释算法结构与机器映射分工,明确开发者仍需选择正确分块。

定位 PDF pp. 1-2, Abstract / §1 on expert kernels, portability and tile abstraction

查看完整证据关系

Triton 的原始论文在列出的矩阵与卷积实验中,得到与成熟厂商库相当的结果。这个范围很重要:它证明一种分工在特定实验里可行,不证明新语言会让任何程序自动加速。

从机器语言走到这里,语言史就不再是一座只向上攀登的楼梯。更像一条会移动的边界:工具先隐藏编号和局部路线,让程序更容易写、更容易迁移;当并行数量和数据距离成为瓶颈,部分结构又向开发者显形;当这些结构能够被稳定描述,工具再接回更细的映射劳动。

所以,抽象不是远离机器,深入底层也不是能力证明。两者都在回答同一个分工问题:哪些选择可以由工具可靠重复,哪些选择必须由理解算法与业务的人承担,又有哪些选择带来的收益根本覆盖不了维护代价?

[018]

引用 [018]

corrects

语言演进收束为劳动边界移动,不建立抽象层级或普遍性能排名。

定位 FORTRAN PDF pp. 1 and 11; Ritchie PDF pp. 2-3 and 14; CUDA guide pp. 23 and 33-40; Triton PDF pp. 1-2 and 8

查看完整证据关系

第五章:让数据留下,或者一路向前

22:13

把时间拨回一九六五年。处理器越来越快,程序和数据越来越大,工程师却很难同时得到容量大、速度快、价格低的存储器。Maurice Wilkes 描述了一种务实办法:在处理器附近放一块较小、较快的存储,把刚从远处取回的内容暂时留下。

这像工匠把马上还要用的螺丝刀和量尺留在工作台,而不是每完成一个动作都走回库房。工具恰好在手边,下一步就省下一次往返;需要的工具不在,仍然要去远处取。

今天我们把这类近处小型快速存储称为高速缓存。它没有让远处库房消失,也不保证下一次一定命中。它只是利用程序经常重复使用近期内容或相邻内容的特点,为依赖缩短常走的那段路。

这项下注有两种直觉。一种是刚用过的内容很快还会再用,例如循环反复查看同一组数据;另一种是程序取了一个位置以后,往往还会取它附近的位置。高速缓存利用的是常见访问规律,不是预知未来。程序一旦跳到完全不同的数据,近处准备就可能落空。

[019]

引用 [019]

supports

工作台类比解释近处留存、命中与远处仍存在,不展开替换算法。

定位 printed pp. 270-271, Summary / Introduction and instruction / large slave-memory schemes

查看完整证据关系

工作台类比很直观,也容易让人误以为是程序员挑选每一件工具。硬件高速缓存通常自动管理,程序照常读取原来的位置,背后究竟走近路还是远路,往往由硬件决定。它和人工整理工作台、应用服务选择缓存内容都不是同一种机制。

真正相同的只有复用逻辑:一份内容取回以后,如果很快还要用,让它留在近处就比反复长途搬运便宜。命中时,等待像是消失了;没有命中,远近差异便重新暴露。

也正因为它靠访问规律下注,同样的代码量不代表同样的速度。两个程序做的计算次数相近,一个总在重复使用小范围数据,另一个每一步都跳到远处的新位置,实际等待可能完全不同。性能分析不能只数运算,还要看访问顺序是否让近处内容真正被用上。

这项设计也解释了一个常见错觉。软件看到的是稳定的读取方式,机器下面却可能铺着多层距离和速度完全不同的存储。抽象替我们藏住了路线,却不能取消路线;性能问题出现时,隐藏的距离就会重新成为原因。

[020]

引用 [020]

context

明确硬件缓存、工作台和应用缓存机制不同,只保留近处复用动作。

定位 Wilkes printed pp. 270-271; Kilburn printed pp. 223-226 on automatic transfers behind a one-level view

查看完整证据关系

一九八二年,H. T. Kung 从另一侧追问:如果专用处理器每做一次运算,都要回存储器重新取数,计算单元越快,取货口供应不上就越明显。继续增加工位,只会让更多工位一起空着。

他的规则阵列像一条流水线。数据从一端进入,经过相邻计算单元;每到一个工位完成一小部分计算,再把数据交给下一处。同一份数据沿途多次参与工作,不必每算一步都返回中央存储器。这种安排后来称为脉动阵列。

流水线真正节省的是返回库房的次数。一个工位刚用过的数据直接交给相邻工位,另一组数据按固定节奏从另一方向进入;计算发生在交接途中。只要节奏稳定,入口不必为每一个乘法重新供应完整数据,有限的通道便能支撑更多计算。

类比的边界正是流水线的边界。任务一旦不规则,步骤频繁分叉,或者数据只用一次就丢,固定阵列就未必合适。它用任务的规则性换取复用和持续供给,不是通用处理器的替代品。

[021]

引用 [021]

supports

流水线解释沿途计算与复用,并以规则任务边界限制适用范围。

定位 printed pp. 37-40: balancing computation with I/O, rhythmic data flow through arrays and multiple computations per memory access

查看完整证据关系

高速缓存和脉动阵列看起来是两种完全不同的设计。前者把可能再用的内容留在近处,后者让内容沿着规则路径继续前进。它们却共同改变了一笔账:为完成一次计算,依赖要走多远;一次搬运以后,又能换回多少次有用工作。

这笔账比“有多少计算单元”更接近真实效率。工位数量决定理想上限,供料速度和复用次数决定多少工位此刻真的有活干。增加计算单元是在扩大消费能力,缩短路程、提高复用则是在改善供给,两者不能互相代替。

可以把它写成一个不用公式的判断:一份数据远道而来,只使用一次,路费就由一次计算独自承担;如果沿途使用十次,同样路费便摊到十次计算上。硬件设计、编译器安排和算法重排都在寻找这种摊薄机会,只是它们能控制的层次不同。

代价也没有消失。近处存储容量有限,固定阵列只擅长规则任务,软件和编译器还要把问题整理成硬件能高效执行的形状。性能提升从来不是免费午餐,而是用容量、通用性或开发复杂度交换更少的等待。

[022]

引用 [022]

context

综合近处留存与沿途复用,明确容量、通用性和软件复杂度代价。

定位 Wilkes printed pp. 270-271; Kung printed pp. 37-40; Hennessy / Patterson PDF pp. 6-10

查看完整证据关系

第六章:硬件上限,为什么没有全部兑现

27:02

现在回到二〇一七年的谷歌张量处理器。主机先安排任务,模型权重保存在离计算阵列较远的位置,当前输入和中间结果被送到近处。内容准备好以后,同一份权重与中间结果沿规则路径前进,在许多计算单元之间反复使用。

这正是脉动阵列的长处:数据一旦进入,沿途完成多次计算,不必每做一次乘法都回远处取数。可它也解释了开场的停顿。阵列只能充分利用已经到手的数据,不能使用尚未抵达的权重。专用硬件提高了消费速度,没有把供给变成无限。

因此,六万多个计算单元是能力上限,不是持续兑现的保证。任务是否规则、每份数据能复用几次、远处权重供应得多快,共同决定实际有多少工位保持忙碌。

这也解释了为什么同一块芯片运行不同模型时,离峰值数字会有不同距离。需要大量重复计算的任务,取回一次权重便能支撑更多工作;频繁更换权重的任务,通道更早成为限制。峰值没有变,任务让供给与消费之间的比例变了。

[023]

引用 [023]

supports

回收规则阵列、数据复用和权重等待,不口播内部部件英文名。

定位 TPU PDF pp. 3-10, especially §4; Kung printed pp. 37-40

查看完整证据关系

论文比较不同任务后发现,有些任务取回一份数据便能完成很多计算,阵列可以得到充分利用;另一些任务很快又要换一批权重,等待供给的时间更突出。对后一类任务,提高数据抵达速度,可能比继续扩大计算阵列更有效。

应用服务里也有同样的诊断顺序。接口如果慢在一条共同的数据库查询上,增加工作线程可以接住更多请求,却不会让查询自动返回得更快。先减少往返、合并请求或改变数据位置,往往比继续增加等待者有效。

诊断时要看等待发生在哪里,而不是只看哪个部件最醒目。计算单元长期忙满,可能确实需要更多执行能力;计算单元经常空闲、数据通道持续繁忙,问题更可能在供给;两边都不忙,系统也许正在等待外部输入。不同迹象指向不同动作,不能用同一种“扩容”覆盖。

芯片不是服务器,权重也不是数据库结果。这个类比不提供同一种解决方案,只保留一种判断习惯:扩充执行者以前,先确认瓶颈究竟在消费能力,还是在所有执行者共同等待的供给。

[024]

引用 [024]

context

服务类比只保留容量与共同供给的诊断顺序,不等同具体机制。

定位 PDF §4, especially pp. 6-10: operations per weight byte, weight stalls and bandwidth / clock sensitivity by workload

查看完整证据关系

硬件不是唯一能缩短路程的地方。二〇二二年,一种注意力加速算法改变了计算顺序。普通做法会产生很大的中间结果,把它写到较远存储,随后为了下一步再读回来;新算法把工作切成较小数据块,让更多步骤在近处完成,只把必要结果送回远处,最终计算结果保持不变。

它像数据库优化器重新安排执行计划:问题没有变,最后答案没有变,但中间结果可以少落盘、少读回。边界是,这里处理的是数学计算,不是数据库查询;结果等价也不是类比保证的,而要由算法本身证明。

这件事把第三条主张推深了一层。数据位置不只由硬件接线决定,也受算法怎样拆分和排序影响。你可以换一块更快的芯片,也可以让同一块芯片少等几次。前者提高上限,后者减少为了得到同一结果所走的路。

[025]

引用 [025]

supports

数据库执行计划类比解释计算重排与减少往返,明确算法和结果边界。

定位 PDF pp. 1-4, Abstract / Figure 1 / §2.1: IO awareness, tiling, exact attention and reduced HBM-SRAM reads / writes

查看完整证据关系

尾声:每生成一步,依赖都要重新集合

30:26

现在,把镜头从一项算法拉回整次大语言模型生成。你输入一句话,按下发送。模型要依靠训练后保存下来的大量权重,读取这段对话此前留下的上下文记录,还必须等待前一个字词单位生成,才能继续下一个。

权重可以粗略想成部署后基本固定的一套庞大只读配置:每次请求都要参考。可它不是普通配置文件,每个数值都直接参加计算,数量大到无法全部留在最快位置。上下文记录又有点像会话状态,只属于当前对话并不断增长;但它同样直接参加后续计算,规模和用途都远超普通会话。

还有一条无法跳过的顺序依赖。模型没有生成前一个字词单位,就不知道下一个位置究竟要接在哪段文字后面。它可以在每一步内部并行完成大量计算,却不能提前把尚未确定的整段回答一次算完。阵列内部很宽,生成过程仍要一步一步向前。

两个类比只帮助我们分清“长期保存的模型内容”和“随请求增长的当前状态”。真正发生的事是,每生成一步,权重、上下文记录和前一步结果都要按计算需要重新集合。用户看到文字一点点出现,机器支付的是依赖一次次抵达的时间。

[026]

引用 [026]

supports

配置与会话类比均紧邻数值直接参与计算、规模和用途边界。

定位 PDF pp. 1-3: autoregressive dependency, weights / KV cache in HBM and stage-dependent memory costs

查看完整证据关系

这也给普通软件团队一条更实际的性能路线。第一步,先用能清楚表达业务、容易测试维护,而且性能已经足够的语言与框架。不要为了证明“懂底层”,提前承担一笔尚未产生收益的复杂度。

第二步,用测量找到等待发生在哪里:算法是不是做了无用工作,数据是不是走了太多往返,并行分工是否让执行者互相等待,还是大部分时间其实耗在数据库、网络或外部服务。一个接口九成时间都在等远程查询,把其中一小段业务判断改成更底层语言,用户几乎感觉不到。

测量还要对应用户真正关心的目标。批处理可能在意单位时间完成多少任务,交互服务更在意一次请求多久返回,移动设备还要计算耗电和发热。只追一个局部循环的速度,可能让整体成本上升,或者把等待转移到另一处。瓶颈必须放回完整工作流里判断。

第三步,只把证据确认的热点下沉,并把代码复杂度、硬件绑定、迁移难度和长期维护一起计价。如果一段模型计算每天执行数百万次,优化数据分块可能值得;如果编译器升级或硬件换代已经消除了收益,旧优化也该有退出方案。深入到底层是一种有价格的工具,不是一场职业忠诚测试。

[027]

引用 [027]

context

三步路线是证据约束下的工程综合,不给出普遍语言或硬件性能排名。

定位 Language and AI sources at the locators recorded in C-012-V6-013 through C-012-V6-018 and C-012-V6-023 through C-012-V6-025

查看完整证据关系

回头看,这八十年的变化没有排成一条从 EDVAC 直达大语言模型的发明链。它们只是让三个反复出现的问题越来越清楚。

第一,架构不是必须复印的物理蓝图,而是程序、状态、控制和执行怎样配合。第二,编程语言不会单向远离机器,它不断在开发者、编译器、运行系统和硬件之间重新分配劳动。第三,性能不只看有多少执行者,还要看依赖何时抵达、一次抵达能被使用多少次,以及为了缩短等待付出的复杂度是否值得。

摩尔电气工程学院团队把“下一步做什么”移进可保存的程序,让换题不再主要依靠人的双手。今天,我们仍在移动同一类边界:把哪些决定交给工具,把哪些数据留在近处,把哪些热点交给专用硬件。

架构让符号可以被保存和执行,却不决定下一个符号更可能是什么。下一期,我们去贝尔实验室,跟随 Claude Shannon 看语言怎样变成一串可以猜测、计数和度量的符号。这是《大语言模型前史》的下一站。

[028]

引用 [028]

context

结尾只收束三条主张,明确排除 EDVAC 到大语言模型的直接因果。

定位 First Draft §§2.2-2.8 and §§15.1-15.6; later architecture, language and AI sources at the separately bounded locators above

查看完整证据关系

独立片尾:单集识别与系列说明

34:22

你刚刚收听的是《原代码》第十二期,《“冯·诺依曼架构”:从 EDVAC 到大语言模型》。

本期也是《原代码》系列《大语言模型前史》的第二期。

系列副标题是,从图灵、冯·诺依曼到 GPT-1。

这个系列关心的不是一条注定抵达今天的发明直线,而是一代代研究者怎样把关于机器与语言的大问题,拆成计算、记忆、概率、知识、学习和预测这些可以动手解决的问题。

欢迎订阅《原代码》,继续收听《大语言模型前史》的后续更新。