5

一度にページ全体を使用して、プログラムのメモリ割り当てを最適化しようとしています。

私は次のようにページサイズを取得しています。sysconf(_SC_PAGESIZE);次に、次のようにページに収まる要素の総数を計算します。elements=pageSize/sizeof(Node);

実際にmallocに行くと、自分の記憶を使うmalloc(elements*sizeof(Node)); と思っていました。sifeof(Node)の乗算と除算はキャンセルされるようですが、整数除算ではそうではないと思います。

これは、一度にページ全体をmallocするための最良の方法ですか?

ありがとう

4

5 に答える 5

8

このmalloc関数には、ページサイズの概念はありません。ページ境界にも揃えられているページを割り当てない限りmalloc、この方法で呼び出すことによるメリットはありません。必要mallocな数の要素を使用し、ほとんど確実にまったくメリットがないものをマイクロ最適化することについて心配する必要はありません。

はい、Linuxカーネルは常にこのようなことをします。それには2つの理由があります。

  1. 割り当てが失敗するリスクが大幅に高まるため、ページよりも大きいブロックを割り当てる必要はありません。
  2. カーネルの割り当ては、一度に大量のメモリを割り当て、それを小さなコンポーネントに分割するCライブラリのようにではなく、ページごとに行われます。

本当にページサイズのメモリを割り当てたい場合は、からの結果をsysconf(_SC_PAGESIZE)サイズ引数として使用します。しかし、割り当てが2ページにまたがることはほぼ確実です。

于 2013-02-21T16:37:47.387 に答える
5

計算では、によって返されるメモリのブロック/チャンクに追加されるメタデータはelements=pageSize/sizeof(Node);考慮されません。多くの場合、は、少なくとも境界上に整列されている可能性が高いメモリブロックを返します(32バイトは非常に一般的になっています...)。メモリブロックをページに揃え、そのチャンクを(パディング付きで)追加し、フルページサイズのデータ​​を書き込むと、最後のバイトが最初のページから外れるため、2ページを使用することになります。malloc()malloc()malloc()min(sizeof(double),2 * sizeof(void *))malloc()

コメントで提案されているように、mmap()/を使用せずに、メモリを浪費することを心配せずに、あなただけのためにページ全体が必要ですか?VirtualAlloc()はい、どうぞ:

int ret;
void *ptr = NULL;
size_t page_size = sysconf(_SC_PAGESIZE);

ret = posix_memalign(&ptr, page_size, page_size);
if (ret != 0 || ptr == NULL) {
    fprintf(stderr, "posix_memalign failed: %s\n", strerror(ret));
}

ちなみに、これはおそらくマイクロ最適化についてです。Nodeキャッシュラインのサイズの倍数があることや、キャッシュの局所性を改善する方法、メモリの断片化を減らす方法をまだ確認していない可能性があります。したがって、おそらく間違った方向に進んでいます。最初に機能するようにし、プロファイリングし、アルゴリズムを最適化し、プロファイリングし、最後のオプションでマイクロ最適化します。

于 2013-02-21T17:01:12.663 に答える
3

C11標準ではaligned_alloc呼び出しが追加されたため、次のようなことができます。

#include <stdlib.h>
#include <unistd.h>

void *alloc_page( void )
{
    long page_size = sysconf( _SC_PAGESIZE );  /* arguably could be a constant, #define, etc. */

    return ( page_size > 0 ? aligned_alloc( page_size, page_size ) : NULL );
}

他の人が指摘しているように、このアプローチの問題は、通常、標準のalloc呼び出しの実装により、割り当てられたメモリの直前に格納される簿記のオーバーヘッドが追加されることです。したがって、この割り当ては通常、2つのページにまたがります。使用するために返されたページと、アロケータの簿記で使用される別のページの最後です。

つまり、このメモリを解放または再割り当てするときに、1ページだけでなく2ページに触れる必要がある場合があります。また、この方法でメモリのすべてまたはほとんどを割り当てると、OSレベルでプロセスに割り当てられたページの約半分がアロケータの簿記に使用されるのはごくわずかであるため、大量の仮想メモリを「浪費」する可能性があります。 。

