libhugetlbfs를 사용하여 1GiB hugepages가 포함된 다중 스레드 애플리케이션에 대한 메모리 할당을 백업하려고 합니다. 그러나 메인 스레드 할당에만 거대한 페이지가 할당됩니다. Glibc malloc 경기장의 최대 수를 1로 제한하면 모든 스레드에 대한 모든 할당이 거대한 페이지로 백업됩니다. 단일 경기장에 대한 동시 액세스에 대한 경합이 발생하므로 이는 이상적이지 않습니다.
libhugetlbfs를 통해 모든 스레드가 hugepages를 사용하도록 투명하게 강제하는 방법이 있습니까?
노트: THP(Transparent Huge Pages)에 대해 알고 있습니다. 그러나 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 환경 변수를 사용하거나 사용하지 않고 실행할 때 얻은 결과입니다.
$ 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
아레나 수에 제한이 없으면 2개의 1GiB(1048576KiB) 페이지만 할당됩니다. 반면 단일 아레나를 강제 적용하는 경우 32개의 1GiB 페이지가 할당됩니다.