实验

实验

我正在尝试使用 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 页面。

相关内容