英語版
このページの英語版を見る

std.experimental.allocator.building_blocks

独自のアロケータを組み立てる

このパッケージは 型付きでないコンポーザブル・メモリ・アロケータも実装している。これらは型付けされていないvoid[] 、割り当てられたメモリがどの型に割り当てられるかという概念を持たないからだ。 型がない。これらはコンポーザブルである。 は、自明でない複雑なアロケータに組み立てることができるビルディング・ブロックだからである。

C言語やC++言語のアロケータとは異なり、これらのアロケータは、割り当てられたサイズを内部で管理する。 C言語やC++言語のアロケータは内部で割り当てサイズを管理する。 これらのアロケータは、クライアントが各メモリのアロケーション・サイズを維持する(あるいは事前に知っている)必要がある。 割り当てられる。簡単に言えば、クライアントは割り当てられたサイズを再割り当て時に渡さなければならない。 を渡さなければならない。アロケータにサイズを保存することは、性能に大きな悪影響を及ぼす。 アロケータにサイズを保存することは、パフォーマンスに重大な悪影響を及ぼす。 なぜなら、クライアントコードはバッファオーバーランを避けるために、割り当てられたサイズを知っておく必要があるからだ。(詳しくは C++におけるサイズ付き の提案で詳しく説明する)。このため、アロケータはトラフィックをvoid[] void*でトラフィックする。

アロケータとして使うためには、型は以下のメソッドを実装しなければならない。 を実装しなければならない。alignmentallocate だけが必須である。他のメソッドのどれかが欠けている場合、そのアロケータはその機能を持っていないとみなされる。 はその機能を持っていないとみなされる(例:あるアロケータはメモリの手動割り当て解除を行わない)。 を提供しない)。アロケータは を実装してはならない。例えば、。 alignedAllocate "処理系定義:"を実装する機能を持たないアロケータは、それを定義すべきではない。 常にnull を返すか例外をスローするように定義するのとは対照的である)。このような 実装がないことは、他のコンポーネントに静的に通知される。 アロケータの能力を他のコンポーネントに静的に通知し、それに応じて設計を決定できるようにする。

