前言
一说到大页内存,很多人都比较懵逼,什么是大页内存?它有什么优缺点呢?主要是用来干什么的?下面我们来一一叙说一下。
什么是大页内存?
操作系统对于数据的存取直接从物理内存要比从磁盘读写数据要快的多,但是物理内存是有限的,这样就引出了物理内存与虚拟内存的概念。虚拟内存就是为了满足物理内存的不足而提出的策略,它是利用磁盘空间虚拟出的一块逻辑内存,这部分磁盘空间Windows下称之为虚拟内存,Linux下被称为交换空间(Swap Space)。
对于这个大内存的管理(物理内存+虚拟内存),大多数操作系统采用了分段或分页的方式进行管理。分段是粗粒度的管理方式,而分页则是细粒度管理方式,分页方式可以避免内存空间的浪费。相应地,也就存在内存的物理地址与虚拟地址的概念。通过前面这两种方式,CPU必须把虚拟地址转换程物理内存地址才能真正访问内存。为了提高这个转换效率,CPU会缓存最近的虚拟内存地址和物理内存地址的映射关系,并保存在一个由CPU维护的映射表中。为了尽量提高内存的访问速度,需要在映射表中保存尽量多的映射关系。
linux的内存管理采取的是分页存取机制,为了保证物理内存能得到充分的利用,内核会按照LRU算法在适当的时候将物理内存中不经常使用的内存页自动交换到虚拟内存中,而将经常使用的信息保留到物理内存。通常情况下,Linux默认情况下每页是4K,这就意味着如果物理内存很大,则映射表的条目将会非常多,会影响CPU的检索效率。因为内存大小是固定的,为了减少映射表的条目,可采取的办法只有增加页的尺寸。因此Hugepage便因此而来。也就是打破传统的小页面的内存管理方式,使用大页面2m,4m,16m等等。如此一来映射条目则明显减少。如果系统有大量的物理内存(大于8G),则物理32位的操作系统还是64位的,都应该使用Hugepage。
如何去设置
- 系统启用大页内存
下述操作,需要重启机器。
不建议 通过 设置
echo 1G > /proc/sys/vm/nr_hugepages的方式设置,因为系统不一定成功。
- 设置大页内存的PAGESIZE
- grubby --update-kernel=ALL --args="hugepagesz=1G”
Centos 下(其他发行版本自行Google)
- 设置大页内存的PAGE个数
grubby --update-kernel=ALL --args="hugepages=10”
上述1、2的顺序不能乱,乱了系统就没办法在启动阶段申请PAGE了。
或者直接在一个命令中完成:
grubby --update-kernel=ALL --args="hugepagesz=1G hugepages=10”
重置参数
sudo grubby --update-kernel=ALL --remove-args="hugepages hugepagesz default_hugepagesz"
使用大页内存
使用hugetlbfs
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
void access_mem(char *ptr, unsigned long memsize)
{
unsigned long i;
for (i = 0; i < memsize; i+=4096) {
ptr[i] = 1;
}
}
int main() {
unsigned long memsize = 1000000000;
unsigned long i;
int fd = open("/dev/hugepages/fileA", O_CREAT|O_RDWR, 0600);
char *my_string = mmap(0, memsize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
access_mem(ptr, memsize);
}
使用 perf 探测 缺页中断数,和TLB miss数,发现非常少。
sudo perf record -e dTLB-loads -e faults ./a.out
sudo perf report
得到的结果如下:
Available samples
1k dTLB-loads
7 faults
不使用hugepage的程序
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <malloc.h>
void access_mem(char *ptr, unsigned long memsize)
{
unsigned long i;
for (i = 0; i < memsize; i+=4096) {
ptr[i] = 1;
}
}
int main() {
unsigned long memsize = 1000000000;
char *my_string = malloc(memsize);
access_mem(my_string, memsize);
}
使用 perf 探测 缺页中断数,和TLB miss数,发现相对使用大页的程序而言,就多多了。
sudo perf record -e dTLB-loads -e faults ./a.out
sudo perf report
得到的结果如下:
Available samples
2K dTLB-loads
1K faults
直接使用mmap
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
void access_mem(char *ptr, unsigned long memsize)
{
unsigned long i;
for (i = 0; i < memsize; i+=4096) {
ptr[i] = 1;
}
}
int main() {
unsigned long memsize = 1000000000;
unsigned long i;
char *my_string = mmap(0, memsize, PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB/**/, -1, 0);
access_mem(my_string, memsize);
sleep(20);
munmap(my_string, memsize);
}
使用 perf 探测 缺页中断数,和TLB miss数,发现非常少。
sudo perf record -e dTLB-loads -e faults ./a.out
sudo perf report
得到的结果如下:
Available samples
970 dTLB-loads
7 faults
大页内存的优缺点:
有点很明显,大页内存TLB miss 很少,缺页中断也很少,极高的提高了性能,对于那些内存操作非常频繁的业务来说,可以有效的提高性能。
缺点就是,大页内存类似专用内存,会从系统中抠出一块大内存(pagesize*nr_pages),而想要使用这块内存,应用程序必须修改程序,使用上述几种方式来使用大页。 其次,如果程序内存使用较少,却申请了大页,就造成了内存浪费的。
Redhat有另一种便捷的方式,就是 透明大页,就是开启后,任何程序都默认使用2MB的大页,不需要修改程序源码,但是也带来了内存浪费。
网友评论