大页面的优点和缺点

大页面的优点和缺点

为课程学生准备的文章翻译 《Linux 管理员》.

之前,我讨论了如何在 Linux 上测试和启用 Hugepages。
仅当您确实有地方可以使用 Hugepages 时,本文才有用。 我遇到过很多人,他们被大页将神奇地提高生产力的前景所愚弄。 然而,大分页是一个复杂的主题,如果使用不当可能会降低性能。

第 1 部分:验证 Linux 上是否启用了大页(原始 这里)

问题:
您需要检查系统上是否启用了 HugePages。

解决方案:
这很简单:

cat /sys/kernel/mm/transparent_hugepage/enabled

你会得到这样的东西:

always [madvise] never

您将看到可用选项的列表(总是,疯狂,从不),当前活动选项将括在括号中(默认情况下 马德维斯).

马德维斯 意思是 transparent hugepages 仅对显式请求大页的内存区域启用 马德维斯(2).

时刻 意思是 transparent hugepages 始终对所有进程启用。 这通常会提高性能,但如果您的用例中有许多进程消耗少量内存,则总体内存负载可能会急剧增加。

决不要 意思是 transparent hugepages 即使使用 madvise 请求,也不会包含在内。 如需了解更多信息,请联系 文件资料 Linux 内核。

如何更改默认值

选项1: 直接改 sysfs (重启后参数将恢复默认值):

echo always >/sys/kernel/mm/transparent_hugepage/enabled
echo madvise >/sys/kernel/mm/transparent_hugepage/enabled
echo never >/sys/kernel/mm/transparent_hugepage/enabled

选项2:通过使用修改后的配置重新编译内核来更改系统默认值(仅在使用自定义内核时建议使用此选项):

  • 要设置始终默认,请使用:
    CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
  • 要将 madvise 设置为默认值,请使用:
    CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
    # Comment out CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y

第 2 部分:HugePages 的优点和缺点

我们将尝试有选择地解释使用 Hugepages 的优点、缺点和可能的陷阱。 由于技术上复杂且迂腐的文章对于那些被欺骗认为 Hugepages 是万能药的人来说可能很难理解,因此我会为了简单性而牺牲准确性。 值得记住的是,许多主题确实很复杂,因此被大大简化了。

请注意,我们正在谈论运行 Linux 的 64 位 x86 系统,并且我只是假设该系统支持透明大页(因为大页不被覆盖并不是一个缺点),就像几乎所有现代 Linux 中的情况一样环境。

我将在下面的链接中附上更多技术说明。

虚拟内存

如果您是一名 C++ 程序员,您就会知道内存中的对象具有特定的地址(指针值)。

然而,这些地址不一定反映内存中的物理地址(RAM 地址)。 它们代表虚拟内存中的地址。 处理器有一个特殊的MMU(内存管理单元)模块,可以帮助内核将虚拟内存映射到物理位置。

这种方法有很多优点,但最重要的是:

  • 性能(出于各种原因);
  • 程序隔离,即任何程序都不能读取另一个程序的内存。

什么是页面?

虚拟内存分为页。 每个单独的页面都指向一个特定的物理内存,它可以指向 RAM 中的一个区域,也可以指向分配给物理设备(例如视频卡)的地址。

您处理的大多数页面要么指向 RAM,要么被交换,这意味着它们存储在您的硬盘驱动器或 SSD 上。 内核管理每个页面的物理布局。 如果访问了欺骗页面,内核会停止尝试访问内存的线程,将该页面从硬盘驱动器/SSD 读取到 RAM,然后继续执行该线程。

此过程是流透明的,这意味着它不一定直接从 HDD/SSD 读取。 普通页的大小为 4096 字节。 大页大小为 2 兆字节。

翻译关联缓冲区 (TLB)

当程序访问内存页时,CPU必须知道从哪个物理页读取数据(即有虚拟地址映射)。

内核有一个数据结构(页表),其中包含有关正在使用的页面的所有信息。 使用此数据结构,您可以将虚拟地址映射到物理地址。

然而,页表相当复杂且缓慢,因此我们根本无法在进程每次访问内存时分析整个数据结构。

幸运的是,我们的处理器有一个 TLB,可以缓存虚拟地址和物理地址之间的映射。 这意味着,虽然我们需要在第一次访问尝试时解析页表,但所有后续对该页的访问都可以在 TLB 中处理,从而实现快速操作。