メソッド名 セマンティクス
`uint alignment;`
Post: `result > 0`
アロケータが返すすべてのデータの アロケータが返すすべてのデータの最小アライメントを返す。アロケータはalignment を静的に既知のenum 値としてのみ実装することができる。動的に選択されたアライメント値が必要なアプリケーションは 動的に選択されたアライメント値を必要とするアプリケーションは、alignedAllocatealignedReallocate APIを使うべきである。
`size_t goodAllocSize(size_t n);`
Post: `result >= n`
アロケータ は通常、離散的なサイズのチャンクでメモリを割り当てる。したがって n バイトのリクエストは、より大きな割り当てになるかもしれない。割り当てられた余分なメモリは使われない。 未使用となり、いわゆる内部フラグメンテーションを増やすことになる。 関数goodAllocSize(n) は、 の要求に対して割り当てられる実際のバイト数を返す。 n を要求したときに割り当てられる実際のバイト数を返す。このモジュールはデフォルトの実装を定義している。 の倍数に切り上げられたn を返す。 の倍数に切り上げられた値を返す。
`void[] allocate(size_t s);`
Post: `result is null || result.length == s`
s == 0 の場合、呼び出しは任意の空のスライス(null を含む)を返すことができる。そうでない場合、コールはs バイトのメモリを確保し、確保したブロックを返す。 リクエストを満たすことができなかった場合、null を返す。
`void[] alignedAllocate(size_t s, uint a);`
Post: `result is null || result.length == s`
allocate と似ている。 返されるメモリは、少なくともa バイトにアラインされていることが保証される。a は2のべき乗でなければならない。
`void[] allocateAll();` のべき乗でなければならない。 アロケータの全メモリを呼び出し元に提供するので、通常は固定サイズのアロケータで定義される。アロケータが現在 アロケータが現在メモリを管理していない場合、allocateAll() 。 アロケータが現在メモリを管理していない場合、。 を呼んでも成功しないはずである(例えば、allocatenull などを返す)。そうでない場合、allocateAll はベストエフォートでのみ動作する。 アロケータは、利用可能なメモリがあったとしても、null を返すことが許される。 allocateAll 、割り当てられたメモリは特別なものではない。 例えば、定義されていれば、通常のプリミティブで再割り当てまたは割り当て解除できる)。
`bool expand(ref void[] b, size_t delta);`
Post: `!result || b.length == old(b).length + delta`
bdelta バイト拡張する。delta == 0 の場合、b を変更せずに成功する。b is null の場合 false (を返す(ヌル・ポインタをそのまま展開することはできない)。そうでない場合、b は以前に同じアロケータで割り当てられたバッファでなければならない。展開が成功した場合 が成功した場合、expandb の長さをb.length + delta に変更し、 を返す。 true を返す。失敗した場合、この呼び出しはアロケータオブジェクトに変更を与えず、 は変更されないままである。 オブジェクトに変更を与えず、b を変更しないままにし、false を返す。
`bool reallocate(ref void[] b, size_t s);`
Post: `!result || b.length == s`
b をサイズs に再割り当てする。 b は、null または同じアロケータで割り当てられたバッファでなければならない。再割り当てが成功した場合 再割り当てが成功した場合、reallocateb を適切に変更し、 を返す。 true を返す。失敗した場合、この呼び出しはアロケータオブジェクトに変更を与えず、 は変更されないままである。 オブジェクトに変更を与えず、b を変更しないままにし、false を返す。アロケータは アロケータはreallocate を実装すべきである; そうでない場合、このモジュールはreallocate, , で実装された free 関数を定義する。 expand deallocateで実装されたallocate free関数を定義する。
`bool alignedReallocate(ref void[] b,
size_t s, uint a);`
Post: `!result || b.length == s`
reallocate と似ているが 再割り当てされたメモリはa バイトにアライメントされる。バッファは、 の呼び出しで生成されたものでなければならない。 alignedAllocate a は、 よりも2のべき乗大きい値でなければならない。 (void*).sizeofよりも大きくなければならない。 アロケータは、そうすることで何らかの利点を得ることができるのであれば、alignedReallocate を実装すべきである、 そうでない場合、このモジュールは、alignedReallocate 、 、および の観点から実装された 自由関数を定義する。 expand deallocateで実装されたalignedAllocate free関数を定義する。
`Ternary owns(void[] b);` b がこのアロケータで割り当てられた場合はTernary.yes を返す。 を返す。アロケータは、以下の場合にのみこのメソッドを定義すべきである。 アロケータは、所有権を正確かつ高速に決定できる場合(定数時間、対数時間、 または乗算係数の小さい線形時間)。従来のアロケータ のような従来のアロケータはそのような関数を定義していない。b is null の場合、アロケータは はTernary.no を返す。つまり、どのアロケータもnull スライスを所有していない。
`Ternary resolveInternalPointer(void* p, ref void[] result);` もし p はこのアロケータで割り当てられたブロックの内側のどこかへのポインタである、 result は割り当てられたブロックの先頭へのポインタを保持し、それを返す。 Ternary.yes.そうでない場合、resultnull を保持し、Ternary.no を返す。 ポインタがアロケートされたブロックの直後を指している場合、結果は次のようになる。 処理系定義:」である。
`bool deallocate(void[] b);` もしb is null 、何もせず、 を返す。 何もせず、true を返す。そうでない場合は、以前にこのアロケータで割り当てられたメモリを 解放する。 成功すればtrue を返し、そうでなければfalse を返す。成功すれば を返し、そうでなければ を返す。 を常に返すような実装は、このプリミティブを定義してはならない)。 false を返すような実装は、このプリミティブを定義してはならない)。
`bool deallocateAll();`
Post: `empty`
このアロケータで割り当てられたすべてのメモリ をすべて解放する。アロケータがこのメソッドを実装する場合、そのデストラクタがこのメソッドを呼び出すかどうかを指定しなければならない。 デストラクタがこのメソッドを呼び出すかどうかも指定しなければならない。
`Ternary empty();` Ternary.yes アロケータがメモリを保持していない(つまり を返す。 を返す。)
`static Allocator instance;`
Post: `instance is a valid Allocator object`
アロケータにはモノステート、つまりインスタンスだけを持ち、グローバルな状態だけを保持するものもある。 インスタンスしか持たず、グローバルな状態しか保持しない。(例: C言語独自の malloc-ベースのアロケータやDのガベージコレクテッド・ヒープがその例である)。このようなアロケータは 静的なinstance インスタンスを定義しなければならない。 を定義しなければならない。アロケータはステート instance を同時に定義してはならない。アロケータが スレッドセーフかどうかによって、このインスタンスはshared

