プロジェクトの 1 つでカスタム ヒープ実装を使用しています。これは、次の 2 つの主要部分で構成されています。
固定サイズのブロック ヒープ。つまり、特定のサイズのブロックのみを割り当てるヒープです。より大きなメモリ ブロック (仮想メモリ ページまたは別のヒープから) を割り当て、アトミックな割り当て単位に分割します。
割り当て/解放を (O(1) で) 高速に実行し、外部ヒープによって課されることを考慮せずに、メモリ使用量のオーバーヘッドはありません。
グローバル汎用ヒープ。上記の (固定サイズの) ヒープのバケットで構成されます。要求された割り当てサイズを WRT して、適切なバケットを選択し、それを介して割り当てを実行します。
アプリケーション全体が (かなり) マルチスレッド化されているため、グローバル ヒープは操作中に適切なバケットをロックします。
注: 従来のヒープとは対照的に、このヒープは、割り当てだけでなく、解放にも割り当てサイズを必要とします。これにより、検索や余分なメモリ オーバーヘッド (割り当てられたブロックの前にブロック サイズを保存するなど) なしで、適切なバケットを特定できます。やや不便ですが、私の場合はこれで問題ありません。さらに、「バケット構成」はコンパイル時に認識されるため (C++ テンプレート voodoo を介して実装されます)、適切なバケットはコンパイル時に決定されます。
これまでのところ、すべてがうまく機能しています。
最近、私はヒープ操作を頻繁に実行するアルゴリズムに取り組みましたが、当然ヒープのパフォーマンスに大きく影響されます。プロファイリングにより、そのパフォーマンスがロックによってかなり影響を受けることが明らかになりました。つまり、ヒープ自体は非常に高速に動作します (通常の割り当てには、メモリ参照解除命令がいくつか含まれます)。ただし、アプリケーション全体がマルチスレッドであるため、適切なバケットは、インターロック命令に依存するクリティカル セクションによって保護されます。重い。
私は、クリティカル セクションによって保護されていない独自の専用ヒープをこのアルゴリズムに与えることで、これを修正しました。しかし、これはコード レベルでいくつかの問題/制限を課します。たとえば、ヒープが必要な場合はスタック内の深いところにコンテキスト情報を渡す必要があります。これを回避するために TLS を使用することもできますが、これは私の特定のケースで再入に問題を引き起こす可能性があります。
これは私を不思議に思います:シングルスレッドの使用のためにヒープを最適化するための既知の手法はありますか?
編集:
Google の tcmalloc をチェックすることを提案してくれた @Voo に感謝します。
多かれ少なかれ(少なくとも小さなオブジェクトの場合)行ったことと同様に機能するようです。さらに、スレッドごとのキャッシュを維持することで、私が抱えている問題を正確に解決します。
私もこの方向で考えましたが、スレッドごとのヒープを維持することを考えました。次に、別のスレッドに属するヒープから割り当てられたメモリ ブロックを解放するのは、やや注意が必要です。ロックされたキューのようなものにメモリ ブロックを挿入し、他のスレッドに通知して、保留中の割り当てを非同期的に解放する必要があります。非同期の割り当て解除は問題を引き起こす可能性があります。そのスレッドが何らかの理由 (たとえば、積極的な計算を実行するなど) でビジー状態の場合、メモリの割り当て解除は実際には発生しません。さらに、マルチスレッドのシナリオでは、割り当て解除のコストが大幅に高くなります。
OTOH キャッシングを使用したアイデアは、はるかに単純で効率的です。私はそれを解決しようとします。
どうもありがとう。
PS:
確かに、Google の tcmalloc は素晴らしいです。私が行ったものとほぼ同じように実装されていると思います(少なくとも固定サイズの部分)。
しかし、詳しく言うと、私のヒープが優れている点が 1 つあります。ドキュメントによると、tcmalloc は (漸近的に) 約 1% のオーバーヘッドを課しますが、私のオーバーヘッドは 0.0061% です。正確には4/64Kです。
:)