web-dev-qa-db-ja.com

posix_memalign / memalignの機能

memalign()posix_memalign()の機能を理解しようとしています。利用可能なドキュメントを読むことは役に立ちませんでした。

誰かがそれがどのように機能し、何のために使用されているのかを理解するのを助けることができますか?または、おそらく使用例を提供しますか?

Linuxメモリの仕組みを理解しようとしています。独自のシンプルなメモリプール(低フラグメンテーションヒープ)を記述する必要があります。

48
twoface88

mallocはアラインメントを持つメモリのチャンクを提供しますが(唯一の要件は、実装がサポートする最大のプリミティブ型にアラインメントする必要があることです)、_posix_memalign_はメモリのチャンクを提供します要求されたアライメントが保証されます。

したがって、例えばposix_memalign(&p, 32, 128)は、開始アドレスが32の倍数であることが保証されている128バイトのメモリチャンクです。

これは、特定のアライメントに従うメモリを必要とするさまざまな低レベル操作(SSE命令、またはDMAを使用するなど)に役立ちます。

59

mallocは常に、いずれかのプリミティブ型に必要な最大アライメントに設定されたメモリを返します。これにより、malloc 'dメモリに必要な型を格納できます。 posix_memalignの説明を理解しているのは、アラインメントとして指定したものの倍数になるアドレスになるメモリロケーションを返すということです。

カスタムメモリプールを記述するときにこれがどれほど役立つかはわかりませんが、これを実装する方法の例を示してみました。私の例との違いは、malloc_alignedで割り当てられたものはすべてfree_alignedで解放する必要があることです。ただし、posix_memalignを使用すると、freeを使用できます。

#include <stdlib.h>
#include <stdio.h>

void *malloc_aligned(size_t alignment, size_t bytes)
{
    // we need to allocate enough storage for the requested bytes, some 
    // book-keeping (to store the location returned by malloc) and some extra
    // padding to allow us to find an aligned byte.  im not entirely sure if 
    // 2 * alignment is enough here, its just a guess.
    const size_t total_size = bytes + (2 * alignment) + sizeof(size_t);

    // use malloc to allocate the memory.
    char *data = malloc(sizeof(char) * total_size);

    if (data)
    {
        // store the original start of the malloc'd data.
        const void * const data_start = data;

        // dedicate enough space to the book-keeping.
        data += sizeof(size_t);

        // find a memory location with correct alignment.  the alignment minus 
        // the remainder of this mod operation is how many bytes forward we need 
        // to move to find an aligned byte.
        const size_t offset = alignment - (((size_t)data) % alignment);

        // set data to the aligned memory.
        data += offset;

        // write the book-keeping.
        size_t *book_keeping = (size_t*)(data - sizeof(size_t));
        *book_keeping = (size_t)data_start;
    }

    return data;
}

void free_aligned(void *raw_data)
{
    if (raw_data)
    {
        char *data = raw_data;

        // we have to assume this memory was allocated with malloc_aligned.  
        // this means the sizeof(size_t) bytes before data are the book-keeping 
        // which points to the location we need to pass to free.
        data -= sizeof(size_t);

        // set data to the location stored in book-keeping.
        data = (char*)(*((size_t*)data));

        // free the memory.
        free(data);
    }
}

int main()
{
    char *ptr = malloc_aligned(7, 100);

    printf("is 5 byte aligned = %s\n", (((size_t)ptr) % 5) ? "no" : "yes");
    printf("is 7 byte aligned = %s\n", (((size_t)ptr) % 7) ? "no" : "yes");

    free_aligned(ptr);

    return 0;
}
10
Node

オリの答えに加えて、さらに重要な問題を指摘したいと思います。

最近のx86アーキテクチャでは、メモリからキャッシュにフェッチできるデータの最小量であるキャッシュラインは64バイトです。構造体のサイズが56バイトで、それらの配列が大きいと仮定します。 1つの要素をルックアップする場合、CPUは2つのメモリリクエストを発行する必要があります(キャッシュラインの途中であっても2つのリクエストを発行する場合があります)。メモリを待たなければならず、キャッシュをより多く使用するため、パフォーマンスにとっては悪いことであり、最終的にキャッシュミス率が高くなります。この場合、posix_memalignを使用するだけでは十分ではありませんが、64バイト境界になるように構造をパディングまたは圧縮する必要があります。

40バイトの構造体を持つのは不運です:)

8
tothphu

どのように機能するかは実装に依存します。この関数の目的は、nバイトにアライメントされたメモリブロックを提供することです(ブロックの開始アドレスはnの倍数です)。

4
MByD

Memalignは廃止されているため(manページを参照)、malloc()とposix_memalign()の違いのみをここで説明します。 malloc()は8バイトでアライメントされます(たとえば、RHEL 32ビットの場合)が、posix_memalign()の場合、アライメントはユーザー定義可能です。これの使用法を知るための、おそらく良い例の1つは、mprotect()を使用してメモリ属性を設定することです。 mprotect()を使用するには、メモリポインターをページに揃える必要があります。したがって、ページサイズをアライメントとしてposix_memalign()を呼び出すと、返されたポインターはmprotect()に簡単に送信して、読み取り/書き込み実行可能属性を設定できます。 (たとえば、データをメモリポインターにコピーした後、そのデータを読み取り専用属性に設定して、変更されないようにすることができます)。 「malloc()」の返されたポインタはここでは使用できません。

3
Peter Teoh

GNU Cposix_memalignを使用する場合、2番目のパラメーターは2のべき乗であるだけでなく、sizeof(void *)の倍数でもあることに注意する必要があります。この要件は、2の累乗のみを必要とするmemalign関数の要件とは異なることに注意してください。

int
__posix_memalign (void **memptr, size_t alignment, size_t size)
{
  void *mem;

  /* Test whether the SIZE argument is valid.  It must be a power of
     two multiple of sizeof (void *).  */
  if (alignment % sizeof (void *) != 0
      || !powerof2 (alignment / sizeof (void *))
      || alignment == 0)
    return EINVAL;


  void *address = RETURN_ADDRESS (0);
  mem = _mid_memalign (alignment, size, address);

  if (mem != NULL)
    {
      *memptr = mem;
      return 0;
    }

  return ENOMEM;
}
weak_alias (__posix_memalign, posix_memalign) 

Posix_memalign実装の最初のif条件を見ると、アライメントがsizeof(void *)の倍数かどうかをチェックします。

0
Jaehyuk Lee

Linux仮想メモリサブシステムでTHPの使用を促進します。 https://youtu.be/fgC6RUlkQE4?t=49

0
lisp-ceo