これらの問題がどれほど重要であるかを一般的に言うのは難しいですが、どうにかして回避することが望ましいでしょう。残念ながら、私はまだそれを行うためのクリーンで簡単でポータブルな方法を見つけていません。

==============================

補遺:mallocのメモリオーバーヘッドを動的に把握し、それが常に一定であると仮定できる場合、それを求めることは通常、私たちが望むものを私たちに与えることはありませんか?

#include <stdlib.h>
#include <unistd.h>

/* decent default guesses (e.g. - Linux x64) */

static size_t Page_Size       = 4096;
static size_t Malloc_Overhead = 32;

/* call once at beginning of program (i.e. - single thread, no allocs yet) */

int alloc_page_init( void )  
{
    int     ret       = -1;
    long    page_size = sysconf( _SC_PAGESIZE );
    char   *p1        = malloc( 1 );
    char   *p2        = malloc( 1 );
    size_t  malloc_overhead;

    if ( page_size <= 0 || p1 == NULL || p2 == NULL )
        goto FAIL;

    malloc_overhead = ( size_t ) ( p2 > p1 ? p2 - p1 : p1 - p2 );  /* non-standard pointer math */

    if ( malloc_overhead > 64 || malloc_overhead >= page_size )
        goto FAIL;

    Page_Size       = page_size;
    Malloc_Overhead = malloc_overhead;
    ret             = 0;

FAIL:
    if ( p1 )
        free( p1 );

    if ( p2 )
        free( p2 );

    return ret;
}

void *alloc_page( void )
{
    return aligned_alloc( Page_Size - Malloc_Overhead, Page_Size - Malloc_Overhead );
}

回答:おそらくそうではありません。たとえば、「「実装によってサポートされる」要件の例として、POSIX関数posix_memalignは、2の累乗とsizeof(void *)の倍数であり、POSIXベースのアライメントを受け入れるためです。 align_allocの実装は、これらの要件を継承します。」

上記のコードは、2の累乗であるアライメントを要求しない可能性が高いため、ほとんどのプラットフォームで失敗する可能性があります。

これは、標準の割り当て関数の一般的な実装では避けられない問題のようです。したがって、ページサイズに基づいて調整して割り当て、別のページにあるアロケータの簿記のペナルティを支払うか、mmapなどのOS固有の呼び出しを使用してこの問題を回避するのがおそらく最善です。

于 2018-03-21T16:35:16.497 に答える
1

規格mallocは、ページサイズの概念さえ持っているという保証を提供しません。ただし、要求された割り当てサイズがページサイズのオーダー(またはそれ以上)の場合、malloc実装がページ全体を使い果たすことは珍しくありません。

たまたまページサイズ(またはページサイズの倍数)に等しい割り当てを要求し、それを自分で細分化しても、少し余分な作業ですが、確かに害はありません。少なくとも一部のマシン/コンパイラ/ライブラリの組み合わせでは、実際に希望する動作が得られる可能性があります。しかし、あなたもそうではないかもしれません。ページサイズの割り当てやページ整列メモリがどうしても必要な場合は、OS固有のAPIを呼び出して取得する必要があります。

于 2013-02-21T18:44:23.193 に答える
1

メモリページ全体を割り当てる方法について質問がある場合:mmap()ではなく、を使用しmalloc()ます。
理由:
malloc()すべての割り当てに常にメタデータを追加する必要があるため、追加するmalloc(4096)と、必ず複数のページが割り当てられます。mmap()一方、は、ページをアドレス空間にマップするためのカーネルのAPIです。それmalloc()はボンネットの下で使用するものです。

a正しい丸めに関する質問の場合:の倍数に切り上げる通常のトリックNは、と言うことrounded = (a + N-1)/N*N;です。最初に加算することによりN-1、すべての場合に除算が切り上げられるようにします。aすでにの倍数である場合N、追加されたN-1ものは効果がありません。他のすべての場合では、よりも1つ多くなりrounded = a/N*N;ます。

于 2018-03-21T17:09:42.460 に答える