前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >TLB缓存是个神马鬼,如何查看TLB miss?

TLB缓存是个神马鬼,如何查看TLB miss?

作者头像
开发内功修炼
发布2022-03-24 17:38:43
发布2022-03-24 17:38:43
1.1K00
代码可运行
举报
文章被收录于专栏:开发内功修炼开发内功修炼
运行总次数:0
代码可运行

介绍TLB之前,我们先来回顾一个操作系统里的基本概念,虚拟内存。

1

虚拟内存概念

在用户的视角里,每个进程都有自己独立的地址空间,A进程的4GB和B进程4GB是完全独立不相关的,他们看到的都是操作系统虚拟出来的地址空间。但是呢,虚拟地址最终还是要落在实际内存的物理地址上进行操作的。操作系统就会通过页表的机制来实现进程的虚拟地址到物理地址的翻译工作。其中每一页的大小都是固定的。这一段我不想介绍的太过于详细,对这个概念不熟悉的同学回去翻一下操作系统的教材。

页表管理有两个关键点,分别是页面大小和页表级数

1.页面大小 在Linux下,我们通过如下命令可以查看到当前操作系统的页大小

代码语言:javascript
代码运行次数:0
运行
复制
# getconf PAGE_SIZE
4096

可以看到当前我的Linux机器的页表是4KB的大小。

2.页表级数

1)如果页表级数越少,虚拟地址到物理地址的映射会很快,但是需要管理的页表项会很多,能支持的地址空间也有限。

2)相反页表级数越多,需要的存储的页表数据就会越少,而且能支持到比较大的地址空间,但是虚拟地址到物理地址的映射就会越慢。

2

Linux虚拟内存实现

1.32位系统的虚拟内存实现:二级页表

为了帮助大家回忆这段知识,我举个例子。如果想支持32位的操作系统下的4GB进程虚拟地址空间,假设页表大小为4K,则共有2的20次方页面。如果采用速度最快的1级页表,对应则需要2的20次方个页表项。一个页表项假如4字节,那么一个进程就需要(1048576*4=)4M的内存来存页表项。 如果是采用2级页表,如图1,则创建进程时只需要有一个页目录就可以了,占用(1024*4)=4KB的内存。剩下的二级页表项只有用到的时候才会再去申请。

则只需要页目录1024个,页表项1024个,总共2028个页表管理条目,(2048*4=)8k就可以支持起4GB的地址空间转换。

2.64位系统的虚拟内存实现:四级页表

现在的操作系统需要支持的可是48位地址空间(理论上可以支持64位,但其实现在只支持到了48位,也足够用了),而且要支持成百上千的进程,如果不采用分级页表的方式,则创建进程时就需要为其维护一个2的36次方个页表项(64位Linux目前只使用了地址中的48位的,在这里面,最后12位都是页内地址,只有前36位才是用来寻找页表的), 236 *4Byte=32GB,这个更不能忍了。也必须和32位系统一样,进一步提高页表的级数。

Linux在v2.6.11以后,最终采用的方案是4级页表,分别是:

  • PGD:page Global directory(47-39), 页全局目录
  • PUD:Page Upper Directory(38-30),页上级目录
  • PMD:page middle directory(29-21),页中间目录
  • PTE:page table entry(20-12),页表项

这样,一个64位的虚拟空间,初始创建的时候只需要维护一个29 大小的一个页全局目录就够了,现在的页表数据结构被扩展到了8byte。这个页全局目录仅仅需要(29 *8=)4K,剩下的中间页目录、页表项只需要在使用的时候再分配就好了。Linux就是通过这种方式支持起(248 =)256T的进程地址空间的。

3

页表副作用引出TLB

上面终于费劲扒了半天Linux虚拟内存的实现,我终于可以开始说我想说的重点了。虽然只需要4k的初始页目录就可以支持起一个256T的进程地址空间。但是,这也带来了额外的问题,页表是存在内存里的。那就是一次内存IO光是虚拟地址到物理地址的转换就要去内存查4次页表,再算上真正的内存访问,竟然需要5次内存IO才能获取一个内存数据!

和CPU的L1、L2、L3的缓存思想一致,既然进行地址转换需要的内存IO次数多,且耗时。那么干脆就在CPU里把页表尽可能地cache起来不就行了么,所以就有了TLB(Translation Lookaside Buffer),专门用于改进虚拟地址到物理地址转换速度的缓存。其访问速度非常快,和寄存器相当,比L1访问还快。

我本来想实际看一下TLB的信息,但翻遍了Linux的各种命令,也没有找到像sysfs这么方便查看L1、L2、L3大小的方法。仅仅提供下图供大家参考吧!(谁要是找到了查看TLB的命令,别忘了分享给飞哥啊,谢谢!)

有了TLB之后,CPU访问某个虚拟内存地址的过程如下

  • 1.CPU产生一个虚拟地址
  • 2.MMU从TLB中获取页表,翻译成物理地址
  • 3.MMU把物理地址发送给L1/L2/L3/内存
  • 4.L1/L2/L3/内存将地址对应数据返回给CPU

由于第2步是类似于寄存器的访问速度,所以如果TLB能命中,则虚拟地址到物理地址的时间开销几乎可以忽略。如果想了解TLB更详细的工作机制,请参考《深入理解计算机系统-第9章虚拟内存》

4

实际查看TLB缓存命中

既然TLB缓存命中很重要,那么有什么工具能够查看你的系统里的命中率呢?还真有

代码语言:javascript
代码运行次数:0
运行
复制
# perf stat -e dTLB-loads,dTLB-load-misses,iTLB-loads,iTLB-load-misses -p $PID
 Performance counter stats for process id '21047':

           627,809 dTLB-loads
             8,566 dTLB-load-misses          #    1.36% of all dTLB cache hits
         2,001,294 iTLB-loads
             3,826 iTLB-load-misses          #    0.19% of all iTLB cache hits

5

扩展一下

因为TLB并不是很大,只有4k,而且现在逻辑核又造成会有两个进程来共享。所以可能会有cache miss的情况出现。而且一旦TLB miss造成的后果可比物理地址cache miss后果要严重一些,最多可能需要进行5次内存IO才行。建议你先用上面的perf工具查看一下你的程序的TLB的miss情况,如果确实不命中率很高,那么Linux允许你使用大内存页,很多大牛包括PHP7作者鸟哥也这样建议。这样将会大大减少页表项的数量,所以自然也会降低TLB cache miss率。所要承担的代价就是会造成一定程度的内存浪费。在Linux里,大内存页默认是不开启的。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-11-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发内功修炼 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 2.64位系统的虚拟内存实现:四级页表
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档