サンプル・アセンブリ

以下の例では、jemallocをモデルにしたアロケータを使用している。 このアロケータは、内部の断片化を最小限に抑えるために、フリー・リスト・アロケータを間隔をあけて使用している。FList 。 というのも、Segregator はすべてのサイズ選択を事前に行うからである。 を行うためである。
3584バイトまでのサイズは、サイズをずらしたフリーリストで扱われる。サイズ 3585バイトから4072KBまでは、BitmappedBlock 。 で処理される。それ以上のサイズはGCAllocator に直接渡される。
import std.experimental.allocator;
import std.algorithm.comparison : max;

alias FList = FreeList!(GCAllocator, 0, unbounded);
alias A = Segregator!(
    8, FreeList!(GCAllocator, 0, 8),
    128, Bucketizer!(FList, 1, 128, 16),
    256, Bucketizer!(FList, 129, 256, 32),
    512, Bucketizer!(FList, 257, 512, 64),
    1024, Bucketizer!(FList, 513, 1024, 128),
    2048, Bucketizer!(FList, 1025, 2048, 256),
    3584, Bucketizer!(FList, 2049, 3584, 512),
    4072 * 1024, AllocatorList!(n => Region!GCAllocator(max(n, 1024 * 4096))),
    GCAllocator
);
A tuMalloc;
auto b = tuMalloc.allocate(500);
assert(b.length == 500);
auto c = tuMalloc.allocate(113);
assert(c.length == 113);
assert(tuMalloc.expand(c, 14));
tuMalloc.deallocate(b);
tuMalloc.deallocate(c);

スレッド間で共有するメモリを確保する

マルチスレッド・アプリケーションで使われるアロケーション・パターンのひとつに、スレッド間でメモリを共有する方法がある。 ブロックを割り当てたスレッドとは別のスレッドでブロックの割り当てを解除することである。 とは異なるスレッドでブロックの割り当てを解除することである。
このモジュールに含まれるすべてのアロケータは、void[] ( shared void[]).これは、割り当て時、割り当て解除時、または再割り当て時に、メモリは事実上(もしそうであれば、)ではないからである。 再割り当ての時点では、メモリは事実上shared (もしそうなら、アプリケーション・レベルでのバグが明らかになる)。 アプリケーション・レベルでのバグを明らかにする)。
問題は、a.deallocate(b) を割り当てたスレッドとは別のスレッドから を呼び出す場合に残る。 を割り当てたスレッドとは別のスレッドからb 。両方のスレッドがそれぞれのアロケータ・タイプの同じインスタンス にアクセスできなければならない。 a にアクセスできなければならない。D.の定義によれば、これは、、 の定義により、これは、ashared 修飾子を持っている場合にのみ可能である。したがって アロケータ型はallocatedeallocateshared メソッドとして実装しなければならない。そうすれば、アロケータは使用可能なshared インスタンスを許可することになる。
逆に、shared でないアロケータでメモリを確保し、それをスレッド間で渡す。 得られたバッファをshared にキャストすることで)スレッド間で渡し、後で別のスレッドで(別のアロケータで)それをデアロケートする。 別のスレッドで(別のアロケータ・オブジェクトを使うか、同じアロケータ・オブジェクトでそれをキャストした後に)それを解放する。 またはshared にキャストした後に同じアロケータ・オブジェクトで) 別のスレッドでそれをデアロケートすることは違法である。

ブロックの構築

以下の表は、定義済みのアロケータ・ビルディング・ブロックの概要である、 をそれぞれのモジュールと一緒に示している。必要なモジュールを個別にimport 、 またはimport std.experimental.building_blocksを参照する。 publicを参照する。ビルディング・ブロックは無制限に組み立てることができ、また自分のものと組み合わせることもできる。 自分のものと組み合わせることもできる。典型的で有用な組み立て済みアロケータを集めた を参照されたい。 std.experimental.allocator.showcase.

