<audio id="audio" title="FAQ第二期 | 世界上第一个编程语言是怎么来的？" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/32/ed/323442a890f99b2a81a9e962eeeb57ed.mp3"></audio>

你好，我是徐文浩，今天是第二期FAQ，我搜集了第3讲到第6讲，大家在留言区问的比较多的问题，来做一次集中解答。

有些问题，可能你已经知道了答案，不妨看看和我的理解是否一样；如果这些问题刚好你也有，那可要认真看啦！

希望今天的你，也同样有收获！

## Q1：为什么user + sys运行出来会比real time多呢？

<img src="https://static001.geekbang.org/resource/image/36/4c/3665db1602c971c2cad1932ee8d0804c.png" alt="">

我们知道，实际的计算机运行的过程中，CPU会在多个不同的进程里面切换，分配不同的时间片去执行任务。所以，运行一个程序，在现实中走过的时间，并不是实际CPU运行这个程序所花费的时间。前者在现实中走过的时间，我们叫作real time。有时候叫作wall clock time，也就是墙上挂着的钟走过的时间。

而实际CPU上所花费的时间，又可以分成在操作系统的系统调用里面花的sys time和用户态的程序所花的user time。如果我们只有一个CPU的话，那real time &gt;= sys time + user time 。所以，我当时在文章里给大家看了对应的示例。

不过，有不少同学运行出来的结果不是这样的。这是因为现在大家都已经用上多核的CPU了。也就是同一时间，有两个CPU可以同时运行任务。

你在一台多核或者多CPU的机器上运行，seq和wc命令会分配到两个CPU上。虽然seq和wc这两个命令都是单线程运行的，但是这两个命令在多核CPU运行的情况下，会分别分配到两个不同的CPU。

于是，user和sys的时间是两个CPU上运行的时间之和，这就可能超过real的时间。而real只是现实时钟里走过的时间，极端情况下user+sys可以到达real的两倍。

你可以运行下面这个命令，快速验证。让这个命令多跑一会儿，并且在后台运行。

```
time seq 100000000 | wc -l &amp;

```

然后，我们利用top命令，查看不同进程的CPU占用情况。你会在top的前几行里看到，seq和wc的CPU占用都接近100，实际上，它们各被分配到了一个不同的CPU执行。

我写这篇文章的时候，测试时只开了一个1u的最小的虚拟机，只有一个CPU，所以不会遇到这个问题。

## Q2：时钟周期时间和指令执行耗时有直接关系吗？

<img src="https://static001.geekbang.org/resource/image/f9/31/f9da13f81cc676645a224b8ea6744931.png" alt="">

这个问题提的得非常好，@易儿易 同学的学习和思考都很仔细、深入。

“晶振时间与CPU执行固定指令耗时成正比”，这个说法更准确一点。我们为了理解，可以暂且认为，是晶振在触发一条一条电路变化指令。这就好比你拨算盘的节奏一样。算盘拨得快，珠算就算得快。结果就是，一条简单的指令需要的时间就和一个时钟周期一样。

当然，实际上，这个问题要比这样一句话复杂很多。你可以仔细去读一读专栏关于CPU的章节呢。

从最简单的单指令周期CPU来说，其实时钟周期应该是放下最复杂的一条指令的时间长度。但是，我们现在实际用的都没有单指令周期CPU了，而是采用了流水线技术。采用了流水线技术之后，单个时钟周期里面，能够执行的就不是一个指令了。我们会把一条机器指令，拆分成很多个小步骤。不同的指令的步骤数量可能还不一样。不同的步骤的执行时间，也不一样。所以，一个时钟周期里面，能够放下的是最耗时间的某一个指令步骤。

这样的话，单看一条指令，其实一定需要很多个时钟周期。也就是说，从响应时间的角度来看，一个时钟周期一定是不够执行一条指令的。但是呢，因为有流水线，我们同时又会去执行很多个指令的不同步骤。再加上后面讲的像超线程技术等等，从吞吐量的角度来看，我们又能够做到，平均一个时钟周期里面，完成指令数可以超过1。

想要准确理解CPU的性能问题，请你一定去仔细读一读专栏的整个CPU的部分啊。

## Q3：为什么低压主频只有标压的2/3？计算向量点积的时候，怎么提高性能？

<img src="https://static001.geekbang.org/resource/image/87/6b/87e8925f9d8b12906164e17dad86626b.png" alt="">

低压和低主频都是为了减少能耗。比如Surface Go的电池很小，机器的尺寸也很小。如果用上高主频，性能更好了，但是耗电并没有下来。

另外，低电压对于CPU的工艺有更高的要求，因为太低的电压可能导致电路都不能导通，要高主频一样对工艺有更高的要求。所以一般低压CPU都是通过和低主频配合，用在对于移动性和续航要求比较高的机器上。

