メモリー割り当てのパフォーマンスのチューニング

このトピックは、インテル® メニー・インテグレーテッド・コア (インテル® MIC) アーキテクチャーにのみ適用されます。

コプロセッサーのユーザー割り当てデータでは、malloc_mm_malloc の代わりに mmap() で大きな (2MB の) ページ割り当てを使用すると、アプリケーションのパフォーマンスが向上する場合があります。

malloc_mm_malloc のデフォルトのページサイズは 4KB で、サイズが 2MB の THP (トランスペアレント・ヒュージ・ページ) は現在サポートされていません。 このため、メモリーを割り当てる前に 2MB ページを手動で予約し、mmap() を使用してメモリーを割り当てる必要があります。

すべてのアプリケーションで大きなページサイズを使用するメリットがあるわけではありません。一般に、大きなページサイズがパフォーマンスに影響するかどうかはデータ・アクセス・パターンに大きく依存します。アプリケーションが異なるページに割り当てられた複数のデータ構造にアクセスする場合、コプロセッサーの 2MB ページに 8 つの TLB (トランスレーション・ルックアサイド・バッファー) エントリーしかないとパフォーマンスの低下を引き起こします。

2MB ページの数を制御する

コプロセッサーで使用できる 2MB ページの数を制御するには、コプロセッサーの /proc/sys/nr_hugepages に値を記述します。

例えば、コプロセッサーで次のコマンドを実行すると、Linux* uOS はサイズがそれぞれ 2MB の 5 つのヒュージページを構成します。

echo 5 >/proc/sys/vm/nr_hugepages

アプリケーションのすべての malloc で必要なページの合計値を指定することを推奨します。 例えば、アプリケーションにページ数 P1 と P2 が必要な 2 つの malloc が含まれる場合、P1+P2 の合計の値を記述します。 ファイルを解析して現在の値を取得し、必要な値を追加して、ファイルに新しい値を書き込むこともできます。すべての割り当てではじめから合計のページ数を要求すると、メモリーの断片化が改善され、要求した割り当てが行われる可能性が高くなります。

この変更を行うアトミックなインターフェイスはありません。この変更により、フリープールで利用可能な物理メモリーは少なくなります。このファイルの値が減ると、uOS はメモリーが実際に解放されるまで待ちます。

コンパイラーは、要求されたページサイズに対するユーザー制御を提供しません。2MB ページを割り当てるアプローチのサンプルを次に示します。

コプロセッサーの 2MB ページの数をモニターする

2MB ページを割り当てるには、HUGE_TLB フラグを使用して mmap() を呼び出します。 要求を満たせない場合は、MAP_FAILED が返されます。

/proc/meminfo を使用して、コプロセッサーで使用している 2MB ページの数をモニターすることができます

コプロセッサーで次のコマンドを使用します。

cat /proc/meminfo

出力には次の値が含まれます。

...
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
...

複数の 2MB ページの割り当て例

下記の関数 allocate_huge_pages() は、Linux* uOS で複数の 2MB ページを割り当てる方法の例です。

/* ヒュージページのサポートを使用してメモリーを割り当て */

#define MALLOC_2M(size) \
  mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | \
       MAP_PRIVATE | MAP_HUGETLB, -1, 0);

size_t _get_huge_pages()
{
    FILE * f;
    size_t sz = 0;
    char rvalue[128] = {0};
    f = popen("cat /proc/sys/vm/nr_hugepages","r");
    if(f==NULL)
        return -1;
    sz = fread(rvalue, 1, sizeof(rvalue)-1, f);
    if(sz==0) {
        pclose(f);
        return -1;
    }
    pclose(f);
    if (rvalue[0])
        return atoi(rvalue);
    else {
        errno = ENODATA;
        return -1;
    }
}

int _set_huge_pages(size_t pages)
{
    int rval = 0;
    char cmd[256];
    sprintf(cmd, "echo %d > /proc/sys/vm/nr_hugepages", (int)pages);
    rval = system(cmd);
    return rval;
}

void* allocate_huge_pages(size_t size)
{
    size_t current_pages = 0;
    size_t request_pages = 0;
    current_pages = _get_huge_pages();
    if( current_pages == -1)
        return MAP_FAILED;
    request_pages = size/1024/1024/2 + 1;
    request_pages += current_pages;
    int err = _set_huge_pages(request_pages);
   if( err == -1)
        return MAP_FAILED;
    return MALLOC_2M(size);
}

このヘルプトピックについてのフィードバックを送信