mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2025-11-19 15:43:44 +08:00
mod
This commit is contained in:
109
极客时间专栏/深入浅出计算机组成原理/入门篇/01 | 冯·诺依曼体系结构:计算机组成的金字塔.md
Normal file
109
极客时间专栏/深入浅出计算机组成原理/入门篇/01 | 冯·诺依曼体系结构:计算机组成的金字塔.md
Normal 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 Unit,ALU)和处理器寄存器(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)。那么图灵机和冯·诺依曼机是两种不同的计算机么?图灵机是一种什么样的计算机抽象呢?
|
||||
|
||||
欢迎留言和我分享你的思考和疑惑,你也可以把今天的内容分享给你的朋友,和他一起学习和进步。
|
||||
110
极客时间专栏/深入浅出计算机组成原理/入门篇/02 | 给你一张知识地图,计算机组成原理应该这么学.md
Normal file
110
极客时间专栏/深入浅出计算机组成原理/入门篇/02 | 给你一张知识地图,计算机组成原理应该这么学.md
Normal 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程序,是怎么在计算机里面跑起来的。这里面,你既需要了解我们的程序是怎么通过编译器和汇编器,变成一条条机器指令这样的编译过程(如果把编译过程展开的话,可以变成一门完整的编译原理课程),还需要知道我们的操作系统是怎么链接、装载、执行这些程序的(这部分知识如果再深入学习,又可以变成一门操作系统课程)。而这一条条指令执行的控制过程,就是由计算机五大组件之一的**控制器**来控制的。
|
||||
|
||||
在计算机的计算部分,你要从二进制和编码开始,理解我们的数据在计算机里的表示,以及我们是怎么从数字电路层面,实现加法、乘法这些基本的运算功能的。实现这些运算功能的ALU(Arithmetic 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自发为你做的流水线、指令级并行和分支预测,还有对应访问到的硬盘、内存,以及加载到高速缓存中的数据,这些都对应着我们学习中的一个个知识点。建议你自己脑子里过一遍,最好是口头表述一遍或者写下来,这样对你彻底掌握这些知识点都会非常有帮助。
|
||||
|
||||
其次,**写一些示例程序来验证知识点。**计算机科学是一门实践的学科。计算机组成中的大量原理和设计,都对应着“性能”这个词。因此,通过把对应的知识点,变成一个个性能对比的示例代码程序记录下来,是把这些知识点融汇贯通的好方法。因为,相比于强记硬背知识点,一个有着明确性能对比的示例程序,会在你脑海里留下更深刻的印象。当你想要回顾这些知识点的时候,一个程序也更容易提示你把它从脑海深处里面找出来。
|
||||
|
||||
最后,**通过和计算机硬件发展的历史做对照**。计算机的发展并不是一蹴而就的。从第一台电子计算机ENIAC(Electronic 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,则是通过实际应用程序,来理解计算机组成原理中各个知识点的最好范例了。
|
||||
|
||||
《编码:隐匿在计算机软硬件背后的语言》和《程序员的自我修养:链接、装载和库》是理解计算机硬件和操作系统层面代码执行的优秀阅读材料。
|
||||
|
||||
## 总结延伸
|
||||
|
||||
学习不是死记硬背,学习材料也不是越多越好。到了这里,希望你不要因为我给出了太多可以学习的材料,结果成了“松鼠症”患者,光囤积材料,却没有花足够多的时间去学习这些知识。
|
||||
|
||||
我工作之后一直在持续学习,在这个过程中,我发现最有效的办法,**不是短时间冲刺,而是有节奏地坚持,希望你能够和专栏的发布节奏同步推进,做好思考题,并且多在留言区和其他朋友一起交流**,就更容易能够“积小步而至千里”,在程序员这个职业上有更长足的发展。
|
||||
|
||||
好了,对于学习资料的介绍就到这里了。希望在接下来的几个月里,你能和我一起走完这趟“计算机组成”之旅,从中收获到知识和成长。
|
||||
|
||||
## 课后思考
|
||||
|
||||
今天我为你梳理计算机组成的知识地图,也讲了我认为学习这个专栏的一些方法,听了这么多,那么你打算怎么学习这个专栏呢?
|
||||
|
||||
欢迎你在留言区写下你的学习目标和学习计划,和大家一起交流,也欢迎你把今天的文章分享给你的朋友,互相督促,共同成长。
|
||||
114
极客时间专栏/深入浅出计算机组成原理/入门篇/03 | 通过你的CPU主频,我们来谈谈“性能”究竟是什么?.md
Normal file
114
极客时间专栏/深入浅出计算机组成原理/入门篇/03 | 通过你的CPU主频,我们来谈谈“性能”究竟是什么?.md
Normal 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/30,Sandy 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设计,乃至指令设计层面,提升计算机的性能。
|
||||
|
||||
## 课后思考
|
||||
|
||||
每次有新手机发布的时候,总会有一些对于手机的跑分结果的议论。乃至于有“作弊”跑分或者“针对跑分优化”的说法。我们能针对“跑分”作弊么?怎么做到呢?“作弊”出来的分数对于手机性能还有参考意义么?
|
||||
|
||||
欢迎留言和我分享你的思考和疑惑,你也可以把今天的内容分享给你的朋友,和他一起学习和进步。
|
||||
|
||||
|
||||
108
极客时间专栏/深入浅出计算机组成原理/入门篇/04 | 穿越功耗墙,我们该从哪些方面提升“性能”?.md
Normal file
108
极客时间专栏/深入浅出计算机组成原理/入门篇/04 | 穿越功耗墙,我们该从哪些方面提升“性能”?.md
Normal 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能够跑到40MHz,1989年的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 Integration,VLSI)。这些电路,实际上都是一个个晶体管组合而成的。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 i9,CPU的电压已经从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_0~W_3$, $W_4~W_7$, $W_8~W_{11}$, $W_{12}~W_{15}$这样四个部分的结果,再由一个人进行汇总,需要的时间就会缩短。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/64/9d/64d6957ecaa696edcf79dc1d5511269d.jpeg" alt="">
|
||||
|
||||
但是,并不是所有问题,都可以通过并行提高性能来解决。如果想要使用这种思想,需要满足这样几个条件。
|
||||
|
||||
第一,需要进行的计算,本身可以分解成几个可以并行的任务。好比上面的乘法和加法计算,几个人可以同时进行,不会影响最后的结果。
|
||||
|
||||
第二,需要能够分解好问题,并确保几个人的结果能够汇总到一起。
|
||||
|
||||
第三,在“汇总”这个阶段,是没有办法并行进行的,还是得顺序执行,一步一步来。
|
||||
|
||||
这就引出了我们在进行性能优化中,常常用到的一个经验定律,**阿姆达尔定律**(Amdahl’s 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节不容错过。
|
||||
|
||||
## 课后思考
|
||||
|
||||
我在这一讲里面,介绍了三种常见的性能提升思路,分别是,加速大概率事件、通过流水线提高性能和通过预测提高性能。请你想一下,除了在硬件和指令集的设计层面之外,你在软件开发层面,有用到过类似的思路来解决性能问题吗?
|
||||
|
||||
欢迎你在留言区写下你曾遇到的问题,和大家一起分享、探讨。你也可以把今天的文章分享给你朋友,和他一起学习和进步。
|
||||
|
||||
|
||||
71
极客时间专栏/深入浅出计算机组成原理/入门篇/开篇词 | 为什么你需要学习计算机组成原理?.md
Normal file
71
极客时间专栏/深入浅出计算机组成原理/入门篇/开篇词 | 为什么你需要学习计算机组成原理?.md
Normal 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串起来。通过一个程序的执行过程进行逐层分解,让你能对整个系统有一个全貌的了解。
|
||||
|
||||
我希望这个专栏,不仅能够让你学好计算机组成原理的知识,更能够成为引领你进入更多底层知识的大门,让你有动力、有方法、更深入地去进一步学习体系结构、操作系统、编译原理这样的课程,成为真正的“内家高手”。
|
||||
|
||||
“人生如逆旅,我亦是行人”。学习总不会是一件太轻松的事情,希望在这个专栏里,你能和我多交流,坚持练完这一手内功。
|
||||
|
||||
下面,你可以讲一讲,你对于计算机组成原理的认识是怎样的?在之前工作中,哪些地方用到了计算机组成原理相关的知识呢?欢迎写在留言区,我们一起交流。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user