linux hugepage

Hugepage是什么?

传统的内存页面大小是4KB,从linux 2.6 kernel引入了Hugepage的feature,即内存页的大小可以从2M到1G,不同的CPU硬件对Hugepage的大小支持不一样,比如ia64 architecture supports multiple page sizes 4K, 8K, 64K, 256K, 1M, 4M, 16M, 256M and ppc64 supports 4K and 16M。

为什么要用Hugepage?

—- 摘录的一段:
大多数操作系统采用了分段或分页的方式进行管理。分段是粗粒度的管理方式,而分页则是细粒度管理方式,分页方式可以避免内存空间的浪费。相应地,也就存在内存的物理地址与虚拟地址的概念。通过前面这两种方式,CPU必须把虚拟地址转换程物理内存地址才能真正访问内存。为了提高这个转换效率,CPU会缓存最近的虚拟内存地址和物理内存地址的映射关系,并保存在一个由CPU维护的映射表中。为了尽量提高内存的访问速度,需要在映射表中保存尽量多的映射关系。Linux的内存管理采取的是分页存取机制,为了保证物理内存能得到充分的利用,内核会按照LRU算法在适当的时候将物理内存中不经常使用的内存页自动交换到虚拟内存中,而将经常使用的信息保留到物理内存。通常情况下,Linux默认情况下每页是4K,这就意味着如果物理内存很大,则映射表的条目将会非常多,会影响CPU的检索效率。因为内存大小是固定的,为了减少映射表的条目,可采取的办法只有增加页的尺寸。因此Hugepage便因此而来。也就是打破传统的小页面的内存管理方式,使用大页面2M,4M等。如此一来映射条目则明显减少。TLB 缓存命中率将大大提高。
Hugepage的主要优势是:

  • 不可交换:Hugepage不可交换,因此没有内存页面换入/换出的开销,Hugepage常驻在物理内存(RAM)中。
  • 减轻TLB的压力:
    • TLB的记录条目将会大大减少;
    • 当使用hugepage时,TLB中每条记录将覆盖更大的地址空间,对于共享内存(SGA)中的全部或者大部分的内存映射,TLB未能命中的情况将会大大减少;
    • TLB的槽位一般只有512个,共享内存需要更少的TLB条目,意味着TLB中可以有更多的条目来保存其它地址空间
  • 减少页表的开销:每个TLB条目需要约64字节,如果是4G的RAM,则需要64M的内存保存TLB,如果配置成hugepage为256M,则仅仅需要1K字节保存整个TLB
  • 减少页表查询的开销:TLB条目变少,则查找内存页面的效率更快,而如果传统的4KB页面,出现了TLB miss,则可能需要额外三次内存读取操作才能将线性地址翻译为物理地址。
  • 提升内存访问的整体性能:使用虚拟内存,每一次对内存的访问实际上都是由两次抽象的内存操作组成。如果只要使用更少的页面,那么原本在页表访问的瓶颈也得以避免

在Linux中, kswapd是负责内核页面交换管理的一个守护进程,它的职责是保证Linux内存管理操作的高效。当物理内存不够时,它就会变得非常aggressive,有些情况下能占用单核CPU的100%. kswapd 进程负责确保内存空间总是在被释放中,它监控内核中的pages_high和pages_low阀值。如果空闲内存的数值低于pages_low,则每次 kswapd 进程启动扫描并尝试释放32个free pages.并一直重复这个过程,直到空闲内存的数值高于 pages_high。kswapd 进程完成以下几个操作:

  • 如果该页处于未修改状态,则将该页放置回空闲列表中.
  • 如果该页处于已修改状态并可备份回文件系统,则将页内容写入到磁盘.
  • 如果该页处于已修改状态但没有任何磁盘备份,则将页内容写入到swap device.

如何使用Hugepage?

内核参数:

