巨页¶
OpenStack 中的巨页功能为高度内存 IO 密集型应用程序提供了重要的性能改进。
注意
巨页也可能被称为 hugepages 或 large pages,具体取决于来源。这些术语是同义词。
页、TLB 和巨页¶
- 页
物理内存被分割成一系列连续的区域,称为页。每个页包含一定数量的字节,称为页大小。系统通过访问整个页而不是逐字节访问来检索内存。
- 转换后备查找缓冲区 (TLB)
TLB 用于将页的虚拟地址映射到实际内存中的物理地址。TLB 是一个缓存,并非无限大,仅存储最近或最常访问的页。在正常操作期间,进程有时会尝试检索未存储在缓存中的页。这被称为 TLB 未命中,会导致延迟,因为处理器会遍历页本身以查找缺失的地址映射。
- 巨页
x86 系统中的标准页大小为 4 kB。这对于通用计算来说是最佳的,但更大的页大小 - 2 MB 和 1 GB - 也可用。这些更大的页大小被称为巨页。巨页会导致内存使用效率降低,因为进程通常不会使用每个页中所有可用的内存。但是,使用巨页会减少整体页数并降低 TLB 未命中的风险。对于具有重要内存需求或内存密集型需求的进程,巨页的好处通常超过缺点。
- 持久巨页
在 Linux 主机上,持久巨页是在启动时预留的巨页。HugeTLB 提供了这种巨页的预配置机制。HugeTLB 允许分配不同大小的不同数量的巨页。分配可以在启动时或运行时进行。有关更多信息,请参阅 Linux hugetlbfs 指南。
- 透明巨页 (THP)
在 Linux 主机上,透明巨页是根据进程请求自动配置的巨页。透明巨页是尽力配置的,尝试配置 2 MB 巨页(如果可用),但如果不可用则回退到 4 kB 小页。但是,无需进行预配置。有关更多信息,请参阅 Linux THP 指南。
在主机上启用巨页¶
重要提示
配置为文件支持内存的主机上,不能使用巨页。有关详细信息,请参阅 文件支持内存
由于其保证的可用性,需要持久巨页。但是,在大多数环境中,持久巨页默认情况下未启用。启用巨页的步骤因平台而异,此处仅描述 Linux 主机的步骤。在 Linux 主机上,可以通过检查 /proc/meminfo 来查询主机上的持久巨页数量
$ grep Huge /proc/meminfo
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
在此实例中,有 0 个持久巨页 (HugePages_Total) 和 0 个透明巨页 (AnonHugePages) 已分配。巨页可以在启动时或运行时分配。巨页需要连续的内存区域 - 随着主机运行时间延长,内存变得越来越碎片化。识别连续的内存区域对于所有巨页大小来说都是一个问题,但对于较大的巨页大小(如 1 GB 巨页)来说尤其成问题。在启动时分配巨页将确保始终可用正确的巨页数量,而在运行时分配它们可能会失败,如果内存已变得过于碎片化。
要在运行时分配巨页,必须将内核启动参数扩展为包含一些巨页特定参数。可以通过修改 /etc/default/grub 并将 hugepagesz、hugepages 和 transparent_hugepages=never 参数附加到 GRUB_CMDLINE_LINUX 来实现。例如,要在启动时分配 2048 个持久 2 MB 巨页,请运行
# echo 'GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX hugepagesz=2M hugepages=2048 transparent_hugepage=never"' >> /etc/default/grub
$ grep GRUB_CMDLINE_LINUX /etc/default/grub
GRUB_CMDLINE_LINUX="..."
GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX hugepagesz=2M hugepages=2048 transparent_hugepage=never"
重要提示
持久巨页不能被标准主机操作系统进程使用。确保为这些进程保留足够的空闲、非巨页内存。
重新启动主机,然后验证是否现在可用巨页
$ grep "Huge" /proc/meminfo
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
HugePages_Total: 2048
HugePages_Free: 2048
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
现在有 2048 个 2 MB 巨页,总计 4 GB 巨页。必须挂载这些巨页。在大多数平台上,这会自动发生。要验证是否已挂载巨页,请运行
# mount | grep huge
hugetlbfs on /dev/hugepages type hugetlbfs (rw)
在此实例中,巨页挂载在 /dev/hugepages。此挂载点因平台而异。如果上述命令未返回任何内容,则必须手动挂载巨页。要将巨页挂载在 /dev/hugepages,请运行
# mkdir -p /dev/hugepages
# mount -t hugetlbfs hugetlbfs /dev/hugepages
还有许多其他方法可以配置巨页,包括在运行时分配巨页、为不同的巨页大小指定不同的分配或从与不同的 NUMA 节点关联的内存分配巨页。有关在 Linux 主机上配置巨页的更多信息,请参阅 Linux hugetlbfs 指南。
自定义实例巨页分配¶
重要提示
以下描述的功能当前仅受 libvirt/KVM 驱动程序支持。
重要提示
出于性能原因,为实例配置巨页会隐式地为实例配置 NUMA 拓扑。为实例配置 NUMA 拓扑需要启用 NUMATopologyFilter。有关更多信息,请参阅 CPU 拓扑。
默认情况下,实例不使用巨页作为其基础内存。但是,巨页可以为某些工作负载带来重要或必需的性能改进。必须通过使用 flavor extra specs 或 image metadata 显式请求巨页。要请求实例使用巨页,可以使用 hw:mem_page_size flavor extra spec
$ openstack flavor set m1.large --property hw:mem_page_size=large
不同的平台提供不同的巨页大小。例如:基于 x86 的平台提供 2 MB 和 1 GB 巨页大小。也可以请求特定的巨页大小,可以带或不带单位后缀。单位后缀必须是以下之一:Kb(it)、Kib(it)、Mb(it)、Mib(it)、Gb(it)、Gib(it)、Tb(it)、Tib(it)、KB、KiB、MB、MiB、GB、GiB、TB、TiB。如果未提供单位后缀,则假定为千字节。要请求实例使用 2 MB 巨页,请运行以下命令之一:
$ openstack flavor set m1.large --property hw:mem_page_size=2MB
$ openstack flavor set m1.large --property hw:mem_page_size=2048
为实例启用巨页可能会对其他实例产生负面影响,因为会消耗有限的巨页资源。要显式请求实例使用小页,请运行
$ openstack flavor set m1.large --property hw:mem_page_size=small
注意
显式请求任何页大小仍将导致应用 NUMA 拓扑到实例,如本文档前面所述。
最后,要将巨页或小页的决定权交给计算驱动程序,请运行
$ openstack flavor set m1.large --property hw:mem_page_size=any
有关 hw:mem_page_size 的语法,请参阅 hw:mem_page_size。
应用程序通常打包为镜像。对于需要巨页提供的 IO 性能改进的应用程序,请配置镜像元数据以确保实例始终请求特定的页大小,而与 flavor 无关。要配置镜像使用 1 GB 巨页,请运行
$ openstack image set [IMAGE_ID] --property hw_mem_page_size=1GB
如果 flavor 指定了数值页大小或页大小为“small”,则镜像不允许指定页大小,如果指定了,将引发异常。如果 flavor 指定了页大小为 any 或 large,则镜像中指定的任何页大小都将被使用。通过在 flavor 中设置 small 页大小,管理员可以防止用户在 flavor 中请求巨页并影响资源利用率。要配置此页大小,请运行
$ openstack flavor set m1.large --property hw:mem_page_size=small
注意
显式请求任何页大小仍将导致应用 NUMA 拓扑到实例,如本文档前面所述。
有关镜像元数据,请参阅 镜像元数据 指南。