6. 存储层次

高速缓存

计算机的运作流程:

  1. 根据指令,将数据从内存读取到寄存器
  2. 基于寄存器上的数据进行运算
  3. 把运算结果写入内存

与在寄存器上执行运算所耗费的平均时间相比,访问内存会消耗更多的时间,产生更长的延迟。
对于计算机系统来说,无论流程2的处理速度有多快,流程1和流程3都会成为性能瓶颈。

高速缓存的存在,正是为了抹平寄存器与内存之间的性能差距。

从高速缓存到寄存器的访问速度比从内存到寄存器的访问速度快了几倍甚至几十倍,利用这一点,即可提高流程1和流程3的处理速度。高速缓存通常内置于CPU内,但页存在位于CPU外的类型。

在从内存往寄存器读取数据时,数据先被送往高速缓存,再被送往寄存器。所读取的数据的大小取决于缓存块大小的值,该值由各个CPU规定。

此后,当CPU需要再次读取地址300上的数据时,比如需要再次把同样的数据读取到R1时,将不用从内存读取数据,只需读取已经存在于高速缓存上的数据即可。

当需要将寄存器上的数据重新写回到地址300上时,首先会把改写后的数据写入高速缓存。此时依然以缓存块大小为单位写入数据。然后,为这些缓存块添加一个标记,以表明这部分从内存读取的数据被改写了。通常我们会称这些被标记的缓存块"脏了"。

这些被标记的数据会在写入高速缓存后的某个指定时间点,通过后台处理写入内存。随之,这些缓存块就不再脏了。

在这样的状态下,当CPU仅访问位于高速缓存上的数据时,访问速度比没有高速缓存时快的多,因为数据访问速度达到了高速缓存的读写速度。

高速缓存不足时

在高速缓存不足时,如果要读写高速缓存中尚不存在的数据,就要销毁一个现有的缓存块,再把新的数据复制到缓存块上。

当需要销毁的缓存块脏了的时候,数据将在被销毁前被同步到内存中。如果在高速缓存不足,且所有缓存块都脏了的时候向内存发起访问,那么将因高速缓存频繁执行读写处理而发生系统抖动,与此同时性能页会大幅降低。

多级缓存

高速缓存都采用分层结构,各层级在容量,延迟以及"由哪些逻辑CPU共享"等方面各不相同。

构成分层结构的各高速缓存分别名为L1,L2,L3。不同规格的CPU中的缓存层级数量页不同。在各高速缓存中,最靠近寄存器、容量最小且速度最快的L1缓存。层级的数字越大,离寄存器越远,速度越慢,但容量越大。

高速缓存的信息可以从/sys/devices/system/cpu/cpu0/cache/index0/目录下的文件中查看。

  • type:高速缓存中缓存的数据类型。Data代表仅缓存数据,Code代表仅缓存指令,Unified代表两者都能缓存
  • shared_cpu_list:共享该缓存的逻辑CPU列表
  • size:容量大小
  • coherency_line_size: 缓存块大小

访问局部性

大部分程序具有名为访问局部性的特征:

  • 时间局限性:在某一时间点被访问过的数据,有很大的可能性在不久的将来会再次被访问,例如循环处理中的代码段
  • 空间局部性:在某一时间点访问过某个数据后,有很大的可能性会继续访问其附近的其他数据,例如遍历数组元素

因此,从比较短的时间区间上来看,进程倾向于访问很小范围内的内存,这个范围要远小于进程所获取的内存总量。如果这个范围的大小在高速缓存的容量以内,就非常完美了。

转译后备缓冲区

进程需要通过下述步骤访问虚拟地址上的特定数据:

  1. 对照物理内存中的页表,把虚拟地址转换为物理地址
  2. 访问通过步骤1得到的物理地址

在CPU上存在一个具有与高速缓冲同样的访问速度的区域,名为转译后备缓冲区又称为快表或页表缓冲,该区域用于保存虚拟地址与物理地址的转换表。利用这个区域,即可提供步骤1的执行速度。

页面缓存

与CPU访问内存的速度比起来,访问外部存储器的速度慢了几个数量级。内核中用于填补这一速度差的机构称为页面缓存

