9

私は現在、JOGL(Java OpenGLバインディング)を使用して3Dグラフィックスアプリケーションを開発しています。簡単に言えば、私は巨大なランドスケープバイナリファイルを持っています。サイズが大きいため、実行時に地形チャンクをストリーミングする必要があります。したがって、ランダムアクセスの懸念が明確にわかります。私はすでに最初の(そして汚い:))実装を完了しました(おそらくそれはマルチスレッドです)、そこで私は愚かなアプローチを使用しています...これがその初期化です:

dataInputStream = new DataInputStream(new BufferedInputStream(fileInputStream,4 * 1024);
dataInputStream.mark(dataInputStream.available());

そして、特別なチャンクを読み取る(ストリーミングする)必要がある場合(ファイル内の「オフセット」はすでにわかっています)、次のことを実行しています(恥ずかしいです:)):

dataInputStream.reset();
dataInputStream.skipBytes(offset);
dataInputStream.read(whatever I need...);

私はほとんど経験がなかったので、最初に考えることができました:)それで、これまでに3つの有用で非常に興味深い記事を読みました(おそらくこのトピックに興味があるなら、それらを読むことをお勧めします)

  1. バイトバッファと非ヒープメモリ-グレゴリー氏はJavaNIOに精通しているようです。

  2. Javaのヒント:ファイルをすばやく読み取る方法[http://nadeausoftware.com/articles/2008/02/java_tip_how_read_files_quickly]-これは興味深いベンチマークです。

  3. 記事:Java I /Oパフォーマンスの調整[http://java.sun.com/developer/technicalArticles/Programming/PerfTuning/]-Sunの簡単な推奨事項ですが、下にスクロールして、そこにある「ランダムアクセス」セクションを参照してください。これらは、自己バッファリングが改善されたRandomAccessFile(RAF)の単純な実装を示しています。

グレゴリー氏は、彼の記事の最後にいくつかの*.javaファイルを提供しています。それらの1つは、FileChannel + ByteBuffer + Mapping(FBM)とRAFの間のベンチマークです。彼は、RAFと比較してFBMを使用すると4倍のスピードアップに気づいたと言います。このベンチマークは、次の条件で実行しました。

  1. オフセット(アクセス場所など)はランダムに生成されます(ファイルスコープでは、0-file.length()など)。
  2. ファイルサイズは220MBです。
  3. 1 000 000アクセス(75%の読み取りと25%の書き込み)

結果は驚くべきものでした:

RAFの場合は約28秒! FBMの場合は約0.2秒!

ただし、このベンチマークでの彼のRAFの実装には自己バッファリングがありません(3番目の記事で自己バッファリングについて説明しています)。したがって、パフォーマンスを大幅に低下させるのは「RandomAccessFile.seek」メソッド呼び出しだと思います。

さて、私が学んだすべてのことの後に、1つの質問と1つのジレンマがあります:)

質問:「FileChannel.map」を使用してファイルをマッピングする場合、Javaはファイルの内容全体をMappedByteBufferにコピーしますか?それともそれをエミュレートするだけですか?コピーする場合、FBMアプローチを使用することは私の状況には適していませんか?

ジレンマ:質問に対するあなたの答えに依存します...

  1. マッピングがファイルをコピーする場合、2つの解決策しか考えられないようです:RAF +セルフバッファリング(3番目の記事からのもの)またはFileChannelの位置を利用する(マッピングではない)...どちらになりますか良くなる?

  2. マッピングでファイルがコピーされない場合は、3つのオプションがあります。前の2つのオプションとFBM自体です。

編集:ここにもう1つの質問があります。ここにいる人の中には、マッピングはファイルをMappedByteBufferにコピーしないと言う人もいます。では、なぜ1GBのファイルをマップできないのですか、「マップに失敗しました」というメッセージが表示されます...

PSインターネットでこのトピックに関する一貫した情報を見つけることができないので、アドバイス付きの充実した回答を受け取りたいと思います。

ありがとう :)

4

3 に答える 3

3

いいえ、データはバッファリングされません。MappedByteBufferは、ポインターを使用してデータを参照します。つまり、データはコピーされず、物理メモリにマッピングされるだけです。まだ行っていない場合は、 APIドキュメントを参照してください。

メモリマップトファイルは、ファイルまたはファイルのようなリソースの一部との直接的なバイト単位の相関関係が割り当てられた仮想メモリのセグメントです。このリソースは通常、ディスク上に物理的に存在するファイルですが、デバイス、共有メモリオブジェクト、またはオペレーティングシステムがファイル記述子を介して参照できるその他のリソースの場合もあります。存在すると、ファイルとメモリスペースの間のこの相関関係により、アプリケーションはマップされた部分をプライマリメモリであるかのように扱うことができます。

出典:ウィキペディア

データを頻繁に読み取る場合は、少なくともその一部をキャッシュすることをお勧めします。

于 2011-01-18T21:15:01.000 に答える
2

220 MBのファイルの場合、すべてを仮想メモリにメモリマップします。その理由FBMは非常に高速で、実際にはデータをメモリに読み込まず、単にデータを使用可能にするだけです。

注:テストを実行するときは、同じように比較する必要があります。つまり、ファイルがOSキャッシュにある場合は、どのように実行してもはるかに高速になります。再現可能な結果を​​得るには、テストを複数回繰り返す必要があります。

于 2011-01-18T20:35:27.823 に答える
1

プログラムを実行してから閉じてから再度実行すると、2回目よりもはるかに速く起動することに気づきましたか?これは、OSが最初の実行でアクセスされたファイルの一部をキャッシュしており、それらのディスクにアクセスする必要がないために発生します。ファイルのメモリマッピングは、基本的にプログラムがこれらのバッファにアクセスできるようにするため、ファイルの読み取り時に作成されるコピーを最小限に抑えます。ファイルをメモリマッピングしても、ファイル全体がメモリに読み込まれるわけではないことに注意してください。読み取ったビットとピースは、オンデマンドでディスクから読み取られます。OSがメモリ不足であると判断した場合、マップされたファイルの一部をメモリから解放し、それらをディスクに残すことを決定する場合があります。

編集:あなたが望むのはFileInputStream.getChannel().map()、それをに適応させInputStream、それをに接続することDataInputStreamです。

于 2011-01-19T22:02:43.527 に答える