カスタムヒープアロケーター


9

ほとんどのプログラムは、関数型プログラミング言語が古いオブジェクトを変更するよりも新しいオブジェクトを割り当てることを好み、ガベージコレクターが解放することを心配する程度にまで、ヒープの割り当てについてかなりカジュアルになり得ます。

ただし、組み込みプログラミングのサイレントセクターでは、メモリとリアルタイムの制約により、ヒープ割り当てをまったく使用できないアプリケーションが数多くあります。処理される各タイプのオブジェクトの数は仕様の一部であり、すべてが静的に割り当てられます。

ゲームプログラミング(少なくともハードウェアのプッシュに意欲的なゲームでは)は、その中間になることがあります。動的割り当てを使用できますが、アロケーターをブラックボックスとして処理できない十分なメモリとソフトリアルタイム制約があります、ガベージコレクションはもちろんのこと、カスタムアロケータを使用する必要があります。これが、ゲーム業界でC ++が依然として広く使用されている理由の1つです。http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2271.htmlのようなことができます

その中間の領域には他にどのようなドメインがありますか?ゲームとは別に、カスタムアロケーターが頻繁に使用されていますか?


1
一部のOSは、オブジェクトキャッシングを提供するスラブアロケーターを使用しますが、オブジェクトのメンバーを2 ** Nのモジュロインデックスキャッシュの異なるセットにマッピングすることで、プロセッサーキャッシュの競合ミスを減らすこともできます(両方とも、隣接するメモリに複数のインスタンスを配置することで、スラブ内の可変パディングによる)。キャッシュの動作は、割り当て/解放速度やメモリ使用よりも重要な場合があります。
ポールA.クレイトン

回答:


4

パフォーマンスを重視するクリティカルパスを持つアプリケーションがある場合は常に、メモリの処理方法を気にする必要があります。ほとんどのエンドユーザークライアント側アプリケーションは、主なイベント駆動型であり、ほとんどのイベントはユーザーとの対話から発生し、パフォーマンスの制約が(もしあれば)それほどないため、このカテゴリに分類されません。

ただし、多くのバックエンドソフトウェアは、メモリの処理方法にいくらか焦点を合わせる必要があります。そのソフトウェアの多くは、より多くのクライアント、より多くのトランザクション、より多くのデータソースを処理できるようにスケールアップできるためです。限界を押し広げると、ソフトウェアがユーザーのメモリを分析し、ソフトウェアに合わせたカスタム割り当てスキームを記述し始めることができます。想像できるユースケースを処理するために作成された完全に汎用的なメモリアロケーターに依存する必要はありません。

いくつか例を挙げましょう...私の最初の会社では、歴史的パッケージ、プロセス制御データの収集/保存/アーカイブを担当するソフトウェア(工場、原子力発電所、数千万のセンサーを備えた石油精製所などを考えています)そのデータを保存します)。Historianがより多くのデータを処理するのを妨げるパフォーマンスのボトルネックを分析したときはいつでも、ほとんどの場合、問題はメモリの処理方法にありました。絶対に必要な場合を除いて、malloc / freeが呼び出されないようにするために、長い時間をかけてきました。

現在の仕事では、監視ビデオデジタルレコーダーと分析パッケージに取り組んでいます。30 fpsでは、各チャネルは33ミリ秒ごとにビデオフレームを受信します。私たちが販売しているハードウェアでは、100チャンネルのビデオを簡単に録画できます。これは、クリティカルパス(ネットワークコール=>キャプチャコンポーネント=>レコーダー管理ソフトウェア=>ストレージコンポーネント=>ディスク)に動的メモリ割り当てがないことを確認する別のケースです。バッファの固定サイズのバケットを含み、LIFOを使用して以前に割り当てられたバッファを再利用するカスタムフレームアロケータがあります。600Kbのストレージが必要な場合は、1024Kbのバッファが必要になることがありますが、これはスペースを浪費しますが、各割り当てが非常に短期間である私たちの用途に合わせて特別に調整されているため、バッファが使用されるため、うまく機能します。