页面缓存和高速缓存非常相似。高速缓存是把内存上的数据缓存到高速缓存上,页面缓存则是将外部存储器上的文件数据缓存到内存上。高速缓存以缓存块为单位处理数据,而页面缓存则以页为单位处理数据。

当进程读取文件的数据时,内核并不会直接把文件数据复制到进程的内存中,而是先把数据复制到位于内核的内存上的页面缓冲区域,然后再把这些数据复制到进程的内存中。

在内核自身的内存中有一个管理区域,该区域中保存着页面缓存所缓存的文件以及这些文件的范围信息等。

在这之后,如果再次读取已经位于页面缓存的数据,内核将直接返回页面缓存中的数据。

由于页面缓存是由全部进程共享的资源,所以发起读取的进程也可以不是最初访问该文件数据的进程。

在进程向文件写入数据后,内核会把数据写入页面缓存中。这时,管理区域中与这部分数据对应的条目会被添加一个标记,以表明"这些是脏数据,其内容比外部存储器中的数据新"。这些被标记的页面称为脏页.

之后,脏页中的数据将在指定时间通过内核的后台处理反映到外部存储器上,与此同时,脏标记也会被去除。

如果各个进程想要访问的文件数据都已存在于页面缓存中,那么系统的文件访问速度将能超越外部存储器的访问速度,接近内存的访问速度,因此可以期待系统整体运行速度的提升。

只要系统上还存在可用内存,则每当各个进程访问那些尚未读取到页面缓存中的文件时,页面缓存的大小就会随之增大。

当系统的内存不足时,内核将释放页面缓存以空出可用内存。此时,首先丢弃脏页以外的页面。如果还是无法空出足够内存,就对脏页执行回写,然后继续释放页面。当需要释放脏页时,由于需要访问外部存储器,所以恐怕会导致系统性能下降。尤其是当系统上存在大量文件写入操作而导致出现大量脏页时,系统负载往往会变得非常大。内存不足引发大量脏页得回写处理,进而导致系统性能下降得情况非常常见。

同步写入

在页面缓存中还存在脏页得状态下,如果系统出现了强制断电得情况,将导致页面缓存中得脏页丢失。
为了避免这种情况出现,利用open()系统调用打开文件时,将flag参数设定为O_SYNC。这样一来,之后每当对该文件执行write()系统调用,都会在往页面缓存写入数据时,将数据同步写入外部存储器。

缓冲区缓存

缓冲区缓存是与页面缓存相似得机制。这是当跳过文件系统,通过设备文件直接访问外部存储器时使用得区域。

页面缓存与缓冲区缓存可以概况为"用于将外部存储器中得数据放到内存上得机制"

调优参数

  • sysctl vm.dirty_writeback_centisecs: 脏页回写周期,单位为1/100秒
  • sysctl vm.dirty_background_ratio: 当系统内存不足时防止产生剧烈得回写负荷得参数。当脏页占用得内存量与系统搭载得内存总量得比值超过这个百分比时,后台就会开始运行回写处理。
  • sysctl vm.dirty_background_bytes: 以字节为单位而非百分比为单位来控制啥时候回写脏页
  • sysctl vm.dirty_ratio: 当脏页得内存占比超过该参数指定得百分比值时,将阻塞进程得写入,直到一定量得脏页完成回写处理。
  • sysctl vm.dirty_bytes:以字节为单位设置

echo 3 > /proc/sys/vm/drop_caches文件中写入3可以清理页面缓存

超线程

访问内存的延迟比CPU运算所消耗的时间长很多。另外,与CPU的运算速度相比,访问缓存的速度也稍微要慢一点。

通过超线程功能,可以有效利用这些浪费再等待上的CPU资源。

在使用超线程功能后,可以为CPU核心提供多份(一般为两份)硬件资源(其中包含一部分CPU核心使用的硬件资源,例如寄存器),然后将其划分为多个会被系统识别为逻辑CPU的超线程。

超线程带来的并非只有好处,它能产生多大效果很大程度上取决于运行在超线程上的进程行为。即使在最理想的情况下,吞吐量也不能翻倍,实际上,能提供20%到30%已经是非常好的状态了。在某些情况下,吞吐量甚至会下降。

posted @ 2024-07-12 11:05  Python习者  阅读(21)  评论(0)    收藏  举报