アロケータ
説明
NullAllocator
`std.experimental.allocator.building_blocks.null_allocator`
何もしないのが得意だ。他のアロケータを定義したり 他のアロケータを定義したり、APIを勉強したりするための良い出発点だ。
GCAllocator
`std.experimental.allocator.gc_allocator`
システムが提供するガベージ・コレクター・アロケーター。 これはシステム・メモリを利用するデフォルトのフォールバック・アロケータであるべきだ。これは 手動でfree 、忠実にゴミを収集する。
Mallocator
`std.experimental.allocator.mallocator`
Cのヒープ・アロケータ、別名malloc/realloc/free 。リークしそうにないコードにのみ、控えめに使うこと。 リークしそうもないコードにのみ、控えめに使うこと。
AlignedMallocator
`std.experimental.allocator.mallocator`
OS固有のアロケータへのインターフェース アラインメントの指定をサポートする: posix_memalign Posixと __aligned_xxxWindowsの場合。
AlignedBlockList
`std.experimental.allocator.building_blocks.aligned_block_list`
アロケータリストのラッパー。 のラッパーである。
AffixAllocator
`std.experimental.allocator.building_blocks.affix_allocator`
アロケータ。 アロケータであり、割り当てられた各ブロックに対して余分なプレフィックスバイトやサフィックスバイトを割り当てることができる。
BitmappedBlock
`std.experimental.allocator.building_blocks.bitmapped_block`
1つの連続したメモリチャンクを、等しいサイズのブロックに整理する。 ブロックごとに1ビットのコストで割り当てステータスを追跡する。 ブロックごとに1ビットのコストで割り当て状況を追跡する。
FallbackAllocator
`std.experimental.allocator.building_blocks.fallback_allocator`
プライマリとフォールバックの アロケータである。アロケーション要求はまずプライマリで試され、失敗するとフォールバックに渡される。 失敗するとフォールバックに渡される。小規模で高速なアロケータに有用である。 汎用のアロケータの前段にある。
FreeList
`std.experimental.allocator.building_blocks.free_list`
アロケータは、他のアロケータの上にフリーリストを実装する。アロケータには 優先サイズ、許容範囲、最大要素はコンパイル時と実行時に設定可能である。 実行時に設定可能である。
SharedFreeList
`std.experimental.allocator.building_blocks.free_list`
FreeList と同じ機能だが、次のようにパッケージ化されている。 shared としてパッケージ化されている。
FreeTree
`std.experimental.allocator.building_blocks.free_tree`
FreeList に似たアロケータ。 バイナリサーチツリーを使用し、1つだけでなく多数の空きリストを適応的に格納する。
Region
`std.experimental.allocator.building_blocks.region`
リージョン・アロケータは、単純なバンプ・ザ・ポインタ・アロケータとしてメモリのチャンクを整理する。 単純なバンプ・ザ・ポインタ・アロケータである。
InSituRegion
`std.experimental.allocator.building_blocks.region`
リージョンはそれ自身のアロケーションを保持する。 スタック上にある。静的に決定されたサイズを持つ。
SbrkRegion
`std.experimental.allocator.building_blocks.region`
リージョンは sbrkを使ってメモリを確保する。
MmapAllocator
`std.experimental.allocator.mmap_allocator`
アロケータ mmapを直接使う。
StatsCollector
`std.experimental.allocator.building_blocks.stats_collector`
他の アロケータに関する統計情報を収集する。
Quantizer
`std.experimental.allocator.building_blocks.quantizer`
粗視化されたクォンタで割り当てを行う。 再割り当てのパフォーマンスを向上させる。欠点は、割り当てられたメモリと未使用のメモリがあるため、メモリ消費量が多くなることだ。
AllocatorList
`std.experimental.allocator.building_blocks.allocator_list`
アロケータ・ファクトリーが与えられると、必要な数だけアロケータを作成する。 アロケータファクトリーが与えられると、割り当て要求を満たすために必要な数のアロケータを遅延的に作成する。アロケータは リンクリストに格納される。アロケータはリンクリストに格納される。 リストを線形に検索することで満たされる。
Segregator
`std.experimental.allocator.building_blocks.segregator`
アロケーション要求をサイズごとに分け アロケータにディスパッチする。
Bucketizer
`std.experimental.allocator.building_blocks.bucketizer`
割り当てサイズを個別のバケットに分割し バケットごとに1つのアロケータのアレイを使用して要求を満たす。
AscendingPageAllocator
`std.experimental.allocator.building_blocks.ascending_page_allocator`
メモリセーフアロケータ。 ここで、サイズはページサイズの倍数に丸められ、割り当ては増加するアドレスで満たされる。