最近阅读的计算机书籍

6 min read

引子

LLM 可以帮我解决很多知识和实践问题,但几乎都出现在一些随机的碎片化场景。有时候,自己确实通过 LLM 知道了一些特定的问题怎么解决,但事实上却把判断和思考外包给了 LLM。为了和这种倾向对抗,在学习具体技术和工具使用之余,还打算培养一下自己的计算机科学素质。

这篇文章将会介绍一些我认为在时代变革中仍具丰富价值的领域经典,这些书在最近 1 年内都或多或少地陪伴过我一段时间。也相当于是做个书评了。

当然,我并没有全数读完,初学者一个,并且我也不是计算机专业学生。不过我能保证我确实从这些书里获得了很多体验。

本文分别从两个层面推荐几本书:1. 计算机基础 2. 图形学。

计算机基础

程序的本质 ——《计算机程序的构造和解释》

《计算机程序的构造和解释》(SICP)这本书是麻省理工学院 6.001 课程的教材,自 1980 年到 2008 年被持续使用。这本书利用 Lisp 编程语言介绍计算机程序的各种伟大思想,可谓是计算机科学领域的哲学书。

当代编程语言的 lambda 表达式、高阶函数、闭包、惰性求值、垃圾回收等特性就是在重新实现 SICP 里描述过的东西。

MIT 现在改教 Python 了。同时 SICP 这门课在 Berkeley 和南京大学等地的课程也正以 Python 的方式呈现。不过,个人认为 SICP 还是搭配 Lisp 更对味一点,可能因为我是以一种考古学的视角去看待的。

SICP
SICP

重新理解递归

我是先学过了 C 和 C# 再去看的 SICP,于是非常有新鲜感。我们知道,如果你尝试在 C(或 C++ / C# / Java)里实现一个迭代计算 Fibonacci 通项的程序:

1
2
3
4
5
6
7
8
9
int fib(int n) {
int a = 0, b = 1;
for (int i = 0; i < n; ++i) {
int next = a + b;
a = b;
b = next;
}
return a;
}

而 Lisp 里是没有显式寄存器变量的,同样的程序会写成:

1
2
3
4
5
6
7
(define (fib n)
(fib-iter 1 0 n))

(define (fib-iter a b count)
(if (= count 0)
b
(fib-iter (+ a b) a (- count 1))))

这不是递归吗?怎么迭代了?我们知道递归计算 Fibonacci 通项的时间复杂度是指数级的 $O(\phi^n), \phi = \frac{1 + \sqrt{5}}{2}$,而实际上本文的这个 Lisp 程序的时间复杂度是线性的 $O(n)$。

可以看出它没有直接把 $F(n) = F(n - 1) + F(n - 2)$ 这个递归的公式翻译上去,而是在线性地更新状态 (a b count) 到 (a+b a count-1),最后 (= count 0) 时候返回,并没有产生 Fibonacci 调用树。也就是说递归语法和递归过程其实并不是一回事,递归语法可以产生迭代过程。这是读完 SICP 第一章就能领会的 —— 直接看到程序的形式和本质的联系。

独特的程序结构

国内数据结构课很多使用的是严蔚敏老师的《数据结构(C 语言版)》紫皮书,它经常被诟病讲解晦涩和部分内容非主流(比如“广义表”、静态链表)。其实这本书里说的广义表就是 (a b (c d) (e f g)) 这样的结构,Lisp 语言的表达式本身就是一种广义表(它叫 S-expression),从上面贴出的 Lisp 源码就可以看出这种独特的结构。

而课上讲的 Head() 和 Tail() 就是 Lisp 里的 car 和 cdr,在反直觉的同时又有种拙劣的模仿感233

因为 Head() 是取表头元素,所以直觉告诉我们 Tail() 是取表尾元素,但实际上这本书规定的是去掉表头之后取整个表,这和 Lisp cdr 的功能一致。

所以这本书讲“广义表”的迷惑之处就在于:在面向 C 程序设计的课程中,引入了一套与 Lisp 列表处理高度相关的抽象,却没有充分地建立两者之间的概念桥梁。

解释器的魔法

对于程序语言本身,SICP 还介绍了 eval-apply 循环,它揭示了一个程序语言可以解释它自己,听起来像魔法。这其实就是解释器(interpreter)的核心—— eval 负责解释表达式的含义,决定如何求值;而 apply 就负责执行相应的求值计算。

SICP
揭示了计算机程序本质的 eval-apply 循环

还有更多有趣东西可以探索!本文对 SICP 的介绍先说到这里了。

