ファイルにデータを書き込むにはどうすれば本当にをJavaでブロックデバイスとフラッシュ/同期させることができますか?Javaで本当にファイルの同期/フラッシュを実行する
IはNIOを使用してこのコードを試してみました:同期()I)はs.getFD(とtogehterそのc.force(true)を想定
FileOutputStream s = new FileOutputStream(filename)
Channel c = s.getChannel()
while(xyz)
c.write(buffer)
c.force(true)
s.getFD().sync()
c.close()
force状態
ためなぜならDOC十分であるべきです。このチャネルのファイルへの更新を、そのチャネルのファイルを含むストレージデバイスに強制的に書き込みます。 このチャネルのファイルがローカルストレージデバイス上にある場合、このメソッドが返ってくると、このチャネルが作成されてからファイルに加えられたすべての変更が最後に呼び出されてから、そのデバイスに書き込まれます。これは、システムクラッシュ時に重要な情報が失われないようにするのに役立ちます。syncの状態:すべてのシステム・バッファドキュメント
フォースは、基本となるデバイスと同期します。このメソッドは、このFileDescriptorのすべての変更されたデータと属性が関連するデバイスに書き込まれた後に戻ります。特に、このFileDescriptorがファイルシステム内のファイルなどの物理記憶媒体を参照する場合、このFileDesecriptorに関連付けられたバッファ内のメモリ内で変更されたすべてのコピーが物理メディアに書き込まれるまで、syncは返されません。 syncは、物理的な格納(ファイルなど)が既知の状態にあることを必要とするコードで使用されることを意味します。
これらの2つの呼び出しで十分です。それは...ですか?彼らはそうではないと思います。
背景C/Javaを使用した小さなパフォーマンス比較(2GB、シーケンシャル書き込み)を行い、JavaバージョンはCバージョンの2倍の速さで、おそらくハードウェアよりも高速です(1台のHD上で120MB /秒)。私はRuntime.getRuntime()。exec( "sync")を使ってコマンドラインツールの同期を実行しようとしましたが、動作を変更していません。
70メガバイト/秒で得られたCコードは、()(書き込み、オープン近い低レベルAPIを使用すると、あまり変化しない)である:
FILE* fp = fopen(filename, "w");
while(xyz) {
fwrite(buffer, 1, BLOCK_SIZE, fp);
}
fflush(fp);
fclose(fp);
sync();
同期する最終的なコールがありません。私は非現実的な値を持っています(1 GBを超える主メモリパフォーマンス)。
CとJavaの間に大きな違いがあるのはなぜですか? 2つの可能性があります。私はJavaでデータを正しく同期しません。何らかの理由でCコードが最適ではありません。
更新: "strace -cfT cmd"でstraceを実行しました。
C(低レベルAPI): MB/sの67.389782
% time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 87.21 0.200012 200012 1 fdatasync 11.05 0.025345 1 32772 write 1.74 0.004000 4000 1 sync
C(高レベルAPI):ここでの結果です MB/sの61.796458
% time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 73.19 0.144009 144009 1 sync 26.81 0.052739 1 65539 write
のJava (1.6 SUN JRE、API java.io): MB/s 1286755466197537
% time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 80.07 105.387609 3215 32776 write 2.58 3.390060 3201 1059 read 0.62 0.815251 815251 1 fsync
のJava(1.6 SUN JRE、java.nioのAPI): MB/sの127.45830221558376
5.52 0.980061 490031 2 fsync 1.60 0.284752 9 32774 write 0.00 0.000000 0 80 close
時間値は、システムの時間であるように見えるので、かなり無意味です。
更新2: 別のサーバーに切り替えて再起動し、新しくフォーマットされたext3を使用します。今はJavaとCの違いがわずか4%です。どういうことが間違っているのか分かりません。時には物事が奇妙な場合もあります。私はこの質問を書く前に別のシステムで測定を試みたはずです。ごめんなさい。答えを要約する :
アップデート3。s.getFD(続く
- 使用c.force(真の))は、Java NIOとs.flushの同期()()とs.getFD ().sync()をJavaのストリームAPIに使用します。 CのハイレベルAPIについては、同期することを忘れないでください。 fflushがデータをOSに提出しましたが、データをブロックデバイスに持ち込むことはありません。
- コマンドで実行されたシステムコールを分析するにはstraceを使用してください。
- 質問を投稿する前に結果をクロスチェックしてください。
更新4: 以下のフォローアップ3210に注意してください。
私は実際にセクション2の関数を使用してスループットを見たいと思います。 –
BLOCK_SIZEには何を使用していますか? Javaのバッファと同じサイズですか?最近では512が非常に最適ではないようです。おそらく、少なくとも4096(x86ではページサイズ)以上、あるいはそれ以上のものが必要でしょう。私はいくつかのマシンで32kまで測定可能な改善を見ました。もちろん、バッファがページアライメントされていると、カーネルに最適化の余地が広がります。 – aij
もう1つの問題は、投稿したコードが「低レベルのAPI(公開、書き込み、終了)」を使用していないことです。それは、より高いレベルの移植可能なstdio API(fopen、fwrite、fclose)を使用しています。これは、デフォルトで追加のバッファリング層を追加します。投稿したコードの外で明示的にバッファリングをオフにしましたか? – aij