1
2
3
4
5
6
7
8
9
10
lizhiyong@ubuntu:~$ grep Huge /proc/meminfo
AnonHugePages: 124928 kB /* 匿名HugePages数量,与透明HugePages有关 */
HugePages_Total: 0 /* 分配的页面数目,和Hugepagesize相乘后得到所分配的内存大小 */
HugePages_Free: 0 /* 从来没有被使用过的Hugepages数目,共享内存中尚未分配的Hugepages数量 */
HugePages_Rsvd: 0 /* 已经被分配预留但是还没有使用的page数目 */
HugePages_Surp: 0 /* “surplus”的缩写形式,表示池中大于/proc/sys/vm/nr_hugepages 中值的 HugePages 数量。剩余 HugePages 的最大数量由 /proc/sys/vm/nr_overcommit_hugepages 控制。此值为0的情况很常见 */
Hugepagesize: 2048 kB /* 页面大小 */
lizhiyong@ubuntu:~$
lizhiyong@ubuntu:~$ cat /proc/sys/vm/nr_hugepages
0

如何配置:

1
2
3
4
5
6
7
8
9
10
11
root@ubuntu:~# echo "vm.nr_hugepages=512" >> /etc/sysctl.conf
root@ubuntu:~# sysctl -p
net.ipv4.ip_forward = 1
vm.nr_hugepages = 512
root@ubuntu:~# grep Huge /proc/meminfo
AnonHugePages: 96256 kB
HugePages_Total: 512
HugePages_Free: 512
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB

PS: 过去使用大页面内存主要透过hugetlbfs需要mount文件系统到某个点去,部署起来很不方便,我们只想要点匿名页面,要搞的那么麻烦吗?
新的2.6.32内核通过支持MAP_HUGETLB方式来使用内存,避免了烦琐的mount操作,对用户更友好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
MAP_HUGETLB (since Linux 2.6.32)
Allocate the mapping using "huge pages." See the Linux kernel source file Docu‐
mentation/vm/hugetlbpage.txt for further information, as well as NOTES, below.

MAP_HUGE_2MB, MAP_HUGE_1GB (since Linux 3.8)
Used in conjunction with MAP_HUGETLB to select alternative hugetlb page sizes
(respectively, 2 MB and 1 GB) on systems that support multiple hugetlb page
sizes.

More generally, the desired huge page size can be configured by encoding the
base-2 logarithm of the desired page size in the six bits at the offset
MAP_HUGE_SHIFT. (A value of zero in this bit field provides the default huge
page size; the default huge page size can be discovered vie the Hugepagesize
field exposed by /proc/meminfo.) Thus, the above two constants are defined as:

#define MAP_HUGE_2MB (21 << MAP_HUGE_SHIFT)
#define MAP_HUGE_1GB (30 << MAP_HUGE_SHIFT)

The range of huge page sizes that are supported by the system can be discovered
by listing the subdirectories in /sys/kernel/mm/hugepages.

这样明显会方便些.

写代码测试下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <sys/mman.h>
#include <stdio.h>
#include <memory.h>

int main(int argc, char *argv[]) {
char *m;
size_t s = (8UL * 1024 * 1024);

m = mmap(NULL, s, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | 0x40000 /*MAP_HUGETLB*/, -1, 0);
if (m == MAP_FAILED) {
perror("map mem");
m = NULL;
return 1;
}

memset(m, 0, s);

printf("map_hugetlb ok, press ENTER to quit!\n");
getchar();

munmap(m, s);
return 0;
}

1
2
3
4
5
6
7
8
lizhiyong@ubuntu:~$ cat /proc/meminfo | grep Huge
AnonHugePages: 124928 kB
HugePages_Total: 512
HugePages_Free: 508
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
lizhiyong@ubuntu:~$

从上面查看使用了4个hugepage页面。

再次PS:
DPDK是使用mount挂载的方式配置hugepage的,详情参考eal_hugepage_info_init函数的实现。

参考文档:

https://kerneltalks.com/services/what-is-huge-pages-in-linux
https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt