jemallocを読んでみる(その5)
図を描く作業をしていてjemallocのアリーナの構造を全部一度に見せることは不可能と思いましたので、1つづつ構造体を説明していくことにします。今日は一番上に来るarena_tです。
typedef struct arena_s arena_t;
arena_tは実際にはstruct arena_sという構造体です。jemallocの構造体はほとんど(多分全部)struct xxxxx_sという名前になっていて、xxxxx_tという型が定義されます。
struct arena_s { #ifdef MALLOC_DEBUG uint32_t magic; # define ARENA_MAGIC 0x947d3d24 #endif
magicはデバッグ目的のみで使っているみたいです。ちゃんとしたアリーナが渡っているかどうかのチェックに使います。
/* All operations on this arena require that lock be locked. */ このアリーナに対して何かするときは、ロックを取得する必要があります #ifdef MOZ_MEMORY malloc_spinlock_t lock; #else pthread_mutex_t lock; #endif
ロックを格納しておくメンバーです。ロックの取得は第3回で見たとおりです。
#ifdef MALLOC_STATS arena_stats_t stats; #endif
アリーナの統計情報を格納します。例えば、small領域にはどんだけメモリがあってどれだけ使われているかとかそんな情報が入っています。
/* * Tree of chunks this arena manages. */ このアリーナで管理しているチャンクの木 arena_chunk_tree_t chunks;
アリーナは割り当てるメモリをチャックという単位で管理しています。さらに、チャンクは木(細かく言うと赤黒木)で管理しています。その木の根っこがこれに入ります。
/* * In order to avoid rapid chunk allocation/deallocation when an arena * oscillates right on the cusp of needing a new chunk, cache the most * recently freed chunk. The spare is left in the arena's chunk tree * until it is deleted. * * There is one spare chunk per arena, rather than one spare total, in * order to avoid interactions between multiple threads that could make * a single spare inadequate. */ チャンクの必要数が激しく振動するような場合に、頻繁な割り当て、解放が 生じるのを防ぐために、もっとも最近に解放されたチャンクをキャッシュします。 このスペア(キャッシュされたチャンク)は削除されるまでアリーナのチャック木に 残されます。 このスペアはアリーナ毎に1つあります。全部で1つにしないのは複数のスレッドで 取り合いになるのを避けるためです。 arena_chunk_t *spare; /* * Current count of pages within unused runs that are potentially * dirty, and for which madvise(... MADV_FREE) has not been called. By * tracking this, we can a limit on how much dirty unused * memory is mapped for each arena. */ 潜在的に汚れていて、madvise(... MADV_FREE)を呼んでいない、使用していない列(runs) を含むページの数。これを追跡することで、それぞれのアリーナでどれだけ汚れた 未使用のメモリ領域が割り付けられたか知ることが出来ます。 (訳注 意味がわからない・・・(汗。 OSの勉強も必要そう) size_t ndirty; /* * Trees of this arena's available runs. Two trees are maintained * using one set of nodes, since one is needed for first-best-fit run * allocation, and the other is needed for coalescing. */ アリーナで使用できる列(run)の木。1組のノードを2つの木で管理しています。 1つはfirst-best-fitによる割り当てのため、もう1つは合体(訳注: 隣り合うfreeされた 列を合体させる話だと思う)のためです。 extent_tree_szad_t runs_avail_szad; extent_tree_ad_t runs_avail_ad; /* Tree of this arena's allocated (in-use) runs. */ 使用中の列の木 extent_tree_ad_t runs_alloced_ad; #ifdef MALLOC_BALANCE /* * The arena load balancing machinery needs to keep track of how much * lock contention there is. This value is exponentially averaged. */ アリーナのロードバランス機構はロックの衝突状態を追跡する必要がありますが、 それをここに格納しておきます。この値は指数平均を取ります。 (訳注 第3回で説明したメンバーです) uint32_t contention; #endif #ifdef MALLOC_LAZY_FREE /* * Deallocation of small objects can be lazy, in which case free_cache * stores pointers to those objects that have not yet been deallocated. * In order to avoid lock contention, slots are chosen randomly. Empty * slots contain NULL. */ 小さいオブジェクトの解放は後回しにすることが出来ます。そのような場合、 free_cacheにはまだ解放していないオブジェクトのポインタを格納しておきます。 ロックの衝突を防ぐため、スロットはランダムに選ばれます 空のスロットはNULLが入ります void **free_cache; #endif
これはコメントのとおりというか、これ以上詳しく説明できません。jemallocで感心するところはいっぱいありますが、的確で少なすぎず過剰すぎないコメントもその1つです。
/* * bins is used to store rings of free regions of the following sizes, * assuming a 16-byte quantum, 4kB pagesize, and default MALLOC_OPTIONS. */ binsは次に示すサイズの領域の輪を格納します。このサイズは16byte単位で割り当て、 4KBのページサイズそして、デフォルトのMALLOC_OPTIONSである場合です。 /* * bins[i] | size | * --------+------+ * 0 | 2 | * 1 | 4 | * 2 | 8 | * --------+------+ * 3 | 16 | * 4 | 32 | * 5 | 48 | * 6 | 64 | * : : * : : * 33 | 496 | * 34 | 512 | * --------+------+ * 35 | 1024 | * 36 | 2048 | * --------+------+ */ arena_bin_t bins[1]; /* Dynamically sized. */ };
小さなサイズの割り当てはあらかじめそのサイズの領域を複数個用意しておいて、それを渡すということをしています。その領域はサイズごとにポインタで輪を作ってbinsに格納されます。
ということで、今日はarena_tを説明し終わって終わりです。きりがよく終わったのは第1回以来じゃないだろうか?今後もこうありたいものです。
つづく