54.2. アラインメントの仕組み

 前の項目で MINSIZE というマクロ出てきましたね。

 MINSIZE マクロの定義内には MALLOC_ALIGN_MASK なるマクロが出てきます。

https://github.com/MacKomatsu/glibc/blob/release/2.27/master/malloc/malloc.c

1195 #define MINSIZE  \
1196   (unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))

 MINSIZE マクロでは最少チャンクサイズ(MIN_CHUNK_SIZE)に対して MALLOC_ALIGN_MASK マスクビットを足してから、マスクビットのビット単位否定値とで AND 演算処理をしてますね。

 この MALLOC_ALIGN_MASK マクロの定義は malloc-internal.h にあるはずです。

https://github.com/MacKomatsu/glibc/blob/release/2.27/master/malloc/malloc-internal.h

 54 #ifndef INTERNAL_SIZE_T
 55 # define INTERNAL_SIZE_T size_t
 56 #endif
 57
 58 /* The corresponding word size.  */
 59 #define SIZE_SZ (sizeof (INTERNAL_SIZE_T))
 60
 61 /* The corresponding bit mask value.  */
 62 #define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)

 つまりマスク値から 1 を引いた値を割り当て済みポインターに足して繰り上げ、そして下位ビットが 0 の値に対して AND 演算子を適用してるわけです。

 これによって下位のビットをリセットしてるだけなんですが、最少アラインメントでなく、もう少し具体的なアラインメントの実装をしながら考えて見ましょう。

main.c. 

  1 #include <stdlib.h>
  2 #include <stdio.h>
  3
  4 int main()
  5 {
  6   int *ptr, *ptr_new, *ptr_next, *ptr_next_new;
  7   const size_t size = 10;
  8   const size_t alignment = 256;
  9   ptr = malloc(size + alignment - 1);
 10   ptr_new = (int*)(((unsigned long)ptr + alignment - 1) & ~(alignment - 1));
 11   printf("old:  %p\n",ptr);
 12   printf("new:  %p\n",ptr_new);
 13   ptr_next = malloc(size + alignment - 1);
 14   printf("old:  %p\n",ptr_next);
 15   ptr_next_new = (int*)(((size_t)ptr_next + alignment - 1) & ~(alignment - 1));
 16   printf("new:  %p\n",ptr_next_new);
 17
 18   free(ptr);
 19   free(ptr_next);
 20   return 0;
 21 }

ビルドと実行結果. 

$ gcc main.c
$ ./a.out
old:  0x5596c692f260
new:  0x5596c692f300
old:  0x5596c692f790
new:  0x5596c692f800

 このソースコードは malloc() で割り当てたメモリーアドレスのアラインメントをしています。

 この例では malloc() で割り当てたポインターと、そのポインターのアラインしたアドレスの 2 種類をつかいます。

 さらに 2 回分の割り当てをするので計 4 個のポインター変数が必要となります。

  6   int *ptr, *ptr_new, *ptr_next, *ptr_next_new;

 ptr と ptr_next が malloc() で割り当てたポインターとなり、 ptr_new と ptr_next_new がアラインしたアドレスとなると考えてくださいね。

 ヒープで割り当てしたいバイトサイズは 10 バイトなんですが、アラインメントは 256 バイトとします。

  7   const size_t size = 10;
  8   const size_t alignment = 256;

 次に malloc() で割り当てをしてみましょう。

  9   ptr = malloc(size + alignment - 1);

 何故か無駄な割り当てをしてますね、あははは… ははは… (´・ω・`)

 このアラインメントはアラインするための予備スペースまたは必要コストだと考えてください。

 この必要コストが発生する理由は次の行を見るとわかります。

 10   ptr_new = (int*)(((unsigned long)ptr + alignment - 1) & ~(alignment - 1));

 (256 - 1) は 0000000011111111 となりますが、これのビット単位の否定は 1111111100000000 となります。

 つまりポインターに対して 1111111100000000 との AND 演算子を適用すれば下位ビットは 0 にリセットできます。

Copyright 2018-2019, by Masaki Komatsu