実験

実験

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のメモリを割り当てて初期化します。その後、3つの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ページが割り当てられます。

関連情報