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