计算机体系中数据存储设备主要分为易失性设备(如内存DRAM)和非易失性设备(如磁盘,固态硬盘等),计算机体系架构中的存储层次结构(memory hierarchy),操作系统中的文件系统(file system)等会涉及如何管理存储设备,以便获得更好的性能。本文作为基础篇介绍常用的存储设备,为后续讲解进行铺垫。
第一部分介绍市面上常见的存储设备,主要包括易失性和非易失性存储设备。易失性存储设备因为断电后数据丢失主要用于作为缓存或内存使用,而非易失性存储设备断电后仍能保证数据不丢失,主要用于持久化存储数据。
其组成部分和U盘类似,包括PCB、主控芯片、缓存芯片、存储媒介Flash或DRAM。基于闪存的存储媒介在断电后仍然能够保存数据,而基于DRAM为存储媒介的固态硬盘要求有独立电源供应保持不断电,Flash SSD是市面上主流产品。相对于传统硬盘,优点是安静、防震、省点、性能好,由于无需机械寻道,获取任意位置数据的时间都相同,使得固态硬盘IO性能远高于传统硬盘。Flash晶片有NOR Flash和NAND Flash两种,由于NAND Flash容量大,写和擦除速度快,因此大部分固态硬盘都是NAND Flash。Flash的基本电路单元是浮空栅极晶体管,每个单元称为一个cell,cell类型主要包括:1.single level cell(SLC);2.multi level cell(MLC);3.Triple level cell(TLC); 4.Quard level cell (QLC)(备注: 历史原因MLC专指2层单元), 单元层次越多,容量越大,价格越便宜。下面简单介绍SSD的工作原理,SSD主控芯片主要负责:错误纠正和检查ECC,磨损平衡(Wear Leveling),坏块映射(Bad block mapping),缓存控制,垃圾回收,加密等。传统硬盘的读写基本单元是扇区,通常是512字节;而固态硬盘的基本读写单元称为page,每个page由一堆cell组成,page分为2部分,一部分存储数据,一部分存储ECC及其他信息;不同型号SSD的page不一样,一般page由2048字节数据区和64字节空闲区组成,共2112字节。每128个page组成一个block。由于电路构造原因,block是数据擦除最小单元,不同于传统磁盘,block写入数据后不能直接覆盖,需要擦除后变为空白block才允许写入新数据。SSD控制器会维护一个mapping table,记录逻辑地址和物理地址的映射关系,当读取逻辑Page页时,需要通过mapping table找到物理Page页地址,然后在Flash读取相应数据。由于Flash无法覆盖写,因此容易产生很多垃圾数据,为了解决这个问题,SSD有垃圾回收机制,在写入新数据时,如果SSD控制器无法找到找到可以写入的空Page时,会选择若干block,将仍然有效的page复制到某个block,然后将原来的block进行擦除以便回收空间,由于有这个垃圾回收过程,因此SSD在写入大量数据后,也会变慢。为了保持gc时有空白block能够用于迁移数据,一般SSD都会有7%的预留空间(Over Provisioning),这部分空间无法对外使用,由SSD用来进行垃圾回收、备用坏块等。预留空间越大,垃圾回收越快,Flash损耗越小,Flash损耗用P/E数(Program/Erase Count)来衡量。考虑到SSD可能会对某些block进行频繁擦写,比如操作系统日志可能频繁更新,导致不同block的损耗不均衡,为了保证所有block的损耗是均衡了,采用了磨损平衡(Wear Leveling)机制。
因为Flash无法覆写即垃圾回收机制,因此引入了写放大(write amplification)机制,写放大倍数=闪存写入数据量/主控写入数据量,当flash是空盘时,写放大倍数为1;当出现垃圾回收时,由于写入数据时要迁移page,写放大倍数可能大于1.
表1: 设备大小和性能对比, 由于不同设备性能存在差异, 表1描述了相关设备的典型参数.
寄存器 | 缓存SDRAM | 主内存DRAM | 固态硬盘SSD | 硬盘HDD | |
---|---|---|---|---|---|
大小 | 1000~4000byte | 64KB~64MB | 4~256GB | 256GB~1TB | 16~64TB |
性能 | 0.2~0.3ns | 1~20ns | 50~100ns | 0.025~0.2ms | 5~10ms |
价格美元/GB, 2012年数据 | * | 500~1000 | 10~20 | 0.75~1 | 0.05~0.1 |
在开始本章节前, 首先给出计算机发展过程中的3个知名定律或原则:
虽然近年摩尔定律和Denard scaling开始失效, CPU的增长已经放缓, 但CPU和存储器之间性能仍存在指数级差异。
理想情况下, 我们希望拥有无限大的内存容量, 这样就可以访问任意一个特定的机器字(指令或数据), 但考虑到实际情况, 我们不得不设计一个分层结构的存储器, 每一层比前一层容量更大, 但访问速度也更慢。这种经济而又实用的解决方案就是存储器层次结构(memory hierarchy), 这一方案充分利用了局部性原理以及"在给定技术和功耗要求下, 硬件尺寸越小, 性能越快"的准则。
存储结构划分为几个级别, 约接近CPU, 硬件容量越小, 速度越快, 而单位字节成本也越高. 通过这种设计, 目标是提供一种存储体系, 每字节的成本和最便宜的存储器级别相同, 而速度与最快速的级别相同. 低层级存储器包含上一级存储器中的数据, 层次结构的最低一层必需满足这一包含性质. 计算机体系架构中存储器结构如下所示:
L1 | L2 | L3 | 内存 | |
---|---|---|---|---|
大小 | 64kB | 256kB | 16~64MB | 32~256GB |
速度 | 1ns | 3~10ns | 10~20ns | 50~100ns |
缓存布局
主内存和缓存中移动数据的基本单位是块(block, 或称为行line), 一般包括多个字, 主要是为了性能考虑, 利用了空间局部性原理。设计时需要考虑块如何放置在缓存中, 常见方案是组相连(set associative), 组由多个块组成, 查找一个块时, 首先需要映射到组, 然后在组内进行搜索. 组的映射: [块地址] MOD [缓存中组数], 如果组中有n个块, 则称为n路组相联(n-way set associative), 如果n为1, 则是直接映射(direct mapped), 如果只有一个组, 则称为全相联(fully associative)
写缓存策略
为了保证缓存和存储器中副本一致, 通常有3种策略
直写(write through): 更新缓存, 并直接写入存储器进行更新
回写(write back): 仅更新缓存, 此时块被标记为脏块, 只有在块替换时才将其更新回存储器
后写(write behind): 仅更新缓存, 并记录脏记录列表, 定期将脏记录刷回存储器
为了提高写性能, 两种策略都是用了写缓冲(write buffer), 将数据写入缓冲区后, 马上进行缓存操作, 而无需等待将数据写入存储器.
采用只写能更好的解决缓存一致性问题, 但直写代价高, 通常在L1采用直写, 而L2/L3采用回写方式
通过缺失率(miss rate)来衡量缓存组织的性能, 缺失率=缓存中未找到数据请求数/总请求数.
缺失产生的类型(4C模式)主要分为:
1.强制(Compulsory):指首次访问时,数据不在缓存中
2.容量(Capacity):指缓存无法包含所有的数据块,因此有些块需要淘汰, 在访问时需重新放回缓存
3.冲突(Conflict):指多个数据块可能映射到一个组中,不同块的访问混在一起,可能导致需要先将一个块淘汰,之后重新加载
4.一致性(coherency):指在多处理器和多线程中,需要将缓存刷新以保持多个处理器缓存中的数据一致性。
存储器平均访问时间=命中时间+缺失率×缺失代价
提升缺失率的方法包括:
(1) 增大块
(2) 增大缓存
(3) 提高相联程度
(4) 采用多级缓存 在加快缓存命中速度以跟上高速时钟频率和加大缓存,缩小处理器访问和存储器访问之间的差距中间很难抉择, 但是可以通过多级缓存方案, L1缓存足够小, 以便跟上时钟频率; L2或L3缓存足够大, 以便能够更多容纳数据减小对存储器的访问
(5)读缺失优先级高于写入操作 读取缺失时先检查缓冲区的内容, 在没有冲突时, 则在写入操作之前读取数据从而降低写缺失
(6)缓存索引期间避免地址转换 由于需要将处理器的虚拟地址转换为存储器的物理地址, 转换旁视缓冲区(translation look aside buffer, TLB)
虚拟存储器中基本单位是页(Page), 一般大小是4KB, 虚拟地址空间中的虚拟页(Virtual Page)和主内存中的物理页(Physical Page)大小一样, 虚拟地址空间大小由处理器的地址大小决定, 为了通过虚拟地址定位页内位置, 需要2^p位, 我们选择虚拟地址的低p位作为偏移量, 选择低位的原因是利用空间局部性原理, 相邻地址对应同一个page或相邻page, 同样选择物理地址的低p位作为偏移量. 虚拟地址由虚拟页号(virtual page number, VPN)和偏移量(offset, VPO)组成, 物理地址由物理页号(physical page number, PPN)和偏移量(offset, PPO)组成, 需要维护一个单独的页表(page table)来实现虚拟页号到物理页号的映射. 当物理页在主内存中不存在时, 则出现页缺失(page fault), 此时会触发页缺失异常, 由处理器中断, 切换到页中断处理函数(page fault handler), 从辅助存储中加载对应的页数据到主内存. 如果主内存中没有空间用于存储新的页数据时, 则需要采用页替换策略选择替换页, 一般采用最近最少使用(LRU)策略, 将LRU页内容换出到辅助内存, 将空出的物理页用于存储新访问的缺失虚拟页内容, 这种处理模式称为页面调度(paging), 这种调度也称为按需调度(demand paging), 由程序的需要将相应页从辅助内存加载到主内存.
程序工作集(working set)是指程序运行需要保持在主内存中活动页集合, 程序开始运行后, 通过不断的页缺失调度, 工作集会逐步稳定, 页缺失也会逐步变少, 从而使得程序只需访问主内存即可运行, 这也是局部性原理来保证的, 如果程序编写的不好, 可能会频繁发生页缺失, 造成频繁从辅助内存读取数据到主内存, 这种现象称为抖动(thrashing), 需要尽量避免.
页表中的每一项(page table item, PTE)除了维护虚拟页号到物理页号的映射, 还会记录额外信息
脏位(dirty bit): 1表示数据从辅助存储加载到内存后数据发生修改, 如果脏页发生替换, 需要首先将脏页内容刷会辅助存储
驻留位(resident bit): 1表示页驻留在主内存, 0表示未驻留(可能在磁盘或者未分配).
下图是页缺失的例子, 当cpu访问的虚拟地址对应的物理地址页缺失, 数据在内存时, 此时主内存已满, 会选择一个LRU页, 将该页内容放回磁盘, 缺失页数据加载到物理内存中
由于地址转换VtoP在每次CPU进行内存访问时都会发生, 为了提升查找page table的性能, 可以考虑专用硬件设备, 如SRAM来作为缓存, 避免CPU每次都查找主内存. 这种专用设备被称为转址旁路缓存(translation lookaside buffer , TLB), 位于CPU中. TLB非常小且速度快, 采用全相联缓存布局, 在TLB命中时只需一次物理内存访问, 通常命中率高达99%.
上下文(context)是指每个程序维护的一个虚拟地址到物理地址的映射, 每个程序有单独上下文, 在程序切换时由操作系统选择合适的上下文加载到映射表; 此外, 提供了保护机制用于区分内核态(kernel)和用户态(user), 比如中断处理只能由OS切换到内核态调用.
为了避免每个上下文有独立的映射表, 造成上下文切换代价高昂(TLB需要清空重新加载), 高效的上下文切换方案调整为TLB中包含上下文信息(上下文id)
实际地址空间被分为多个区域进行管理, 调度方式有分页式、段式、段页式. 如下图所示, 被划分为独立区间, 如代码码, 保留的中断处理, 堆空间和栈空间等.
页映射也可以采用层次结构, 使用多级缓存可以减小页映射表的大小. 如图所示, 采用2级映射时, 一级页和二级页只有1024PTE, 考虑到程序局部性原理, 可以只需要缓存部分项, 尤其是在多进程, 每个进程有自己的上下文时, 可以极大节省TLB占用.
TODO
【1】Computer Architecture: A Quantitative Approach (6th)
【2】NAND Flash电路单元SLC&MLC&TLC的区别
【3】Architecture of an SSD and Benchmarking
【4】Computer Organization And Design (5th)
【5】计算机结构: MIT课程
【6】Virtual memory - Wikipedia