This commit is contained in:
louzefeng
2024-07-11 05:50:32 +00:00
parent bf99793fd0
commit d3828a7aee
6071 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
<audio id="audio" title="01 | 冯·诺依曼体系结构:计算机组成的金字塔" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/bf/f0/bfb4cbb06a226658acb06b2ca0ecddf0.mp3"></audio>
学习计算机组成原理,到底是在学些什么呢?这个事儿,一两句话还真说不清楚。不过没关系,我们先从“装电脑”这个看起来没有什么技术含量的事情说起,来弄清楚计算机到底是由什么组成的。
不知道你有没有自己搞过“装机”这回事儿。在2019年的今天大部分人用的计算机应该都已经是组装好的“品牌机”。如果我们把时钟拨回到上世纪八九十年代不少早期的电脑爱好者都是自己采购各种电脑配件来装一台自己的计算机的。
## 计算机的基本硬件组成
早年要自己组装一台计算机要先有三大件CPU、内存和主板。
在这三大件中,我们首先要说的是**CPU**它是计算机最重要的核心配件全名你肯定知道叫中央处理器Central Processing Unit。为什么说CPU是“最重要”的呢因为计算机的所有“计算”都是由CPU来进行的。自然CPU也是整台计算机中造价最昂贵的部分之一。
<img src="https://static001.geekbang.org/resource/image/a9/3c/a9af6307db5b3dde094c964e8940d83c.jpg" alt="">
CPU是一个超级精细的印刷电路板[图片来源](https://www.flickr.com/photos/130561288@N04/39836037882/in/photolist-23Gb7cm-25V6DAn-q421FW-qMvhAJ-7yVugk-qMvgHb-o3NoQV-qMwDkj-qMvgT1-7yVu7T-qMvgMj-7yVu5c-py3Fpg-8pZhf1-7yZhR5-7yVuax-ewr4C-7TQAKk-7SbTox-8pZh3b-fkLugb-HCGERb-231L6Mo-5SSUsD-28WhLvN-K2Tvk-98Cc4e-6ag8YH-7Sf6KS-aDGEYV-7yY2XT-b66LSc-r2oZqk-rPcasz-7TQ1dB-754sSu-qMwEzy-npvMDK-4BDkou-zrid4-a8X3jn-5uTaCd-7SbRFV-7TTeJh-6ag8zX-6akhEm-7ihCSj-8Whgmi-6j5iUJ-6ag8m8)
第二个重要的配件,就是**内存**Memory。你撰写的程序、打开的浏览器、运行的游戏都要加载到内存里才能运行。程序读取的数据、计算得到的结果也都要放在内存里。内存越大能加载的东西自然也就越多。
<img src="https://static001.geekbang.org/resource/image/aa/ad/aa20e3813fd7cb438bb0c13f43e09cad.jpg" alt="">
内存通常直接可以插在主板上,[图片来源](https://www.flickr.com/photos/dennissylvesterhurd/7633424314/in/photolist-cCxi73-4DT7ov-5SFN7f-22ptD6Q-5SEAjJ-5SMkhQ-qvfnJh-7TQ7bM-5SAgnX-jwzhXx-5SFTJY-7TQe2k-atvnG7-YGowK7-4w9tXh-5SEDih-dPcqJ1-5SAgFV-8EboSi-5SGJ9r-62Yv2h-5Tft1r-5Xz9Na-89gSAF-5SFFVy-5SMcvH-5KtAAz-eaehyJ-8kYkea-rEdcLj-b39Kug-EST98f-8tR3Vk-7ihCSj-dTYG6-YL543f-4dEEe-BJ8QZ-88ZMZg-6ZzkhW-8Z6NkM-5SBoXn-6JKJfA-7Zx3Su-5SFT2q-7TQkLk-75VyrS-5SGnr4-5SJnWV-5SBpq8)
存放在内存里的程序和数据需要被CPU读取CPU计算完之后还要把数据写回到内存。然而CPU不能直接插到内存上反之亦然。于是就带来了最后一个大件——**主板**Motherboard
主板是一个有着各种各样有时候多达数十乃至上百个插槽的配件。我们的CPU要插在主板上内存也要插在主板上。主板的**芯片组**Chipset和**总线**Bus解决了CPU和内存之间如何通信的问题。芯片组控制了数据传输的流转也就是数据从哪里到哪里的问题。总线则是实际数据传输的高速公路。因此**总线速度**Bus Speed决定了数据能传输得多快。
<img src="https://static001.geekbang.org/resource/image/16/b0/16bed40e3f1b1484e842cac3d6e596b0.jpg" alt="">
计算机主板上通常有着各种各样的插槽,[图片来源](https://www.flickr.com/photos/117150261@N02/12448712795/in/photolist-jY3UBe-7JggqE-DUWwNz-9GWzCa-bvGsRS-8m9cYn-e1BaEo-5SEAjJ-5SMkhQ-2eXVzdk-5SEDoU-dmvKB-5SAgnX-5SFTJY-e1vtir-5Pnxus-5SFFVy-63duyC-5SMcvH-jrTkcC-25V6DAn-imfxix-7VRFgR-inZF2N-io1oLM-zHB1BQ-C7aA66-dmE49-K6oVVQ-7VUTom-4pd9Jb-5SEDih-6LK87S-5SAgFV-5SGJ9r-22u9CTJ-7ihCSj-75VyrS-5PigdF-5SGnr4-5SJnWV-5SBpq8-5SNggT-jrTfcY-5SAjgT-5SSUsD-5SAgMi-4eqcQq-22cvYDk-5SAgSn)
有了三大件,只要配上**电源**供电计算机差不多就可以跑起来了。但是现在还缺少各类输入Input/输出Output设备也就是我们常说的**I/O设备**。如果你用的是自己的个人电脑,那显示器肯定必不可少,只有有了显示器我们才能看到计算机输出的各种图像、文字,这也就是所谓的**输出设备**。
同样的,鼠标和键盘也都是必不可少的配件。这样我才能输入文本,写下这篇文章。它们也就是所谓的**输入设备**。
最后,你自己配的个人计算机,还要配上一个硬盘。这样各种数据才能持久地保存下来。绝大部分人都会给自己的机器装上一个机箱,配上风扇,解决灰尘和散热的问题。不过机箱和风扇,算不上是计算机的必备硬件,我们拿个纸板或者外面放个电风扇,也一样能用。
说了这么多其实你应该有感觉了显示器、鼠标、键盘和硬盘这些东西并不是一台计算机必须的部分。你想一想我们其实只需要有I/O设备能让我们从计算机里输入和输出信息是不是就可以了答案当然是肯定的。
你肯定去过网吧吧不知道你注意到没有很多网吧的计算机就没有硬盘而是直接通过局域网读写远程网络硬盘里面的数据。我们日常用的各类云服务器只要让计算机能通过网络SSH远程登陆访问就好了因此也没必要配显示器、鼠标、键盘这些东西。这样不仅能够节约成本还更方便维护。
还有一个很特殊的设备,就是**显卡**Graphics Card。现在使用图形界面操作系统的计算机无论是Windows、Mac OS还是Linux显卡都是必不可少的。有人可能要说了我装机的时候没有买显卡计算机一样可以正常跑起来啊那是因为现在的主板都带了内置的显卡。如果你用计算机玩游戏做图形渲染或者跑深度学习应用你多半就需要买一张单独的显卡插在主板上。显卡之所以特殊是因为显卡里有除了CPU之外的另一个“处理器”也就是**GPU**Graphics Processing Unit图形处理器GPU一样可以做各种“计算”的工作。
鼠标、键盘以及硬盘这些都是插在主板上的。作为外部I/O设备它们是通过主板上的**南桥**SouthBridge芯片组来控制和CPU之间的通信的。“南桥”芯片的名字很直观一方面它在主板上的位置通常在主板的“南面”。另一方面它的作用就是作为“桥”来连接鼠标、键盘以及硬盘这些外部设备和CPU之间的通信。
有了南桥自然对应着也有“北桥”。是的以前的主板上通常也有“北桥”芯片用来作为“桥”连接CPU和内存、显卡之间的通信。不过随着时间的变迁现在的主板上的“北桥”芯片的工作已经被移到了CPU的内部所以你在主板上已经看不到北桥芯片了。
## 冯·诺依曼体系结构
刚才我们讲了一台计算机的硬件组成,这说的是我们平时用的个人电脑或者服务器。那我们平时最常用的智能手机的组成,也是这样吗?
我们手机里只有SD卡Secure Digital Memory Card这样类似硬盘功能的存储卡插槽并没有内存插槽、CPU插槽这些东西。没错因为手机尺寸的原因手机制造商们选择把CPU、内存、网络通信乃至摄像头芯片都封装到一个芯片然后再嵌入到手机主板上。这种方式叫**SoC**也就是System on a Chip系统芯片
这样看起来个人电脑和智能手机的硬件组成方式不太一样。可是我们写智能手机上的App和写个人电脑的客户端应用似乎没有什么差别都是通过“高级语言”这样的编程语言撰写、编译之后一样是把代码和数据加载到内存里来执行。这是为什么呢因为无论是个人电脑、服务器、智能手机还是Raspberry Pi这样的微型卡片机都遵循着同一个“计算机”的抽象概念。这是怎么样一个“计算机”呢这其实就是计算机祖师爷之一冯·诺依曼John von Neumann提出的**冯·诺依曼体系结构**Von Neumann architecture也叫**存储程序计算机**。
什么是存储程序计算机呢?这里面其实暗含了两个概念,一个是“**可编程**”计算机,一个是“**存储**”计算机。
说到“可编程”,估计你会有点懵,你可以先想想,什么是“不可编程”。计算机是由各种门电路组合而成的,然后通过组装出一个固定的电路板,来完成一个特定的计算程序。一旦需要修改功能,就要重新组装电路。这样的话,计算机就是“不可编程”的,因为程序在计算机硬件层面是“写死”的。最常见的就是老式计算器,电路板设好了加减乘除,做不了任何计算逻辑固定之外的事情。
<img src="https://static001.geekbang.org/resource/image/9b/6a/9bc9634431f627d3e684ce2f83cd946a.jpg" alt="">
计算器的本质是一个不可编程的计算机,[图片来源](https://www.flickr.com/photos/horiavarlan/4273218725/in/photolist-7vBn3V-3j7qrv-8iUqcs-biaK7a-qdmGPv-3jbGUN-6pFNS-3jbBa1-4MZAxs-292yK5p-2akim1j-26Bw8bE-qgskU-4EeDGe-NhdPhL-28gSRkC-292yLd6-4wVKuz-29iaje9-81BJ2h-27DSFgw-292yQkV-2akis1L-292yWRa-292yTqn-9sATYG-2akirG9-29ian6G-27DSDV5-9sAUCq-8EGHW5-29iaj49-2akigzf-29iarj1-MexNtE-292yUkt-LDNqXB-29jdR8d-4pyKYY-29nivE4-29iavZy-29iamfy-292yUMa-2akig6u-2akifN5-29jdQs5-29jdQhW-2akifUN-29jdRah-29jdQtN)
我们再来看“存储”计算机。这其实是说程序本身是存储在计算机的内存里可以通过加载不同的程序来解决不同的问题。有“存储程序计算机”自然也有不能存储程序的计算机。典型的就是早年的“Plugboard”这样的插线板式的计算机。整个计算机就是一个巨大的插线板通过在板子上不同的插头或者接口的位置插入线路来实现不同的功能。这样的计算机自然是“可编程”的但是编写好的程序不能存储下来供下一次加载使用不得不每次要用到和当前不同的“程序”的时候重新插板子重新“编程”。
<img src="https://static001.geekbang.org/resource/image/cb/9e/cbf639bab23f61d464aa80b4fd10019e.jpg" alt="">
著名的[Engima Machine](https://en.wikipedia.org/wiki/Enigma_machine)就用到了Plugboard来进行“编程”[图片来源](https://commons.wikimedia.org/wiki/File:Enigma-plugboard.jpg)
可以看到,无论是“不可编程”还是“不可存储”,都会让使用计算机的效率大大下降。而这个对于效率的追求,也就是“存储程序计算机”的由来。
于是我们的冯祖师爷基于当时在秘密开发的EDVAC写了一篇报告[**First Draft of a Report on the EDVAC**](https://en.wikipedia.org/wiki/First_Draft_of_a_Report_on_the_EDVAC),描述了他心目中的一台计算机应该长什么样。这篇报告在历史上有个很特殊的简称,叫**First Draft**,翻译成中文,其实就是《第一份草案》。这样,现代计算机的发展就从祖师爷写的一份草案开始了。
**First Draft**里面说了一台计算机应该有哪些部分组成,我们一起来看看。
首先是一个包含算术逻辑单元Arithmetic Logic UnitALU和处理器寄存器Processor Register的**处理器单元**Processing Unit用来完成各种算术和逻辑运算。因为它能够完成各种数据的处理或者计算工作因此也有人把这个叫作数据通路Datapath或者运算器。
然后是一个包含指令寄存器Instruction Register和程序计数器Program Counter的**控制器单元**Control Unit/CU用来控制程序的流程通常就是不同条件下的分支和跳转。在现在的计算机里上面的算术逻辑单元和这里的控制器单元共同组成了我们说的CPU。
接着是用来存储数据Data和指令Instruction的**内存**。以及更大容量的**外部存储**,在过去,可能是磁带、磁鼓这样的设备,现在通常就是硬盘。
最后就是各种**输入和输出设备**,以及对应的输入和输出机制。我们现在无论是使用什么样的计算机,其实都是和输入输出设备在打交道。个人电脑的鼠标键盘是输入设备,显示器是输出设备。我们用的智能手机,触摸屏既是输入设备,又是输出设备。而跑在各种云上的服务器,则是通过网络来进行输入和输出。这个时候,网卡既是输入设备又是输出设备。
任何一台计算机的任何一个部件都可以归到运算器、控制器、存储器、输入设备和输出设备中,而所有的现代计算机也都是基于这个基础架构来设计开发的。
而所有的计算机程序,也都可以抽象为从**输入设备**读取输入信息,通过**运算器**和**控制器**来执行存储在**存储器**里的程序,最终把结果输出到**输出设备**中。而我们所有撰写的无论高级还是低级语言的程序,也都是基于这样一个抽象框架来进行运作的。
<img src="https://static001.geekbang.org/resource/image/fa/2b/fa8e0e3c96a70cc07b4f0490bfe66f2b.jpeg" alt="">
冯·诺依曼体系结构示意图,[图片来源](https://en.wikipedia.org/wiki/Von_Neumann_architecture#/media/File:Von_Neumann_Architecture.svg)
## 总结延伸
可以说,冯·诺依曼体系结构确立了我们现在每天使用的计算机硬件的基础架构。因此,学习计算机组成原理,其实就是学习和拆解冯·诺依曼体系结构。
具体来说学习组成原理其实就是学习控制器、运算器的工作原理也就是CPU是怎么工作的以及为何这样设计学习内存的工作原理从最基本的电路到上层抽象给到CPU乃至应用程序的接口是怎样的学习CPU是怎么和输入设备、输出设备打交道的。
学习组成原理,就是在理解从控制器、运算器、存储器、输入设备以及输出设备,从电路这样的硬件,到最终开放给软件的接口,是怎么运作的,为什么要设计成这样,以及在软件开发层面怎么尽可能用好它。
好了,这一讲说到这儿就结束了。你应该已经理解了计算机的硬件是由哪些设备组成的,以及冯·诺依曼体系结构是什么样的了。下一讲,我会带你看一张地图,也是计算机组成原理的知识地图。我们一起来看一看怎么样才是学习组成原理的好方法。
## 推荐阅读
我一直认为,读读经典的论文,是从一个普通工程师迈向优秀工程师必经的一步。如果你有时间,不妨去读一读[**First Draft of a Report on the EDVAC**](https://en.wikipedia.org/wiki/First_Draft_of_a_Report_on_the_EDVAC)。对于工程师来说,直接读取英文论文的原文,既可以搞清楚、弄明白对应的设计及其背后的思路来源,还可以帮你破除对于论文或者核心技术的恐惧心理。
## 课后思考
计算机行业的两大祖师爷之一除了冯·诺依曼机之外还有一位就是著名的图灵Alan Mathison Turing。对应的我们现在的计算机也叫**图灵机**Turing Machine。那么图灵机和冯·诺依曼机是两种不同的计算机么图灵机是一种什么样的计算机抽象呢
欢迎留言和我分享你的思考和疑惑,你也可以把今天的内容分享给你的朋友,和他一起学习和进步。

View File

@@ -0,0 +1,110 @@
<audio id="audio" title="02 | 给你一张知识地图,计算机组成原理应该这么学" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/3b/87/3b1729fc5eaa20f711b753f65d147d87.mp3"></audio>
了解了现代计算机的基本硬件组成和背后最基本的冯·诺依曼体系结构,我们就可以正式进入计算机组成原理的学习了。在学习一个一个零散的知识点之前,我整理了一份学习地图,好让你对将要学习的内容有一个总纲层面的了解。
<img src="https://static001.geekbang.org/resource/image/12/ff/12bc980053ea355a201e2b529048e2ff.jpg" alt="">
从这张图可以看出来,**整个计算机组成原理,就是围绕着计算机是如何组织运作展开的**。
## 计算机组成原理知识地图
计算机组成原理的英文叫Computer Organization。这里的Organization是“组织机构”的意思。计算机由很多个不同的部件放在一起变成了一个“组织机构”。这个组织机构最终能够进行各种计算、控制、读取输入进行输出达成各种强大的功能。
在这张图里面我们把整个计算机组成原理的知识点拆分成了四大部分分别是计算机的基本组成、计算机的指令和计算、处理器设计以及存储器和I/O设备。
首先,我们来看**计算机的基本组成**。
这一部分,你需要学习计算机是由哪些硬件组成的。这些硬件,又是怎么对应到经典的冯·诺依曼体系结构中的,也就是运算器、控制器、存储器、输入设备和输出设备这五大基本组件。除此之外,你还需要了解计算机的两个核心指标,性能和功耗。性能和功耗也是我们在应用和设计五大基本组件中需要重点考虑的因素。
了解了组成部分,接下来你需要掌握**计算机的指令和计算**。
在计算机指令部分你需要搞明白我们每天撰写的一行行C、Java、PHP程序是怎么在计算机里面跑起来的。这里面你既需要了解我们的程序是怎么通过编译器和汇编器变成一条条机器指令这样的编译过程如果把编译过程展开的话可以变成一门完整的编译原理课程还需要知道我们的操作系统是怎么链接、装载、执行这些程序的这部分知识如果再深入学习又可以变成一门操作系统课程。而这一条条指令执行的控制过程就是由计算机五大组件之一的**控制器**来控制的。
在计算机的计算部分你要从二进制和编码开始理解我们的数据在计算机里的表示以及我们是怎么从数字电路层面实现加法、乘法这些基本的运算功能的。实现这些运算功能的ALUArithmetic Logic Unit/ALU也就是算术逻辑单元其实就是我们计算机五大组件之一的**运算器**。
这里面有一个在今天看起来特别重要的知识点就是浮点数Floating Point。浮点数是我们在日常运用中非常容易用错的一种数据表示形式。掌握浮点数能让你对数据的编码、存储和计算能够有一个从表到里的深入理解。尤其在AI火热的今天浮点数是机器学习中重度使用的数据表示形式掌握它更是非常有必要。
明白计算机指令和计算是如何运转的,我们就可以深入到**CPU的设计**中去一探究竟了。
CPU时钟可以用来构造寄存器和内存的锁存器和触发器因此CPU时钟应该是我们学习CPU的前导知识。搞明白我们为什么需要CPU时钟CPU Clock以及寄存器和内存是用什么样的硬件组成的之后我们可以再来看看整个计算机的数据通路是如何构造出来的。
数据通路其实就是连接了整个运算器和控制器并最终组成了CPU。而出于对于性能和功耗的考虑你要进一步理解和掌握面向流水线设计的CPU、数据和控制冒险以及分支预测的相关技术。
既然CPU作为控制器要和输入输出设备通信那么我们就要知道异常和中断发生的机制。在CPU设计部分的最后我会讲一讲指令的并行执行看看如何直接在CPU层面通过SIMD来支持并行计算。
最后,我们需要看一看,计算机五大组成部分之一,**存储器的原理**。通过存储器的层次结构作为基础的框架引导你需要掌握从上到下的CPU高速缓存、内存、SSD硬盘和机械硬盘的工作原理它们之间的性能差异以及实际应用中利用这些设备会遇到的挑战。存储器其实很多时候又扮演了输入输出设备的角色所以你需要进一步了解CPU和这些存储器之间是如何进行通信的以及我们最重视的性能问题是怎么一回事理解什么是IO_WAIT如何通过DMA来提升程序性能。
对于存储器我们不仅需要它们能够正常工作还要确保里面的数据不能丢失。于是你要掌握我们是如何通过RAID、Erasure Code、ECC以及分布式HDFS这些不同的技术来确保数据的完整性和访问性能。
## 学习计算机组成原理,究竟有没有好办法?
相信这个学习地图,应该让你对计算机组成这门课要学些什么,有了一些了解。不过这个地图上的知识点繁多,应该也给你带来了不小的挑战。
我上一节也说过,相较于整个计算机科学中的其他科目,计算机组成原理更像是整个计算机学科里的“纲要”。这门课里任何一个知识点深入挖下去,都可以变成计算机科学里的一门核心课程。
比如说程序怎样从高级代码变成指令在计算机里面运行对应着“编译原理”和“操作系统”这两门课程计算实现背后则是“数字电路”如果要深入CPU和存储器系统的优化必然要深入了解“计算机体系结构”。
因此,为了帮你更快更好地学计算机组成,我为你总结了三个学习方法,帮你更好地掌握这些知识点,并且能够学为所用,让你在工作中能够用得上。
首先,**学会提问自己来串联知识点**。学完一个知识点之后,你可以从下面两个方面,问一下自己。
<li>
我写的程序,是怎样从输入的代码,变成运行的程序,并得到最终结果的?
</li>
<li>
整个过程中,计算器层面到底经历了哪些步骤,有哪些地方是可以优化的?
</li>
无论是程序的编译、链接、装载和执行以及计算时需要用到的逻辑电路、ALU乃至CPU自发为你做的流水线、指令级并行和分支预测还有对应访问到的硬盘、内存以及加载到高速缓存中的数据这些都对应着我们学习中的一个个知识点。建议你自己脑子里过一遍最好是口头表述一遍或者写下来这样对你彻底掌握这些知识点都会非常有帮助。
其次,**写一些示例程序来验证知识点。**计算机科学是一门实践的学科。计算机组成中的大量原理和设计,都对应着“性能”这个词。因此,通过把对应的知识点,变成一个个性能对比的示例代码程序记录下来,是把这些知识点融汇贯通的好方法。因为,相比于强记硬背知识点,一个有着明确性能对比的示例程序,会在你脑海里留下更深刻的印象。当你想要回顾这些知识点的时候,一个程序也更容易提示你把它从脑海深处里面找出来。
最后,**通过和计算机硬件发展的历史做对照**。计算机的发展并不是一蹴而就的。从第一台电子计算机ENIACElectronic Numerical Integrator And Computer电子数值积分计算机的发明到现在已经有70多年了。现代计算机用的各个技术都是跟随实际应用中遇到的挑战一个个发明、打磨最后保留下来的。这当中不仅仅有学术层面的碰撞更有大量商业层面的交锋。通过了解充满戏剧性和故事性的计算机硬件发展史让你更容易理解计算机组成中各种原理的由来。
比如说奔腾4和SPARC的失败以及ARM的成功能让我们记住CPU指令集的繁与简、权衡性能和功耗的重要性而现今高速发展的机器学习和边缘计算又给计算机硬件设计带来了新的挑战。
## 给松鼠症患者的学习资料
学习总是要花点笨功夫的。最有效的办法还是“读书百遍,其义自见”。对于不够明白的知识点,多搜索,多看不同来源的资料,多和朋友、同事、老师一起交流,一定能够帮你掌握好想要学习的知识点。
在这个专栏之前,计算机组成原理,已经有很多优秀的图书和课程珠玉在前了。为了覆盖更多知识点的细节,这些书通常都有点厚,课程都会有点长。不过作为专栏的补充阅读材料,却是最合适不过了。
因此,每一讲里,我都会留下一些“**补充阅读**”的材料。如果你想更进一步理解更多深入的计算机组成原理的知识,乃至更多相关的其他核心课程的知识,多用一些业余时间来看一看,读一读这些“补充阅读”也一定不会让你对花在上面的时间后悔的。
下面给你推荐一些我自己看过、读过的内容。我在之后的文章里推荐的“补充阅读”,大部分都是来自这些资料。你可以根据自己的情况来选择学习。
### 入门书籍
我知道,订阅这个专栏的同学,有很多是非计算机科班出身,我建议你先对计算机组成原理这门课有个基本概念。建立这个概念,有两种方法,第一,你可以把我上面那张地图的核心内容记下来,对这些内容之间的关系先有个大致的了解。
第二,我推荐你阅读两本书,准确地说,这其实是两本小册子,因为它们非常轻薄、好读,而且图文并茂,非常适合初学者和想要入门组成原理的同学。一本是《计算机是怎样跑起来的》,另一本是《程序是怎样跑起来的》。我要特别说一下后面这本,它可以说是一个入门微缩版本的“计算机组成原理”。
除此之外计算机组成中硬件层面的基础实现比如寄存器、ALU这些电路是怎么回事你可以去看一看Coursera上的北京大学免费公开课[](https://www.coursera.org/learn/jisuanji-zucheng)[Computer Organization](https://www.coursera.org/learn/jisuanji-zucheng)[](https://www.coursera.org/learn/jisuanji-zucheng)。这个视频课程的视频部分也就10多个小时。在学习专栏相应章节的前后去浏览一遍相信对你了解程序在电路层面会变成什么样子有所帮助。
### 深入学习书籍
对于想要深入掌握计算机组成的同学,我推荐你去读一读《计算机组成与设计:硬件/软件接口》和经典的《深入理解计算机系统》这两本书。后面这本被称为CSAPP的经典教材网上也有配套的视频课程。我在这里给你推荐两个不同版本的链接[B](https://www.bilibili.com/video/av24540152/)[ilibili版](https://www.bilibili.com/video/av24540152/)和[Y](https://www.youtube.com/playlist?list=PLmBgoRqEQCWy58EIwLSWwMPfkwLOLRM5R)[outube版](https://www.youtube.com/playlist?list=PLmBgoRqEQCWy58EIwLSWwMPfkwLOLRM5R) 。不过这两本都在500页以上坚持啃下来需要不少实践经验。
计算机组成原理还有一本的经典教材就是来自操作系统大神塔能鲍姆Andrew S. Tanenbaum的《计算机组成结构化方法》。这本书的组织结构和其他教材都不太一样适合作为一个辅助的参考书来使用。
如果在学习这个专栏的过程中,引发了你对于计算机体系结构的兴趣,你还可以深入读一读《计算机体系结构:量化研究方法》。
### 课外阅读
在上面这些教材之外对于资深程序员来说来自Redhat的**What Every Programmer Should Know About Memory**是写出高性能程序不可不读的经典材料。而LMAX开源的Disruptor则是通过实际应用程序来理解计算机组成原理中各个知识点的最好范例了。
《编码:隐匿在计算机软硬件背后的语言》和《程序员的自我修养:链接、装载和库》是理解计算机硬件和操作系统层面代码执行的优秀阅读材料。
## 总结延伸
学习不是死记硬背,学习材料也不是越多越好。到了这里,希望你不要因为我给出了太多可以学习的材料,结果成了“松鼠症”患者,光囤积材料,却没有花足够多的时间去学习这些知识。
我工作之后一直在持续学习,在这个过程中,我发现最有效的办法,**不是短时间冲刺,而是有节奏地坚持,希望你能够和专栏的发布节奏同步推进,做好思考题,并且多在留言区和其他朋友一起交流**,就更容易能够“积小步而至千里”,在程序员这个职业上有更长足的发展。
好了,对于学习资料的介绍就到这里了。希望在接下来的几个月里,你能和我一起走完这趟“计算机组成”之旅,从中收获到知识和成长。
## 课后思考
今天我为你梳理计算机组成的知识地图,也讲了我认为学习这个专栏的一些方法,听了这么多,那么你打算怎么学习这个专栏呢?
欢迎你在留言区写下你的学习目标和学习计划,和大家一起交流,也欢迎你把今天的文章分享给你的朋友,互相督促,共同成长。

View File

@@ -0,0 +1,114 @@
<audio id="audio" title="03 | 通过你的CPU主频我们来谈谈“性能”究竟是什么" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/eb/bd/eb4c772179b74302531d32aa6da224bd.mp3"></audio>
“性能”这个词,不管是在日常生活还是写程序的时候,都经常被提到。比方说,买新电脑的时候,我们会说“原来的电脑性能跟不上了”;写程序的时候,我们会说,“这个程序性能需要优化一下”。那么,你有没有想过,我们常常挂在嘴边的“性能”到底指的是什么呢?我们能不能给性能下一个明确的定义,然后来进行准确的比较呢?
在计算机组成原理乃至体系结构中,“性能”都是最重要的一个主题。我在前面说过,学习和研究计算机组成原理,就是在理解计算机是怎么运作的,以及为什么要这么运作。“为什么”所要解决的事情,很多时候就是提升“性能”。
## 什么是性能?时间的倒数
计算机的性能,其实和我们干体力劳动很像,好比是我们要搬东西。对于计算机的性能,我们需要有个标准来衡量。这个标准中主要有两个指标。
第一个是**响应时间**Response time或者叫执行时间Execution time。想要提升响应时间这个性能指标你可以理解为让计算机“跑得更快”。
<img src="https://static001.geekbang.org/resource/image/4c/96/4c87a1851aeb6857a323064859da6396.png" alt="">
第二个是**吞吐率**Throughput或者带宽Bandwidth想要提升这个指标你可以理解为让计算机“搬得更多”。
<img src="https://static001.geekbang.org/resource/image/27/27/27cab77c0eec95ec29792e6c3d093d27.png" alt="">
所以说,响应时间指的就是,我们执行一个程序,到底需要花多少时间。花的时间越少,自然性能就越好。
而吞吐率是指我们在一定的时间范围内,到底能处理多少事情。这里的“事情”,在计算机里就是处理的数据或者执行的程序指令。
和搬东西来做对比,如果我们的响应时间短,跑得快,我们可以来回多跑几趟多搬几趟。所以说,缩短程序的响应时间,一般来说都会提升吞吐率。
除了缩短响应时间我们还有别的方法吗当然有比如说我们还可以多找几个人一起来搬这就类似现代的服务器都是8核、16核的。人多力量大同时处理数据在单位时间内就可以处理更多数据吞吐率自然也就上去了。
提升吞吐率的办法有很多。大部分时候我们只要多加一些机器多堆一些硬件就好了。但是响应时间的提升却没有那么容易因为CPU的性能提升其实在10年前就处于“挤牙膏”的状态了所以我们得慎重地来分析对待。下面我们具体来看。
我们一般把性能,定义成响应时间的倒数,也就是:
这样一来响应时间越短性能的数值就越大。同样一个程序在Intel最新的CPU Coffee Lake上只需要30s就能运行完成而在5年前CPU Sandy Bridge上需要1min才能完成。那么我们自然可以算出来Coffee Lake的性能是1/30Sandy Bridge的性能是1/60两个的性能比为2。于是我们就可以说Coffee Lake的性能是Sandy Bridge的2倍。
过去几年流行的手机跑分软件就是把多个预设好的程序在手机上运行然后根据运行需要的时间算出一个分数来给出手机的性能评估。而在业界各大CPU和服务器厂商组织了一个叫作**SPEC**Standard Performance Evaluation Corporation的第三方机构专门用来指定各种“跑分”的规则。
<img src="https://static001.geekbang.org/resource/image/a5/22/a50a6cb9d3df027aeda5ee8e53b75422.png" alt="">
SPEC提供的CPU基准测试程序就好像CPU届的“高考”通过数十个不同的计算程序对于CPU的性能给出一个最终评分。这些程序丰富多彩有编译器、解释器、视频压缩、人工智能国际象棋等等涵盖了方方面面的应用场景。感兴趣的话你可以点击[这个链接](https://www.spec.org/cpu2017/results/cpu2017.html)看看。
## 计算机的计时单位CPU时钟
虽然时间是一个很自然的用来衡量性能的指标,但是用时间来衡量时,有两个问题。
**第一个就是时间不“准”**。如果用你自己随便写的一个程序来统计程序运行的时间每一次统计结果不会完全一样。有可能这一次花了45ms下一次变成了53ms。
为什么会不准呢这里面有好几个原因。首先我们统计时间是用类似于“掐秒表”一样记录程序运行结束的时间减去程序开始运行的时间。这个时间也叫Wall Clock Time或者Elapsed Time就是在运行程序期间挂在墙上的钟走掉的时间。
但是计算机可能同时运行着好多个程序CPU实际上不停地在各个程序之间进行切换。在这些走掉的时间里面很可能CPU切换去运行别的程序了。而且有些程序在运行的时候可能要从网络、硬盘去读取数据要等网络和硬盘把数据读出来给到内存和CPU。所以说**要想准确统计某个程序运行时间,进而去比较两个程序的实际性能,我们得把这些时间给刨除掉**。
那这件事怎么实现呢Linux下有一个叫time的命令可以帮我们统计出来同样的Wall Clock Time下程序实际在CPU上到底花了多少时间。
我们简单运行一下time命令。它会返回三个值第一个是**real time**也就是我们说的Wall Clock Time也就是运行程序整个过程中流逝掉的时间第二个是**user time**也就是CPU在运行你的程序在用户态运行指令的时间第三个是**sys time**是CPU在运行你的程序在操作系统内核里运行指令的时间。而**程序实际花费的CPU执行时间CPU Time就是user time加上sys time**。
```
$ time seq 1000000 | wc -l
1000000
real 0m0.101s
user 0m0.031s
sys 0m0.016s
```
在我给的这个例子里你可以看到实际上程序用了0.101s但是CPU time只有0.031+0.016 = 0.047s。运行程序的时间里,只有不到一半是实际花在这个程序上的。
<img src="https://static001.geekbang.org/resource/image/0b/00/0b340db019d7e389a2bde4c237ee4700.jpg" alt="">
**其次即使我们已经拿到了CPU时间我们也不一定可以直接“比较”出两个程序的性能差异**。即使在同一台计算机上CPU可能满载运行也可能降频运行降频运行的时候自然花的时间会多一些。
除了CPU之外时间这个性能指标还会受到主板、内存这些其他相关硬件的影响。所以我们需要对“时间”这个我们可以感知的指标进行拆解把程序的CPU执行时间变成 CPU时钟周期数CPU Cycles和 时钟周期时间Clock Cycle的乘积。
我们先来理解一下什么是时钟周期时间。你在买电脑的时候一定关注过CPU的主频。比如我手头的这台电脑就是Intel Core-i7-7700HQ 2.8GHz这里的2.8GHz就是电脑的主频Frequency/Clock Rate。这个2.8GHz我们可以先粗浅地认为CPU在1秒时间内可以执行的简单指令的数量是2.8G条。
如果想要更准确一点描述这个2.8GHz就代表我们CPU的一个“钟表”能够识别出来的最小的时间间隔。就像我们挂在墙上的挂钟都是“滴答滴答”一秒一秒地走所以通过墙上的挂钟能够识别出来的最小时间单位就是秒。
而在CPU内部和我们平时戴的电子石英表类似有一个叫晶体振荡器Oscillator Crystal的东西简称为晶振。我们把晶振当成CPU内部的电子表来使用。晶振带来的每一次“滴答”就是时钟周期时间。
在我这个2.8GHz的CPU上这个时钟周期时间就是1/2.8G。我们的CPU是按照这个“时钟”提示的时间来进行自己的操作。主频越高意味着这个表走得越快我们的CPU也就“被逼”着走得越快。
如果你自己组装过台式机的话可能听说过“超频”这个概念这说的其实就相当于把买回来的CPU内部的钟给调快了于是CPU的计算跟着这个时钟的节奏也就自然变快了。当然这个快不是没有代价的CPU跑得越快散热的压力也就越大。就和人一样超过生理极限CPU就会崩溃了。
我们现在回到上面程序CPU执行时间的公式。
最简单的提升性能方案自然缩短时钟周期时间也就是提升主频。换句话说就是换一块好一点的CPU。不过这个是我们这些软件工程师控制不了的事情所以我们就把目光挪到了乘法的另一个因子——CPU时钟周期数上。如果能够减少程序需要的CPU时钟周期数量一样能够提升程序性能。
对于CPU时钟周期数我们可以再做一个分解把它变成“指令数×**每条指令的平均时钟周期数**Cycles Per Instruction简称CPI”。不同的指令需要的Cycles是不同的加法和乘法都对应着一条CPU指令但是乘法需要的Cycles就比加法要多自然也就慢。在这样拆分了之后我们的程序的CPU执行时间就可以变成这样三个部分的乘积。
因此,如果我们想要解决性能问题,其实就是要优化这三者。
<li>
时钟周期时间,就是计算机主频,这个取决于计算机硬件。我们所熟知的[摩尔定律](https://zh.wikipedia.org/wiki/%E6%91%A9%E5%B0%94%E5%AE%9A%E5%BE%8B)就一直在不停地提高我们计算机的主频。比如说我最早使用的80386主频只有33MHz现在手头的笔记本电脑就有2.8GHz在主频层面就提升了将近100倍。
</li>
<li>
每条指令的平均时钟周期数CPI就是一条指令到底需要多少CPU Cycle。在后面讲解CPU结构的时候我们会看到现代的CPU通过流水线技术Pipeline让一条指令需要的CPU Cycle尽可能地少。因此对于CPI的优化也是计算机组成和体系结构中的重要一环。
</li>
<li>
指令数,代表执行我们的程序到底需要多少条指令、用哪些指令。这个很多时候就把挑战交给了编译器。同样的代码,编译成计算机指令时候,就有各种不同的表示方式。
</li>
我们可以把自己想象成一个CPU坐在那里写程序。计算机主频就好像是你的打字速度打字越快你自然可以多写一点程序。CPI相当于你在写程序的时候熟悉各种快捷键越是打同样的内容需要敲击键盘的次数就越少。指令数相当于你的程序设计得够合理同样的程序要写的代码行数就少。如果三者皆能实现你自然可以很快地写出一个优秀的程序你的“性能”从外面来看就是好的。
## 总结延伸
好了学完这一讲对“性能”这个名词你应该有了更清晰的认识。我主要对于“响应时间”这个性能指标进行抽丝剥茧拆解成了计算机时钟周期、CPI以及指令数这三个独立的指标的乘积并且为你指明了优化计算机性能的三条康庄大道。也就是提升计算机主频优化CPU设计使得在单个时钟周期内能够执行更多指令以及通过编译器来减少需要的指令数。
在后面的几讲里面我会为你讲解具体怎么在电路硬件、CPU设计乃至指令设计层面提升计算机的性能。
## 课后思考
每次有新手机发布的时候,总会有一些对于手机的跑分结果的议论。乃至于有“作弊”跑分或者“针对跑分优化”的说法。我们能针对“跑分”作弊么?怎么做到呢?“作弊”出来的分数对于手机性能还有参考意义么?
欢迎留言和我分享你的思考和疑惑,你也可以把今天的内容分享给你的朋友,和他一起学习和进步。

View File

@@ -0,0 +1,108 @@
<audio id="audio" title="04 | 穿越功耗墙,我们该从哪些方面提升“性能”?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/fd/b0/fd962c97feb010f816548f488750e7b0.mp3"></audio>
上一讲在讲CPU的性能时我们提到了这样一个公式
这么来看如果要提升计算机的性能我们可以从指令数、CPI以及CPU主频这三个地方入手。要搞定指令数或者CPI乍一看都不太容易。于是研发CPU的硬件工程师们从80年代开始就挑上了CPU这个“软柿子”。在CPU上多放一点晶体管不断提升CPU的时钟频率这样就能让CPU变得更快程序的执行时间就会缩短。
于是从1978年Intel发布的8086 CPU开始计算机的主频从5MHz开始不断提升。1980年代中期的80386能够跑到40MHz1989年的486能够跑到100MHz直到2000年的奔腾4处理器主频已经到达了1.4GHz。而消费者也在这20年里养成了“看主频”买电脑的习惯。当时已经基本垄断了桌面CPU市场的Intel更是夸下了海口表示奔腾4所使用的CPU结构可以做到10GHz颇有一点“大力出奇迹”的意思。
## 功耗CPU的“人体极限”
然而计算机科学界从来不相信“大力出奇迹”。奔腾4的CPU主频从来没有达到过10GHz最终它的主频上限定格在3.8GHz。这还不是最糟的更糟糕的事情是大家发现奔腾4的主频虽然高但是它的实际性能却配不上同样的主频。想要用在笔记本上的奔腾4 2.4GHz处理器其性能只和基于奔腾3架构的奔腾M 1.6GHz处理器差不多。
于是这一次的“大力出悲剧”不仅让Intel的对手AMD获得了喘息之机更是代表着“主频时代”的终结。后面几代Intel CPU主频不但没有上升反而下降了。到如今2019年的最高配置Intel i9 CPU主频也只不过是5GHz而已。相较于1978年到2000年这20年里300倍的主频提升从2000年到现在的这19年CPU的主频大概提高了3倍。
<img src="https://static001.geekbang.org/resource/image/18/80/1826102a89e4cdd31f7573db53dd9280.png" alt=""><br>
奔腾4的主频为什么没能超过3.8GHz的障碍呢?答案就是功耗问题。什么是功耗问题呢?我们先看一个直观的例子。
一个3.8GHz的奔腾4处理器满载功率是130瓦。这个130瓦是什么概念呢机场允许带上飞机的充电宝的容量上限是100瓦时。如果我们把这个CPU安在手机里面不考虑屏幕内存之类的耗电这个CPU满载运行45分钟充电宝里面就没电了。而iPhone X使用ARM架构的CPU功率则只有4.5瓦左右。
我们的CPU一般都被叫作**超大规模集成电路**Very-Large-Scale IntegrationVLSI。这些电路实际上都是一个个晶体管组合而成的。CPU在计算其实就是让晶体管里面的“开关”不断地去“打开”和“关闭”来组合完成各种运算和功能。
想要计算得快一方面我们要在CPU里同样的面积里面多放一些晶体管也就是**增加密度**;另一方面,我们要让晶体管“打开”和“关闭”得更快一点,也就是**提升主频**。而这两者,都会增加功耗,带来耗电和散热的问题。
这么说可能还是有点抽象我还是给你举一个例子。你可以把一个计算机CPU想象成一个巨大的工厂里面有很多工人相当于CPU上面的晶体管互相之间协同工作。
为了工作得快一点我们要在工厂里多塞一点人。你可能会问为什么不把工厂造得大一点呢这是因为人和人之间如果离得远了互相之间走过去需要花的时间就会变长这也会导致性能下降。这就好像如果CPU的面积大晶体管之间的距离变大电信号传输的时间就会变长运算速度自然就慢了。
除了多塞一点人我们还希望每个人的动作都快一点这样同样的时间里就可以多干一点活儿了。这就相当于提升CPU主频但是动作快每个人就要出汗散热。要是太热了对工厂里面的人来说会中暑生病对CPU来说就会崩溃出错。
我们会在CPU上面抹硅脂、装风扇乃至用上水冷或者其他更好的散热设备就好像在工厂里面装风扇、空调发冷饮一样。但是同样的空间下装上风扇空调能够带来的散热效果也是有极限的。
因此在CPU里面能够放下的晶体管数量和晶体管的“开关”频率也都是有限的。一个CPU的功率可以用这样一个公式来表示
那么为了要提升性能我们需要不断地增加晶体管数量。同样的面积下我们想要多放一点晶体管就要把晶体管造得小一点。这个就是平时我们所说的提升“制程”。从28nm到7nm相当于晶体管本身变成了原来的1/4大小。这个就相当于我们在工厂里同样的活儿我们要找瘦小一点的工人这样一个工厂里面就可以多一些人。我们还要提升主频让开关的频率变快也就是要找手脚更快的工人。
<img src="https://static001.geekbang.org/resource/image/f5/ed/f59f2f33e308000cb5d2ad017f2ff8ed.jpeg" alt="">
但是功耗增加太多就会导致CPU散热跟不上这时我们就需要降低电压。这里有一点非常关键在整个功耗的公式里面功耗和电压的平方是成正比的。这意味着电压下降到原来的1/5整个的功耗会变成原来的1/25。
事实上从5MHz主频的8086到5GHz主频的Intel i9CPU的电压已经从5V左右下降到了1V左右。这也是为什么我们CPU的主频提升了1000倍但是功耗只增长了40倍。比如说我写这篇文章用的是Surface Go在这样的轻薄笔记本上微软就是选择了把电压下降到0.25V的低电压CPU使得笔记本能有更长的续航时间。
## 并行优化,理解阿姆达尔定律
虽然制程的优化和电压的下降在过去的20年里让我们的CPU性能有所提升。但是从上世纪九十年代到本世纪初软件工程师们所用的“面向摩尔定律编程”的套路越来越用不下去了。“写程序不考虑性能等明年CPU性能提升一倍到时候性能自然就不成问题了”这种想法已经不可行了。
于是从奔腾4开始Intel意识到通过提升主频比较“难”去实现性能提升边开始推出Core Duo这样的多核CPU通过提升“吞吐率”而不是“响应时间”来达到目的。
提升响应时间就好比提升你用的交通工具的速度比如原本你是开汽车现在变成了火车乃至飞机。本来开车从上海到北京要20个小时换成飞机就只要2个小时了但是在此之上再想要提升速度就不太容易了。我们的CPU在奔腾4的年代就好比已经到了飞机这个速度极限。
那你可能要问了接下来该怎么办呢相比于给飞机提速工程师们又想到了新的办法可以一次同时开2架、4架乃至8架飞机这就好像我们现在用的2核、4核乃至8核的CPU。
虽然从上海到北京的时间没有变但是一次飞8架飞机能够运的东西自然就变多了也就是所谓的“吞吐率”变大了。所以不管你有没有需要现在CPU的性能就是提升了2倍乃至8倍、16倍。这也是一个最常见的提升性能的方式**通过并行提高性能**。
这个思想在很多地方都可以使用。举个例子,我们做机器学习程序的时候,需要计算向量的点积,比如向量$W = [W_0, W_1, W_2, …, W_{15}]$和向量 $X = [X_0, X_1, X_2, …, X_{15}]$$W·X = W_0 * X_0 + W_1 * X_1 +$ $W_2 * X_2 + … + W_{15} * X_{15}$。这些式子由16个乘法和1个连加组成。如果你自己一个人用笔来算的话需要一步一步算16次乘法和15次加法。如果这个时候我们把这个任务分配给4个人同时去算$W_0W_3$, $W_4W_7$, $W_8W_{11}$, $W_{12}W_{15}$这样四个部分的结果,再由一个人进行汇总,需要的时间就会缩短。
<img src="https://static001.geekbang.org/resource/image/64/9d/64d6957ecaa696edcf79dc1d5511269d.jpeg" alt="">
但是,并不是所有问题,都可以通过并行提高性能来解决。如果想要使用这种思想,需要满足这样几个条件。
第一,需要进行的计算,本身可以分解成几个可以并行的任务。好比上面的乘法和加法计算,几个人可以同时进行,不会影响最后的结果。
第二,需要能够分解好问题,并确保几个人的结果能够汇总到一起。
第三,在“汇总”这个阶段,是没有办法并行进行的,还是得顺序执行,一步一步来。
这就引出了我们在进行性能优化中,常常用到的一个经验定律,**阿姆达尔定律**Amdahls Law。这个定律说的就是对于一个程序进行优化之后处理器并行运算之后效率提升的情况。具体可以用这样一个公式来表示
在刚刚的向量点积例子里4个人同时计算向量的一小段点积就是通过并行提高了这部分的计算性能。但是这4个人的计算结果最终还是要在一个人那里进行汇总相加。这部分汇总相加的时间是不能通过并行来优化的也就是上面的公式里面**不受影响的执行时间**这一部分。
比如上面的各个向量的一小段的点积需要100ns加法需要20ns总共需要120ns。这里通过并行4个CPU有了4倍的加速度。那么最终优化后就有了100/4+20=45ns。即使我们增加更多的并行度来提供加速倍数比如有100个CPU整个时间也需要100/100+20=21ns。
<img src="https://static001.geekbang.org/resource/image/f1/e5/f1d05ec439e6377803df741bc07b09e5.jpeg" alt="">
## 总结延伸
我们可以看到无论是简单地通过提升主频还是增加更多的CPU核心数量通过并行来提升性能都会遇到相应的瓶颈。仅仅简单地通过“堆硬件”的方式在今天已经不能很好地满足我们对于程序性能的期望了。于是工程师们需要从其他方面开始下功夫了。
在“摩尔定律”和“并行计算”之外,在整个计算机组成层面,还有这样几个原则性的性能提升方法。
1.**加速大概率事件**。最典型的就是过去几年流行的深度学习整个计算过程中99%都是向量和矩阵计算于是工程师们通过用GPU替代CPU大幅度提升了深度学习的模型训练过程。本来一个CPU需要跑几小时甚至几天的程序GPU只需要几分钟就好了。Google更是不满足于GPU的性能进一步地推出了TPU。后面的文章我也会为你讲解GPU和TPU的基本构造和原理。
2.**通过流水线提高性能**。现代的工厂里的生产线叫“流水线”。我们可以把装配iPhone这样的任务拆分成一个个细分的任务让每个人都只需要处理一道工序最大化整个工厂的生产效率。类似的我们的CPU其实就是一个“运算工厂”。我们把CPU指令执行的过程进行拆分细化运行也是现代CPU在主频没有办法提升那么多的情况下性能仍然可以得到提升的重要原因之一。我们在后面也会讲到现代CPU里是如何通过流水线来提升性能的以及反面的过长的流水线会带来什么新的功耗和效率上的负面影响。
3.**通过预测提高性能**。通过预先猜测下一步该干什么而不是等上一步运行的结果提前进行运算也是让程序跑得更快一点的办法。典型的例子就是在一个循环访问数组的时候凭经验你也会猜到下一步我们会访问数组的下一项。后面要讲的“分支和冒险”、“局部性原理”这些CPU和存储系统设计方法其实都是在利用我们对于未来的“预测”提前进行相应的操作来提升我们的程序性能。
好了,到这里,我们讲完了计算机组成原理这门课的“前情提要”。一方面,整个组成乃至体系结构,都是基于冯·诺依曼架构组成的软硬件一体的解决方案。另一方面,你需要明白的就是,这里面的方方面面的设计和考虑,除了体系结构层面的抽象和通用性之外,核心需要考虑的是“性能”问题。
接下来,我们就要开始深入组成原理,从一个程序的运行讲起,开始我们的“机器指令”之旅。
## 补充阅读
如果你学有余力,关于本节内容,推荐你阅读下面两本书的对应章节,深入研读。
1.《计算机组成与设计:软/硬件接口》第5版的1.7和1.10节,也简单介绍了功耗墙和阿姆达尔定律,你可以拿来细细阅读。
2.如果你想对阿姆达尔定律有个更细致的了解《深入理解计算机系统》第3版的1.9节不容错过。
## 课后思考
我在这一讲里面,介绍了三种常见的性能提升思路,分别是,加速大概率事件、通过流水线提高性能和通过预测提高性能。请你想一下,除了在硬件和指令集的设计层面之外,你在软件开发层面,有用到过类似的思路来解决性能问题吗?
欢迎你在留言区写下你曾遇到的问题,和大家一起分享、探讨。你也可以把今天的文章分享给你朋友,和他一起学习和进步。

View File

@@ -0,0 +1,71 @@
<audio id="audio" title="开篇词 | 为什么你需要学习计算机组成原理?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/2e/b8/2e1a27c3f76d6e4542b6aa5d998bcfb8.mp3"></audio>
你好,我是徐文浩,一个正在创业的工程师。目前主要是通过自然语言处理技术,为走向海外的中国企业提供英语的智能客服和社交网络营销服务。
2005年从上海交通大学计算机系毕业之后我一直以写代码为生。如果从7岁第一次在少年宫写程序开始算起到今天我的码龄快有30岁了。这些年里我在Trilogy Software写过各种大型企业软件在MediaV这样的广告科技公司从零开始搭建过支撑每天百亿流量的广告算法系统2015年我又加入了拼多多参与重写拼多多的交易系统。
这么多年一直在开发软件我深感软件这个行业变化太快了。语言上十年前流行Java这两年流行Go框架上前两年流行TensorFlow最近又流行PyTorch。我逐渐发现学习应用层的各种语言、框架好比在练拳法招式可以短期给予你回报而深入学习“底层知识”就是在练扎马步、核心肌肉力量是在提升你自己的“根骨”和“资质”。
正所谓“练拳不练功,到老一场空”。**如果越早去弄清楚计算机的底层原理,在你的知识体系中“储蓄”起这些知识,也就意味着你有越长的时间来收获学习知识的“利息”。虽然一开始可能不起眼,但是随着时间带来的复利效应,你的长线投资项目,就能让你在成长的过程中越走越快。**
## 计算机底层知识的“第一课”
如果找出各大学计算机系的培养计划,你会发现,它们都有差不多十来门核心课程。其中,“计算机组成原理”是入门和底层层面的第一课。
这是为什么呢我们直接用肉眼来看计算机是由CPU、内存、显示器这些设备组成的硬件但是计算机系的学生毕业之后大部分却都是从事各种软件开发工作。显然在硬件和软件之间需要一座桥梁而“计算机组成原理”就扮演了这样一个角色它既隔离了软件和硬件也提供了让软件无需关心硬件就能直接操作硬件的接口。
也就是说你只需要对硬件有原理性的理解就可以信赖硬件的可靠性安安心心用高级语言来写程序。无论是写操作系统和编译器这样的硬核代码还是写Web应用和手机App这样的应用层代码你都可以做到心里有底。
除此之外,组成原理是计算机其他核心课程的一个“导引”。学习组成原理之后,向下,你可以学习数字电路相关的课程,向上,你可以学习编译原理、操作系统这些核心课程。如果想要深入理解,甚至设计一台自己的计算机,体系结构是必不可少的一门课,而组成原理是计算机体系结构的一个入门版本。
<img src="https://static001.geekbang.org/resource/image/aa/73/aa5f644331319421eb7549d67d4f8773.jpeg" alt="">
所以说,无论你想要学习计算机的哪一门核心课程,之前你都应该先学习一下“计算机组成原理”,这样无论是对计算机的硬件原理,还是软件架构,你对计算机方方面面的知识都会有一个全局的了解。
学习这门“第一课”的过程会为你在整个软件开发领域中打开一扇扇窗和门让你看到更加广阔的天地。比如说明白了高级语言是如何对应着CPU能够处理的一条条指令能为你打开编译原理这扇门搞清楚程序是如何加载运行的能够让你对操作系统有更深入的理解。
因此学好计算机组成原理会让你对整个软件开发领域的全貌有一个系统了解也会给你带来更多的职业发展机会。像我自己的团队里有个小伙伴开始是做算法应用开发的因为有扎实的计算机基础知识后来就转去开发TVM这样的深度学习编译器了是不是很厉害
## 理论和实践相结合
说了这么多计算机组成原理的重要性,但到底该怎么学呢?接下来跟你分享我的心得。
我自己对计算机硬件的发展历史一直很感兴趣,所以,我读了市面上很多组成原理相关的资料。
互联网时代我们从来不缺少资料。无论是Coursera上北京大学的《计算机组成》开放课程还是图灵奖作者写的《计算机组成与设计硬件/软件接口》都珠玉在前是非常优秀的学习资料。不过“买书如山倒读书如抽丝”。从业这么多年周围想要好好学一学组成原理的工程师不少但是真的坚持下来学完、学好的却不多。大部分买来的书都是前面100页已经发黄了后面500页从来没有打开过更有不少非科班出身的程序员直接说“这些书根本看不懂”。
对这些问题,我都深有感触。从自己学习和工作的经验看,我找到了三个主要原因。
第一广。组成原理中的概念非常多每个概念的信息量也非常大。比如想要理解CPU中的算术逻辑单元也就是ALU是怎么实现加法的需要牵涉到如何把整数表示成二进制还需要了解这些表示背后的电路、逻辑门、CPU时钟、触发器等知识。
第二深。组成原理中的很多概念阐述开来就是计算机学科的另外一门核心课程。比如计算机的指令是怎么从你写的C、Java这样的高级语言变成计算机可以执行的机器码的如果我们展开并深入讲解这个问题就会变成《编译原理》这样一门核心课程。
第三,学不能致用。学东西是要拿来用的,但因为这门课本身的属性,很多人在学习时,常常沉溺于概念和理论中,无法和自己日常的开发工作联系起来,以此来解决工作中遇到的问题,所以,学习往往没有成就感,就很难有动力坚持下去。
考虑到这些,在这个专栏构思之初,我就给自己定了一个交付目标:**我要把这些知识点和日常工作、生活以及整个计算机行业的发展史联系起来,教你真正看懂、学会、记住组成原理的核心内容,教你更多地从“为什么”这个角度,去理解这些知识点,而不是只是去记忆“是什么”。**
对于这个专栏,具体我是这样设计的。
第一,我把组成原理里面的知识点,和我在应用开发和架构设计中遇到的实际案例,放到一起进行印证,通过代码和案例,让你消化理解。
比如为什么Disruptor这个高性能队列框架里要定义很多没有用的占位变量呢其实这是为了确保我们唯一关心的参数能够始终保留在CPU的高速缓存里面而高速缓存比我们的内存要快百倍以上。
第二,我会尽可能地多举一些我们日常生活里面的例子,让你理解计算机的各个组件是怎么运作的。在真实的开发中,我们会遇到什么问题,这些问题产生的根源是什么。让你从知识到应用,最终又回到知识,让学习和实践之间形成一道闭环。
计算机组成中很多组件的设计,都不是凭空发明出来,它们中的很多都来自现实生活中的想法和比喻。而底层很多硬件设计和开发的思路,其实也和你进行软件架构的开发设计和思路是一样的。
比如说在硬件上我们是通过最基本的与、或、非、异或门这些最基础的门电路组合形成了强大的CPU。而在面向对象和设计模式里我们也常常是通过定义基本的Command然后组合来完成更复杂的功能再比如说CPU里面的冒险和分支预测的策略就好像在接力赛跑里面后面几棒的选手早点起跑如果交接棒没有问题自然占了便宜但是如果没能交接上就会吃个大亏。
第三,在知识点和应用之外,我会多讲一些计算机硬件发展史上的成功和失败,让你明白很多设计的历史渊源,让你更容易记住“为什么”,更容易记住这些知识点。
比如说奔腾4的失败就是受限于超长流水线带来的散热和功耗问题而移动时代ARM的崛起则是因为Intel的芯片功耗太大不足以在小小的手机里放下足够支撑1天的电池。计算机芯片的兴盛和衰亡往往都是因为我们的计算机遇到了“功耗墙”这个散热和能耗上的挑战。而现代的云计算数据中心的设计到选址也是围绕功耗和散热的。理解了这些成功和失败背后的原因你自然记住了这些背后的知识点。
最后在这三种帮助你理解“为什么”的方法之上我会把整个的计算机组成原理通过指令、计算、CPU、存储系统和I/O串起来。通过一个程序的执行过程进行逐层分解让你能对整个系统有一个全貌的了解。
我希望这个专栏,不仅能够让你学好计算机组成原理的知识,更能够成为引领你进入更多底层知识的大门,让你有动力、有方法、更深入地去进一步学习体系结构、操作系统、编译原理这样的课程,成为真正的“内家高手”。
“人生如逆旅,我亦是行人”。学习总不会是一件太轻松的事情,希望在这个专栏里,你能和我多交流,坚持练完这一手内功。
下面,你可以讲一讲,你对于计算机组成原理的认识是怎样的?在之前工作中,哪些地方用到了计算机组成原理相关的知识呢?欢迎写在留言区,我们一起交流。