因为它是作为物理设备实现的(这首先使其速度很快),所以它的容量是有限的。 因此,如果您想访问更多页面,TLB 将无法存储所有页面的映射,从而导致程序运行速度变慢。

大页来拯救

那么我们该如何避免TLB溢出呢? (我们假设程序仍然需要相同数量的内存)。

这就是大页发挥作用的地方。 现在,一个 TLB 条目可以指向高达 4096 兆字节,而不是仅需要一个 TLB 条目的 2 字节。 假设 TLB 有 512 个条目,这里没有大页我们可以匹配:

4096 b⋅512=2 MB

那么我们如何与他们进行比较:

2 MB⋅512=1 GB

这就是 Hugepages 如此出色的原因。 他们可以毫不费力地提高生产力。 但这里有一些重要的警告。

大页欺骗

内核自动监视每个内存页的使用频率。 如果没有足够的物理内存 (RAM),内核会将不太重要(不经常使用)的页面移动到硬盘,以便为更重要的页面释放一些 RAM。
原则上,这同样适用于大页。 但是,内核只能交换整个页面,而不能交换单个字节。

假设我们有一个这样的程序:

char* mymemory = malloc(2*1024*1024); // Возьмем это за одну Hugepage!
// Заполним mymemory какими-либо данными
// Сделаем много других вещей,
// которые приведут к подмене страницы mymemory
// ...
// Запросим доступ только к первому байту
putchar(mymemory[0]); 

在这种情况下,内核将需要从硬盘驱动器/SSD 中替换(读取)多达 2 MB 的信息,只是为了让您读取一个字节。 对于常规页面,只需要从硬盘/SSD 读取 4096 字节。

因此,如果覆盖hugepage,只有在需要访问整个页面时读取速度才会更快。 这意味着,如果您尝试随机访问内存的不同部分并且仅读取几千字节,则应该使用常规页面,而不必担心其他任何事情。

另一方面,如果您需要顺序访问大部分内存,大页面将提高您的性能。 但是,您需要自己测试它(而不是使用抽象软件)并查看哪种效果更快。

内存分配

如果您编写 C,您知道可以使用以下命令从堆中请求任意小(或几乎任意大)的内存量 malloc()。 假设您需要 30 字节内存:

char* mymemory = malloc(30);

对于程序员来说,您可能会从操作系统“请求”30 字节内存并返回指向某个虚拟内存的指针。 但实际上 malloc () 只是一个从函数内部调用的 C 函数 brk和sbrk 从操作系统请求或释放内存。

然而,为每次分配请求越来越多的内存是低效的; 很可能某些内存段已经被释放 (free()),我们可以重用它。 malloc() 实现了相当复杂的算法来重用释放的内存。

与此同时,一切都在你不经意间发生,所以你为什么要担心呢? 但因为挑战 free() 并不意味着 内存必须立即返回给操作系统.

存在内存碎片这样的事情。 在极端情况下,存在仅使用几个字节的堆段,而其间的所有内容都已被释放 (free()).

请注意,内存碎片是一个极其复杂的主题,即使对程序进行微小的更改也会产生重大影响。 大多数情况下,程序不会造成明显的内存碎片,但你应该意识到,如果堆的某些区域存在碎片问题,hugepages 会让情况变得更糟。

选择性使用大页

阅读本文后,您已经确定程序的哪些部分可以从使用大页中受益,哪些部分不能。 那么是否应该启用大页呢?

幸运的是你可以使用 madvise()仅对那些有用的内存区域启用大分页。

首先,使用以下命令检查大页面是否在 madvise() 模式下运行 指示 在文章的开头。

然后,使用 madvise()告诉内核到底在哪里使用大页面。

#include <sys/mman.h>
// Аллоцируйте большое количество памяти, которую будете использовать
size_t size = 256*1024*1024;
char* mymemory = malloc(size);
// Просто включите hugepages…
madvise(mymemory, size, MADV_HUGEPAGE);
// … и задайте следующее
madvise(mymemory, size, MADV_HUGEPAGE | MADV_SEQUENTIAL)

请注意,此方法只是向内核提供有关如何管理内存的建议。 这并不意味着内核将自动对给定内存使用大页。

参考文档 (联机帮助页)madvise了解有关内存管理的更多信息 madvise(),这个主题的学习曲线非常陡峭。 因此,如果您打算真正擅长于此,请准备好几周的阅读和测试,然后才能期待任何积极的结果。

读什么?

有一个问题? 写在评论里吧!

来源: habr.com

添加评论