カスタムアロケータは、パフォーマンスの向上、メモリ効率の改善、および頻繁な小規模メモリ割り当ての問題解決に役立ちます。
前縁
最近、ネットワークデータパケットの開発に携わり、頻繁な小さなメモリの割り当てと解放が必要になった。メモリプールを使用しようと考えたが、既存のメモリプールの調査でこれを発見した。
https://github.com/cacay/MemoryPool
インターフェースを見たときから、少し疑問に思っていた。このメモリプールの実装がなんだか奇妙だ。__INLINE_CODE_0__的实现逻辑,是在申请固定大小的内存空间。看过boost的内存池接口,提供的是一个模板,用的时候进行实例化。正巧这个库已经有文章进行过介绍,提到了__INLINE_CODE_1__という概念。
wiki
C++プログラミングにおいて、アロケータ(allocator)はC++標準ライブラリの重要な構成要素です。コンテナ(container)と呼ばれる様々なデータ構造(例えば、リストや集合など)が定義されており、これらのコンテナには実行時にサイズを変更できるという共通の特徴があります。この実現のために動的なメモリ割り当てが必要となり、アロケータはそのメモリ割り当てと解放のリクエストを処理するために使用されます。つまり、アロケータはSTLコンテナのメモリ管理における低レベルの詳細をカプセル化するものです。デフォルトでは、C++標準ライブラリは独自の汎用アロケータを使用しますが、必要に応じてプログラマはカスタムアロケータを作成して置き換えることも可能です。
アロケータは当初、アレクサンドル・ステパノフによってC++標準テンプレートライブラリ(STL)の一部として考案され、「ライブラリをより柔軟にし、基盤となるデータモデルに依存しない方法」を提供し、プログラマがカスタムのポインタや参照型を利用できるようにすることを目的としていました。しかし、C++標準に取り込む際、C++標準委員会は完全なデータモデル抽象化による容認できないパフォーマンス損失を認識し、その代替案としてアロケータに対する制限が厳しくなりました。その結果、現在の規格で記述されているアロケータのカスタマイズ性は、ステパノフの当初の構想に比べて大幅に制限されています。
割り当て方のカスタマイズには制限があるものの、多くの状況でカスタムアロケータが必要となる場合があり、これは主に異なるメモリ空間(共有メモリやリサイクル済みメモリなど)へのアクセス方法をカプセル化するため、あるいはメモリプールを用いたメモリ割り当て時のパフォーマンス向上を目的として行われる。また、頻繁に少量メモリを割り当てるプログラムにおいては、専用のカスタムアロケータを導入することで、メモリ使用量と実行時間の両面で大きな恩恵が得られる場合がある。
申し訳ありませんが、翻訳する中国語のテキストが提供されていません。テキストを提供していただければ、日本語に翻訳します。
カスタムアロケータを定義する主な理由の一つは、パフォーマンスの向上です。専用のカスタムアロケータを使用することで、プログラムのパフォーマンスを向上させたり、メモリ使用効率を高めたり、あるいはその両方を実現できます。[4][8] デフォルトのアロケータはnew演算子を使用してメモリ空間を割り当てますが、これはしばしばC言語のヒープ割り当て関数(malloc())によって実装されます。[9] ヒープ割り当て関数は、大量のメモリを一度に割り当てる場合に最適化されていることが多いため、ベクトルや両端キューなどのメモリを大量に割り当てるコンテナでは、デフォルトのアロケータは一般的に効率的です。[8] しかし、連想コンテナや双方向リストなど、頻繁に少量ずつメモリを割り当てる必要があるコンテナの場合、デフォルトのアロケータを使用すると通常、効率が低下します。[4][9] さらに、malloc()ベースのデフォルトアロケータには、参照局所性の低さ[4]や、メモリ断片化を引き起こす可能性[4][9]などの問題も存在します。
要するに、この部分(……)は、この規格がディスペンサーに対して行う「私には夢がある」のスピーチのようなものだ。夢が実現するまでは、可搬性を気遣うプログラマーは、ステートレスなカスタムアロケータの使用に縛られることになるだろう。 スコット・メイヤーズ『Effective STL』 この状況を鑑みると、頻繁な少量メモリ割り当ての問題を解決するために、メモリプールベースの割り当て器がよく用いられる[8]。デフォルトの「オンデマンド割り当て」とは異なり、メモリプールベースの割り当て器では、プログラムは事前に大きなメモリブロック(「メモリプール」)を確保し、メモリが必要な際には、カスタム割り当て器は単にプールのメモリへのポインタを要求者に返すだけです。オブジェクトの破棄時には、実際にメモリを解放する代わりに、メモリプールのライフサイクルが終了した時点でまとめて解放されます[注 1][8]。
「カスタムアロケータ」という話題に関して、すでに多くのC++専門家や関連著者が議論に参加しており、例えばスコット・メイヤーズの『Effective STL』やアンドレイ・アレクサンドレスキューの『Modern C++ Design』にも言及されています。メイヤーズは、ある型Tに対するアロケータのすべてのインスタンスが等しいと要求する場合、可搬性のあるアロケータのインスタンスは状態を含んではならないことに気づきました。C++標準は、ライブラリの実装者がステートフルなアロケータをサポートすることを推奨していますが[文 4]、メイヤーズはこの関連箇所を「(一見)素晴らしい考え」と述べつつも、ほとんどナンセンスであり、アロケータの制限は「あまりにも厳しすぎる」[4]と批判しています。例えば、STLのlistはspliceメソッドを許可しており、これはlistオブジェクトAのノードが直接別のlistオブジェクトBに移動されることを意味します。これにより、Aのアロケータによって割り当てられたメモリが、Bのアロケータによって解放可能である必要があり、それによりAとBのアロケータインスタンスが等しいことが推論されます。メイヤーズの結論は、アロケータを静的方法を使用する型として定義するのが最適であるというものです。例えば、C++標準によると、アロケータはrebindメソッドを実装したotherクラステンプレートを提供する必要があります。
另外、C++プログラミング言語のビアニ・ストラウスルップは、「各オブジェクトの情報が異なることを防ぐための厳格なアロケータの制限」については「問題ない」(大意)と述べ、ほとんどのアロケータには状態が必要なく、むしろ状態がない方が性能が良い場合もあると指摘しています。彼は、メモリプール型アロケータ、共有メモリ型アロケータ、ガベージコレクション型アロケータという3つのカスタムアロケータの利用例を提示し、内部メモリプールを利用して少量メモリを高速に割り当て/解放するアロケータの実装例を示しました。しかし、彼はそのような最適化は既に彼が提供しているサンプルアロケータで実現されている可能性もあると述べています。[3]
カスタムアロケータの別の用途は、メモリ関連のエラーをデバッグすることです[10]。そのためには、割り当て時に追加のメモリを割り当ててデバッグ情報を格納するアロケータを作成できます。このようなアロケータは、メモリが同じ種類の割り当て/解放アロケータによって割り当て/解放されることを保証するだけでなく、キャッシュオーバーフローからプログラムをある程度保護することもできます[11]。