向量计算是可以通过让加法也并行来优化的，不过真实的CPU里面其实是通过SIMD指令来优化向量计算的，我在后面也会讲到SIMD指令。

## Q4：世界上第一个编程语言是怎么来的？

<img src="https://static001.geekbang.org/resource/image/8d/75/8d8e399dfef0d4b62c34910ccd4f4d75.png" alt="">

如果你去计算机历史博物馆看一下真机，就会明白，第一台通用计算机ENIAC，它的各种输入都是一些旋钮，可以认为是类似用机器码在编程，后来才有了汇编语言、C语言这样越来越高级的语言。

编程语言是自举的，指的是说，我们能用自己写出来的程序编译自己。但是自举，并不要求这门语言的**第一个**编译器就是用自己写的。

比如，这里说到的Go，先是有了Go语言，我们通过C++写了编译器A。然后呢，我们就可以用这个编译器A，来编译Go语言的程序。接着，我们再用Go语言写一个编译器程序B，然后用A去编译B，就得到了Go语言写好的编译器的可执行文件了。

这个之后，我们就可以一直用B来编译未来的Go语言程序，这也就实现了所谓的自举了。所以，即使是自举，也通常是先有了别的语言写好的编译器，然后再用自己来写自己语言的编译器。

更详细的关于鸡蛋问题，可以直接看Wikipedia上[这个链接](https://en.wikipedia.org/wiki/Bootstrapping_(compilers))，里面讲了多种这个问题的解决方案。

## Q5：不同指令集中，汇编语言和机器码的关系怎么对应的？

<img src="https://static001.geekbang.org/resource/image/26/bc/26dbff8c5bf1f6fa4c24516cf4d911bc.png" alt="">

不同指令集里，对应的汇编代码会对应这个指令集的机器码呀。大家不要把“汇编语言”当成是像C一样的一门统一编程语言。

“汇编语言”其实可以理解成“机器码”的一种别名或者书写方式，不同的指令集和体系结构的机器会有不同的“机器码”。

高级语言在转换成为机器码的时候，是通过编译器进行的，需要编译器指定编译成哪种汇编/机器码。

物理机自己执行的时候只有机器码，并不认识汇编代码。

编译器如果支持编译成不同的体系结构的汇编/机器码，就要维护很多不同的对应关系表，但是这个表并不会太大。以最复杂的Intel X86的指令集为例，也只有2000条不同的指令而已。

## Q6：某篇文章大段大段读不懂怎么办？

<img src="https://static001.geekbang.org/resource/image/3e/f6/3eeecdd9685196f4eb2e32012dc867f6.png" alt="">

@胖胖胖 同学说得很好。在专栏最开始几篇，或者到后面比较深入的文章，很多非科班的或者基础不太好的同学，会觉得读不下去，甚至很多地方看不懂。这些其实都是正常现象。

即便我在写的时候，已经尽可能考虑得比较完善，照顾大家的情况，但是肯定无法面面俱到。在我平时学习过程遇到拦路虎的时候，我一般有两种方法，这里跟你分享一下。

第一种，硬读。

你可能说了，这也叫方法吗？没错，事实就是这样。如果这个知识点，我必须要攻克，就想要搞明白，那我就会尽我所能，去看每一个字眼，把每个不理解的地方，都一点一点搞明白。不吝啬花费时间和精力。

当然这种情况适合我对这个内容完全不了解，或者已经基本了解，现在需要进一步提升的情况下。因为，在完全不了解一个知识的时候，这个壁垒是很高的。如果不想办法突破的话，那可能就没办法了解这个新的领域。而在已经基本了解某个领域或者某块知识的情况下，我去攻克一些更高难度的知识，很多时候也需要同样的方法，我会建立在兴趣的基础上去硬读，但是之后会非常非常有成就感。

第二种，先抓主要矛盾，再抓细节问题。

很多时候，大家在对一个知识不了解的时候，会感觉很“恐慌”。其实完全没必要，大家学任何东西都是从不会到会这么一个过程。就像@胖胖胖 同学说的那样，先找出这篇文章的主干，先对这些东西有个大致的概念。如果有需要，在之后的过程中，你还会碰到，你可以再重读，加深印象。

有时候，学习知识可以尝试“短期多次”。也就是说，看完一遍之后，如果不明白，先放下，过一段时间再看一遍，如果还不明白，再过一段时间再看。这样循环几次，在大脑中发酵几次，说不定就明白了，要给大脑一个缓冲的时间。

好了，今天的答疑到这里就结束了。不知道能否帮你解决了一些疑惑和问题呢？

我会持续不断地回复留言，并把比较好的问题精选出来，作为答疑。欢迎你继续在留言区留言，和大家一起交流学习。