私が説明した種類のアプリケーション(大量のデータをAからBに移動し、多数のクライアント要求を処理する)では、ヒープに行き来して、CPUパフォーマンスのボトルネックの主な原因になります。ヒープの断片化を最小限に抑えることは二次的な利点ですが、私が知る限り、ほとんどの最新のOSはすでに低断片化ヒープを実装しています(少なくともWindowsは知っているので、他のOS も同様に期待しています)。個人的には、これらのタイプの環境で12年以上働いていて、ヒープに関連するCPU使用率の問題をかなり頻繁に目にしましたが、実際に断片化されたヒープに実際に苦しんでいるシステムを見たことはありません。


「絶対に必要な場合を除いて、malloc / freeが呼び出されないようにするために長い時間を費やしました...」 -ルーターを構築するハードウェアの人を知っています。彼らも気にしないでくださいmalloc/free。それらはメモリのブロックを予約し、それをカーソルデータ構造として使用します。彼らの仕事のほとんどは、インデックスを追跡することだけになりました。

4

ビデオ処理、VFX、オペレーティングシステムなど。効率的な割り当てを実現するために、データ構造とアロケータを分離する必要はありません。

たとえば、オクツリー内の効率的なツリーノードの割り当てをオクツリー自体から分離し、外部アロケータに依存するために、多くの複雑さが導入されています。変更する理由の数が増えるわけではないので、これらの2つの懸念事項を融合してoctreeが一度に連続して多くのノードを割り当てるのはSRPの違反ではありません。実際には、減少する可能性があります。

たとえば、C ++では、標準のコンテナーを外部アロケーターに依存させることによる遅延副作用の1つにより、リンクされた構造がC ++コミュニティのようにstd::mapなりstd::list、C ++コミュニティによってほとんど役に立たないと見なされています。std::allocator一方、これらのデータ構造は一度に1つのノードを割り当てます。もちろん、その場合、リンクされた構造のパフォーマンスは低下しますが、リンクされた構造に対するノードの効率的な割り当てが、アロケーターではなくデータ構造の責任であると見なされた場合、状況は大きく異なります。メモリー追跡/プロファイリングなどの他の理由でカスタム割り当てを引き続き使用する可能性がありますが、ノードを一度に1つずつ割り当てようとするときに、アロケーターに依存してリンクされた構造を効率化すると、デフォルトでそれらすべてが非常に非効率になります。リンクされた構造がフリーリストのようなカスタムアロケータを必要とすることで十分な効率が得られ、左右のキャッシュミスのトリガーを回避できるというよく知られた警告が付いていれば問題ありません。はるかに実際に適用できるのは、std::list<T, BlockSize, Alloc>、ここBlockSizeで、フリーリストに一度に割り当てる隣接ノードの数を示します(1を指定するstd::listと、現在のように効果的につながります)。

しかし、そのような警告はありません。リンクされたリストは役に立たないというカルトマントラをエコーするブロックヘッドのコミュニティ全体につながります。


3

カスタムアロケータが必要になる可能性があるもう1つの領域は、ヒープの断片化を防ぐことです。時間の経過とともに、ヒープは断片化された小さなオブジェクトをヒープ全体に割り当てる可能性があります。プログラムがヒープメモリを一緒に維持できない場合、プログラムがより大きなオブジェクトを割り当てようとすると、既存の断片化されたヒープの間に空きブロックが見つからないため、システムからより多くのメモリを要求する必要があります(小さすぎるオブジェクトが邪魔です)。プログラムの総メモリ使用量は時間の経過とともに増加し、不必要にメモリの追加ページを消費します。したがって、これは長期間実行することが予想されるプログラム(データベース、サーバーなど)にとってかなり大きな問題です。

ゲームとは別に、カスタムアロケーターが頻繁に使用されていますか?

フェイスブック

Facebookがヒープのパフォーマンスを改善し、断片化を減らすために使用し始めているjemallocを確認してください。


正しい。ただし、コピーガベージコレクターは断片化の問題を適切に解決しますよね。
rwallace 2011
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.