计算机组成 -- IO_WAIT
IO性能
硬盘厂商的性能报告:响应时间(Response Time)、数据传输率(Data Transfer Rate)
HDD硬盘一般用的是SATA 3.0的接口;SSD硬盘通常会用两种接口,一部分用SATA 3.0接口,另一部分用PCI Express接口
数据传输率
SATA 3.0接口的带宽是6Gb/s ≈ 768MB/s
日常用的HDD硬盘的数据传输率,一般在200MB/s
SATA 3.0接口的SSD的数据传输率差不多是500MB/s
PCI Express接口的SSD,读取时的数据传输率能到2GB/s,写入时的数据传输率也能有1.2GB/s,大致是HDD的10倍
响应时间
程序发起一个硬盘的读取或写入请求,直到请求返回的时间
SSD的响应时间大致在几十微秒这个级别,HDD的响应时间大致在十几毫秒这个级别,相差几十倍到几百倍
IOPS
每秒读写的次数,相对于响应时间,更关注IOPS这个性能指标
在顺序读写和随机读写的情况下,硬盘的性能是完全不同的
IOPS和DTR才是IO性能的核心指标
在实际的应用开发当中,对于数据的访问 ...
计算机组成 -- IO设备
接口 + 设备 – 适配器模式
大部分的输入输出设备,都有两个组成部分,一个是接口,另一个是实际的IO设备
硬件设备并不是直接接入到总线上和CPU通信的,而是通过接口,用接口连接到总线上,再通过总线和CPU通信
串行接口、USB接口等都是计算机主板上内置的各个接口,实际使用的硬件设备,都需要插入到这些接口上,才能和CPU通信
接口本身是一块电路板,CPU不需要和实际的硬件设备打交道,只需要和这个接口电路板打交道
设备里面的三类寄存器(状态寄存器、命令寄存器、数据寄存器),都在接口电路上,而不在实际的设备上
除了内置在主板上的接口外,有些接口可以集成在设备上 – IDE(Integrated Device Electronics)硬盘
设备的接口电路直接在设备上,只需要通过一个线缆,把集成了接口的设备连接到主板上即可
接口和设备分离:各种输入输出设备的制造商,根据接口的控制协议,来设计各种外设
Windows设备管理器
Devices:着重实际的IO设备本身
Controllers:着重输入输出设备接口里面的控制电路
Adaptors:着重接口作为一个适配器后面可以插上不同的实际设备
CPU控 ...
计算机组成 -- 总线
设计来源:降低复杂度
计算机内部有很多不同的硬件设备,除了CPU和内存,还有大量的输入输出设备
如果各个设备间的通信,都是互相之间单独进行的,如果有N个不同的设备,他们之间需要各自单独连接,那么系统复杂度为**$N^2$**
为了简化系统的复杂度,引入了总线,把**$N^2$复杂度,变成了$N$**的复杂度
CPU想要和什么设备通信,通信的指令是什么,对应的数据是什么,都发送到这个线路上
设备想要向CPU发送什么消息,也发送到这条线路上
这条线路好像一个高速公路,各个设备和其他设备之间,不需要单独建公路,只需要建一条小路通向这条高速公路即可
总线(Bus),其实就是一组线路,CPU、内存、输入输出设备,都是通过这组线路,进行相互间通信的
设计模式:事件总线
在大型系统开发的过程中,经常会用到一种叫作事件总线(Event Bus)的设计模式
系统中的各个组件之间需要相互通信,如果两两之间单独去定义协议,复杂度为**$N^2$**
各个模块触发对应的事件,并把事件对象发送到总线上,即每个模块都是一个发布者(Publisher)
各个模块也会把自己注册到总线上,去监听总线上的事件
并根据事件的对象类型 ...
计算机组成 -- 内存
程序装载
在Linux或Windows下,程序并不能直接访问物理内存
内存需要被分成固定大小的页,然后通过虚拟内存地址到物理内存地址的地址转换,才能到达实际存放数据的物理内存位置
程序看到的内存地址,都是虚拟内存地址
地址转换简单页表
页表(Page Table,一一映射):<**虚拟**内存的页, **物理**内存的页>
页表:把一个内存地址分成页号(Directory)和偏移量(Offset)两部分
前面的高位,是内存地址的页号;后面的低位,是内存地址的偏移量
页表只需要保留虚拟内存地址的页号和物理内存地址的页号之间的映射关系即可
同一个页里面的内存,在物理层面是连续的
对于32位的内存地址,4KB大小的页,需要保留20位的高位,12位的低位
内存地址转换步骤
把虚拟内存地址,切分成页号和偏移量
从页表里面,查询出虚拟页号对应的物理页号
直接拿到物理页号,加上前面的偏移量,得到物理内存地址
空间问题
32位的内存地址空间,页表一共需要记录2^20个到物理页号的映射关系
一个页号是完整的32位的4 Bytes,一个页表就需要4MB的空间(2^20 * 4 Bytes ...
计算机组成 -- MESI协议
缓存一致性问题
iPhone降价了,要把iPhone最新的价格更新到主内存里,为了性能问题,采用写回策略
先把数据写入到L2 Cache里,然后把Cache Block标记为脏的
此时数据其实没有被同步到L3 Cache或主内存里
1号核心希望在这个Cache Block要被交换出去的时候,数据才写入到主内存里
此时2号核心尝试从内存里读取iPhone的价格,就会读取一个错误的价格
缓存一致性问题:1号核心和2号核心的缓存,此时是不一致的
同步机制能够达到的目标
写传播(Write Propagation)
在一个CPU核心里面的Cache数据更新,必须能够传播到其他对应节点的Cache Line里
事务串行化(Transaction Serialization)
在一个CPU核心里面的读取和写入,在其他节点看起来,顺序是一样的
事务串行化
1号核心先把iPhone的价格改成5000,差不多时间,2号核心把iPhone的价格改成6000,这两个修改会传播到3号核心和4号核心
3号核心先收到2号核心的写传播,再收到1号核心的写传播;4号核心刚好相反
虽然写传播做到了,但各个 ...
计算机组成 -- 高速缓存
缓存行1234567$ sysctl -a | grep -E 'cacheline|cachesize'hw.cachesize: 17179869184 32768 262144 6291456 0 0 0 0 0 0hw.cachelinesize: 64 # 64 Byteshw.l1icachesize: 32768 # 32 KBhw.l1dcachesize: 32768 # 32 KBhw.l2cachesize: 262144 # 256 KBhw.l3cachesize: 6291456 # 6 MB
1234567891011121314151617181920212223242526public static void f1() { int[] arr = new int[64 * 1024 * 1024]; long start = System.currentTimeMillis(); for (int i = 0; i < arr.length; i++) { arr[i] *= 3; ...
计算机组成 -- 局部性原理
局部性原理
局部性原理:时间局部性(temporal locality)+空间局部性(spatial locality)
时间局部性:如果一个数据被访问了,在短时间内还会被再次访问
空间局部性:如果一个数据被访问了,和它相邻的数据也很快会被访问
亚马逊的商品假设:总共6亿商品,每件商品需要4MB的存储空间,总共需要2400TB的数据存储
存储成本
如果所有数据都放在内存里面,需要3600万美元
$3600 = \frac{2400TB}{1MB}×0.015$
如果只在内存里存放前1%的热门商品,其余的放在HDD上,存储成本可以下降为45.6万美元,即原来成本的1.3%
$45.5 ≈ 3600 \times 0.01+3600 \times 0.99 \times \frac{0.00004}{0.015}$
时间局部性
时间局部性:LRU(Least Recently Used)缓存算法
热门商品被访问得多,就会始终被保存在内存里,而冷门商品被访问得少,就只存放在HDD硬盘上
越是热门的商品,越容易在内存中找到,能更好地利用内存的随机访问性能
假设日活为1亿,活跃用户每人每天 ...
计算机组成 -- 存储层次结构
SRAM
CPU类比成计算机的大脑;而正在思考的东西,可以类比成CPU中的寄存器(Register)
寄存器更像是CPU本身的一部分,存放极其有限的信息,但速度非常快,和CPU同步
大脑中的记忆,类比成CPU Cache(高速缓存)
CPU Cache使用的芯片是SRAM(Static Random-Access Memory,静态随机存取存储器)
静态:只要处于通电状态,里面的数据就能保持存在,而一旦断电,里面的数据就会丢失
在SRAM里,1个比特的数据,需要6~8个晶体管
所以SRAM的存储密度不高,同样的物理空间下,能够存储的数据有限
SRAM的电路简单,所以访问速度非常快
在CPU里,通常会有L1、L2、L3这三层高速缓存
L1 Cache
每个CPU核心都有一块独占的L1 Cache,通常分为指令缓存和数据缓存
L1 Cache通常嵌在CPU核心的内部
L2 Cache
L2 Cache同样是每个CPU核心都有,但往往不在CPU核心的内部,因此L2 Cache的访问速度会比L1 Cache稍慢
L3 Cache
L3 Cache通常是多个CPU核心共用的,尺寸更大,访问速度更慢
...
计算机组成 -- 虚拟机
解释型虚拟机
要模拟一个计算机系统,最简单的办法,就是兼容这个计算机系统的指令集
开发一个应用程序,运行在操作系统上,该应用程序可以识别想要模拟的计算机系统的程序格式和指令,然后一条条去解释执行
原先的操作系统称为宿主机(Host),有能力模拟指令执行的软件称为模拟器(Emulator)
实际运行在模拟器上被虚拟出来的系统,称为客户机(Guest VM)
这种方式和运行Java程序的JVM比较类似,只不过JVM运行的是Java中间代码(字节码),而不是一个特定的计算机系统的指令
真实的应用案例:Android模拟器、游戏模拟器
优势
模拟的系统可以跨硬件
Android用的是ARM CPU,开发机用的是Intel X86 CPU,两边的CPU指令集是不一样的,但一样可以正常运行
劣势
无法做到精确模拟
很多老旧的硬件的程序运行,需要依赖特定的电路乃至电路特有的时钟频率,很难通过软件做到100%模拟
性能很差
并不是直接把指令交给CPU去执行,而是要经过各种解释和翻译的工作
编译优化:Java的JIT
把本来解释执行的指令,编译成Host可以直接运行的指令
全虚拟化
全虚拟化 ...
计算机组成 -- CISC + RISC
历史
在早期,所有的CPU都是CISC
实际的计算机设计和制造会严格受到硬件层面的限制,当时的计算很慢,存储空间很小
为了让计算机能够尽量多地工作,每个字节乃至每个比特都特别重要
CPU指令集的设计,需要仔细考虑硬件限制,为了性能考虑,很多功能都直接通过硬件电路来完成
为了少用内存,指令长度也是可变的
常用的指令要短一些,不常用的指令要长一些
用尽量少的内存空间,存储尽量多的指令
计算机的性能越来越好,存储的空间也越来越大,70年代末,RISC出现
CPU运行程序,80%的运行代码都在使用20%的简单指令
对比
CISC
RISC
以硬件为中心的指令集设计
以软件为中心的指令集设计
通过硬件实现各类程序指令
通过编译器实现简单指令组合,完成复杂功能
更高效地使用内存和寄存器 – 一开始都是CISC,硬件资源非常珍贵
需要更大的内存和寄存器,并更频繁地使用
可变的指令集,支持更复杂的指令长度
简单、定长的指令
大量指令数
少量指令数
CISC的缺点
在硬件层面,如果想要支持更多的复杂指令,CPU里面的电路就要更复杂,设计起来更困难
更复杂的电路,在散热和功耗层面 ...