总的来说,这本书对于只学过特定主流语言的人而言,无疑是一场大脑升级(,,,) 其实这隐含了一个你已经学过一门语言的前提,假如你是一个完完全全的编程新手,对程序语言的理解还是一张白纸,阅读这本书反而不会感到惊异。

击穿黑盒 ——《深入理解计算机系统》

此书就是大名鼎鼎的 CSAPP (Computer Systems: A Programmer’s Perspective) ,同时是CMU 15-213 课程的教材。

这本书主要就是介绍计算机底层基础,把二进制、汇编、体系结构、链接和装载、虚拟内存、OS、网络这些知识都放进了一本书,相当于把几门零散的计算机核心专业课都拉通来整理了一遍,让人形成系统认识。

具体的专业课程是一定比这本书介绍的相关主题更深入的(例如 jyyOS, CS61C 等)。而 CSAPP 以程序员的视角把基础知识串起来,能起到很好的预习作用。

CSAPP 除了提供很多优质的小练习之外,还有几个著名的 Lab。这些 Lab 的难度很大,非常锻炼人,比如 Bomb Lab 就是让你使用 gdb 逆向一个二进制程序,通过研究反汇编来得到关卡的答案 否则这个程序就要爆炸!砰! ,我之前 有一篇文章 尝了下咸淡,确实挺有意思的。

如果感兴趣或者时间充裕,这几个 Lab 是不容错过的,它们也是 CSAPP 的精髓。

工作在具体的机器上

看这个例子:

1
2
3
int run() {
return 1145141919 * 1919810;
}

理论上 run() 会计算出一个很大的数,但实际上控制台输出的是 -1412353538,并且在这之前,编译器就在抱怨了:

1
2
3
4
5
Overflow.c:4:23: warning: overflow in expression; result is -1'412'353'538
with type 'int' [-Winteger-overflow]
4 | return 1145141919 * 1919810;
| ~~~~~~~~~~~^~~~~~~~~
1 warning generated.

这是 32 位整数溢出的结果,如果你经常看科普视频或者评论区,一定或多或少了解过,而 CSAPP 就会从底层(甚至会提及抽象代数)出发解释这些现象的原因和规律。

由于现在的框架过于抽象、电脑内部过于精巧和复杂,以至于我们很多时候都忘了软件是真真实实地跑在粗犷的数字电路器件上的。数学是理想,物理是现实,为了实现计算,必要的设计限制一定是存在的,而我们至少要对这些限制知情,以更好地使用机器。

漫游计算机系统

这其实是 CSAPP 第一章的标题,倒不如说整本书都在漫游计算机系统。

我同时还算半个垃圾佬,对硬件 DIY 也感兴趣,关注了很多这个领域的 up 主。硬件 DIY 可以说就是能直接摸(包括物理上)到计算机系统了,所以阅读 CSAPP 对成为一个资深垃圾佬也很有帮助(存疑(((

最近看到极客湾发了一期视频: 《旗舰手机芯片大横评:苹果高通发哥玄戒谁最强?》 ,内容做的相当硬核,从缓存、虚拟内存机制、TLB 的角度分析骁龙 8 Elite Extreme Gen6 的性能问题:

gkw
虚拟内存机制
gkw
TLB 与页表
gkw
骁龙 8EE6 的 TLB

这部分实际上就属于 OS 和组成原理/体系结构的交叉。极客湾这个视频还出现了四路组相连的缓存设计(4-way set associative cache,如上图 A B C D 为一组所示,虽然视频没有明确说出这个名词)

以上的内容(包括那一整期视频),如果你暂时觉得晕头转向,而你又很感兴趣,那么 CSAPP 简直就是你的必读之书!你真的会知道这些硬核的知识这意味着什么。CSAPP 的第六章 存储器层次结构 和第九章 虚拟内存 会给予你足够的启发!

更好的算法和数据结构入门 ——《算法(第四版)》

这本书的书评得酝酿一会儿,先放张占位图片233

algs4
《算法(第四版)》

图形学

下面要介绍的书其实从 2023 年起就在陪伴我了。我了解图形学比正经学计算机内容还要早两年,从高一的时候就一直在看。在高中学校封闭又窒息的环境下,这些书是我唯一可以逃避的去处,给了我活着的理由。

先放几张占位图片,之后再更~

图形学入门圣经 —— 虎书

cg
虎书

实时渲染圣经 —— 《实时渲染》

rtr
RTR4

离线渲染圣经 ——《基于物理的渲染:从理论到实现

pbrt
PBRT