私はプロジェクトのメモリマップトファイルを調査してきましたが、以前にそれらを使用したことがあるか、使用しないことに決めた人からの考えをいただければ幸いです。その理由は何ですか。
特に、重要度の高い順に、次の点について懸念しています。
- 並行性
- ランダムアクセス
- パフォーマンス
- 使いやすさ
- 移植性
私はプロジェクトのメモリマップトファイルを調査してきましたが、以前にそれらを使用したことがあるか、使用しないことに決めた人からの考えをいただければ幸いです。その理由は何ですか。
特に、重要度の高い順に、次の点について懸念しています。
利点は、ファイルを読み取る従来の方法よりも必要なデータのコピーの量を削減できることだと思います。
アプリケーションがメモリ マップト ファイル内のデータを "その場で" 使用できる場合、そのデータはコピーせずに取り込むことができます。システム コール (Linux の pread() など) を使用する場合は、通常、カーネルが独自のバッファーからユーザー空間にデータをコピーする必要があります。この余分なコピーには時間がかかるだけでなく、データのこの余分なコピーにアクセスすることで、CPU のキャッシュの有効性が低下します。
データを実際にディスクから読み取る必要がある場合 (物理 I/O のように)、OS は引き続きそれらを読み取る必要があります。ページ フォールトはおそらくシステム コールよりもパフォーマンスの面で優れているわけではありませんが、そうしないと (つまり、既に OS キャッシュにある場合)、理論上はパフォーマンスが大幅に向上するはずです。
欠点は、メモリ マップ ファイルへの非同期インターフェイスがないことです。マップされていないページにアクセスしようとすると、ページ フォールトが生成され、スレッドが I/O を待機するようになります。
メモリ マップ ファイルの明らかな欠点は、32 ビット OS にあることです。アドレス空間が簡単に不足する可能性があります。
ユーザーが入力しているときに、メモリマップファイルを使用して「オートコンプリート」機能を実装しました。1つのインデックスファイルに100万をはるかに超える製品部品番号が保存されています。ファイルにはいくつかの典型的なヘッダー情報がありますが、ファイルの大部分はキーフィールドでソートされた固定サイズのレコードの巨大な配列です。
実行時に、ファイルはメモリマップされ、C
スタイルstruct
配列にキャストされます。バイナリ検索を実行して、ユーザーが入力したときに一致する部品番号を見つけます。ファイルの数メモリページのみが実際にディスクから読み取られます。バイナリ検索中にヒットしたページはどれでもかまいません。
メモリマップトファイルは、読み取り/書き込みアクセスを置き換えるため、または同時共有をサポートするために使用できます。それらを一方のメカニズムに使用すると、もう一方のメカニズムも取得します。
ファイルを探したり、書き込んだり、読み取ったりするのではなく、ファイルをメモリにマップして、期待する場所にアクセスするだけです。
これは非常に便利であり、仮想メモリインターフェイスによってはパフォーマンスを向上させることができます。オペレーティングシステムがこの以前の「ファイルI/O」を他のすべてのプログラムによるメモリアクセスとともに管理できるようになり、(理論的には)すでに使用しているページングアルゴリズムなどをサポートできるため、パフォーマンスが向上する可能性があります。プログラムの残りの部分の仮想メモリ。ただし、基盤となる仮想メモリシステムの品質によって異なります。私が聞いた逸話では、Solarisおよび* BSD仮想メモリシステムはLinuxのVMシステムよりも優れたパフォーマンスの向上を示す可能性がありますが、これをバックアップするための経験的なデータはありません。YMMV。
マップされたメモリを介して同じ「ファイル」を使用する複数のプロセスの可能性を検討すると、同時実行性が浮き彫りになります。読み取り/書き込みモデルでは、2つのプロセスがファイルの同じ領域に書き込んだ場合、プロセスのデータの1つがファイルに到着し、他のプロセスのデータを上書きすることはほぼ確実です。あなたはどちらか一方を手に入れるでしょうが、奇妙な混ざり合いはありません。これが標準で義務付けられている動作であるかどうかはわかりませんが、かなり信頼できるものです。(実際には良いフォローアップの質問です!)
対照的に、マップされた世界では、2つのプロセスが両方とも「書き込み」であると想像してください。これを行うには、「メモリストア」を実行します。これにより、O / Sがデータをディスクにページングします(最終的には)。ただし、その間に、重複する書き込みが発生することが予想されます。
これが例です。2つのプロセスが両方ともオフセット1024で8バイトを書き込んでいるとします。プロセス1は「11111111」を書き込んでおり、プロセス2は「22222222」を書き込んでいます。ファイルI/Oを使用している場合、O / Sの奥深くに、1でいっぱいのバッファーと、2でいっぱいのバッファーがあり、どちらもディスク上の同じ場所に向かっていることが想像できます。それらの1つは最初にそこに到達し、もう1つは2番目に到達します。この場合、2番目のものが勝ちます。 ただし、メモリマップトファイルアプローチを使用している場合、プロセス1は4バイトのメモリストアになり、その後に4バイトの別のメモリストアが続きます(これが最大メモリストアサイズではないと仮定します)。プロセス2も同じことをします。プロセスがいつ実行されるかに基づいて、次のいずれかが表示されることが期待できます。
11111111
22222222
11112222
22221111
これに対する解決策は、明示的な相互排除を使用することです。これは、いずれにしてもおそらく良い考えです。とにかく、ファイルの読み取り/書き込みI / Oの場合、「正しいこと」を行うためにO/Sに依存していました。
分類相互排除プリミティブはミューテックスです。メモリマップトファイルの場合、(たとえば)pthread_mutex_init()を使用して利用できるメモリマップトミューテックスを確認することをお勧めします。
1つの落とし穴で編集する:マップされたファイルを使用している場合、ファイル内のデータへのポインターをファイル自体に埋め込みたいという誘惑があります(マップされたファイルに格納されているリンクリストを考えてください)。ファイルは異なる時間に、または異なるプロセスで異なる絶対アドレスにマップされる可能性があるため、これは望ましくありません。代わりに、マップされたファイル内でオフセットを使用してください。
並行性が問題になります。ランダムアクセスの方が簡単ですパフォーマンスは良いものから素晴らしいものまであります。使いやすさ。あまり良くありません。移植性-それほど熱くはありません。
私はずっと前にSunシステムでそれらを使用しました、そしてそれらは私の考えです。