我正在尝试使用 libhugetlbfs 备份具有 1GiB 大页的多线程应用程序的内存分配。但是,只有主线程分配被分配了大页。如果我将 Glibc malloc arenas 的最大数量限制为 1,则所有线程的所有分配都将使用大页进行备份。由于引入了并发访问单个竞技场的争用,这并不理想。
有没有办法通过 libhugetlbfs 透明地强制所有线程使用大页面?
笔记:我知道透明大页面 (THP)。但是,小于 1GiB 的分配不会自动分配大页面。较小的页面只有在 khugepaged 内核线程处理它们时才会压缩成较大的页面,这是我不希望依赖的。理想情况下,我希望所有 malloc 调用都使用大页面进行服务,即使分配很小。这对于执行大量小分配的应用程序很有用。
实验
以下是我设置 1GiB 大页面所遵循的步骤:
sudo mkdir /dev/hugepages1G
sudo mount -t hugetlbfs -o uid=<my_user_id>,pagesize=1g,min_size=50g none /dev/hugepages1G
sudo hugeadm --pool-pages-min 1G:50
我正在使用下面的虚拟应用程序进行测试。主线程分配并初始化1GiB内存。然后,它创建三个 pthread,每个线程分配并初始化 10GiB 内存。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
void *iamathread(void *data)
{
char *addr;
char dummy;
size_t size, i;
size = 10*1024*1024*1024UL;
pid_t x = syscall(__NR_gettid);
addr = malloc(size);
if (!addr) {
perror("cannot allocate memory");
pthread_exit(NULL);
}
memset(addr, 1, size);
printf("%d:\t sleeping\n", x);
sleep(1000000U);
return NULL;
}
int main(int argc, char *agv[])
{
char *addr;
char dummy;
size_t size, i;
int npt;
npt = 3;
size = 1*1024*1024*1024UL;
pthread_t pt[npt];
for (i = 0; i < npt; i++) {
if (pthread_create(&pt[i], NULL, iamathread, NULL)) {
fprintf(stderr, "Error creating thread\n");
return 1;
}
}
pid_t x = syscall(__NR_gettid);
printf("%d:\t I'm main\n", x);
addr = malloc(size);
if (!addr) {
perror("cannot allocate memory");
return 1;
}
memset(addr, 1, size);
printf("Press any key to exit and release memory\n");
scanf("%c", &dummy);
return 0;
}
我创建了以下脚本来计算应用程序使用的每个页面大小的页面数:
#!/usr/bin/bash
PID=$1
awk '
BEGIN {
tmp_size = -1
}
$1 == "Size:" {
tmp_size = $2
next
}
$1 == "KernelPageSize:" {
page_size = $2
vmas[page_size]["count"] += 1
vmas[page_size]["pages"] += tmp_size/page_size
tmp_size = -1
next
}
END {
for (key in vmas) {
print(key " KiB VMAs: " vmas[key]["count"])
}
for (key in vmas) {
print(key " KiB num pages: " vmas[key]["pages"])
}
}
' /proc/$PID/smaps
这些是在使用和不使用 MALLOC_ARENA_MAX 环境变量来限制 arenas 数量的情况下运行时获得的结果:
$ LD_PRELOAD=/usr/lib64/libhugetlbfs.so HUGETLB_MORECORE=1G HUGETLB_PATH=/dev/hugepages1G ./main &
$ hugepagecount.sh `pgrep main`
4 KiB VMAs: 41
1048576 KiB VMAs: 2
4 KiB num pages: 7922277
1048576 KiB num pages: 2
$ MALLOC_ARENA_MAX=1 LD_PRELOAD=/usr/lib64/libhugetlbfs.so HUGETLB_MORECORE=1G HUGETLB_PATH=/dev/hugepages1G ./main &
$ hugepagecount.sh `pgrep main`
4 KiB VMAs: 37
1048576 KiB VMAs: 5
4 KiB num pages: 8802
1048576 KiB num pages: 32
当不限制 arena 数量时,仅分配 2 个 1GiB (1048576 KiB) 页。相反,当强制使用单个 arena 时,会分配 32 个 1GiB 页面。