3

何百万ものエントリを含む2つの配列(intとlong)があります。これまで、DataOutputStreamを使用し、長いバッファーを使用して実行しているため、ディスクI / Oコストが低くなります(nioも、巨大なバッファーがあるのとほぼ同じであるため、I / Oアクセスコストが低くなります)。

DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("abc.txt"),1024*1024*100));

for(int i = 0 ; i < 220000000 ; i++){
    long l = longarray[i];
    dos.writeLong(l);
}

しかし、それを行うには数秒(5分以上)かかります。実際、私が一括フラッシュしたいもの(ある種のメインメモリからディスクメモリへのマップ)。そのために、私はここここで素晴らしいアプローチを見つけました。しかし、私のjavacでそれを使用する方法を理解することはできません。誰かがそれについて、またはそれをうまく行うための他の方法について私を助けることができますか?

4

2 に答える 2

2

私のマシンでは、SSDを搭載した3.8 GHzi7

DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("abc.txt"), 32 * 1024));

long start = System.nanoTime();
final int count = 220000000;
for (int i = 0; i < count; i++) {
    long l = i;
    dos.writeLong(l);
}
dos.close();
long time = System.nanoTime() - start;
System.out.printf("Took %.3f seconds to write %,d longs%n",
        time / 1e9, count);

プリント

Took 11.706 seconds to write 220,000,000 longs

メモリマップトファイルの使用

final int count = 220000000;

final FileChannel channel = new RandomAccessFile("abc.txt", "rw").getChannel();
MappedByteBuffer mbb = channel.map(FileChannel.MapMode.READ_WRITE, 0, count * 8);
mbb.order(ByteOrder.nativeOrder());

long start = System.nanoTime();
for (int i = 0; i < count; i++) {
    long l = i;
    mbb.putLong(l);
}
channel.close();
long time = System.nanoTime() - start;
System.out.printf("Took %.3f seconds to write %,d longs%n",
        time / 1e9, count);

// Only works on Sun/HotSpot/OpenJDK to deallocate buffer.
((DirectBuffer) mbb).cleaner().clean();

final FileChannel channel2 = new RandomAccessFile("abc.txt", "r").getChannel();
MappedByteBuffer mbb2 = channel2.map(FileChannel.MapMode.READ_ONLY, 0, channel2.size());
mbb2.order(ByteOrder.nativeOrder());
assert mbb2.remaining() == count * 8;
long start2 = System.nanoTime();
for (int i = 0; i < count; i++) {
    long l = mbb2.getLong();
    if (i != l)
        throw new AssertionError("Expected "+i+" but got "+l);
}
channel.close();
long time2 = System.nanoTime() - start2;
System.out.printf("Took %.3f seconds to read %,d longs%n",
        time2 / 1e9, count);

// Only works on Sun/HotSpot/OpenJDK to deallocate buffer.
((DirectBuffer) mbb2).cleaner().clean();

3.8GHzi7で印刷します。

Took 0.568 seconds to write 220,000,000 longs

遅いマシンでの印刷

Took 1.180 seconds to write 220,000,000 longs
Took 0.990 seconds to read 220,000,000 longs

これを作成しない他の方法はありますか?そのアレイはすでにメインメモリにあり、500 MBを超える容量を割り当てることができないためですか?

これは、1KB未満のヒープを使用しません。この呼び出しの前後に使用されているメモリの量を見ると、通常はまったく増加していません。

もう1つ、これにより効率的な読み込みが可能になり、MappedByteBufferも意味しますか?

私の経験では、システムコールとメモリへのコピーの数を減らすため、メモリマップトファイルを使用するのがはるかに高速です。

なぜなら、いくつかの記事でread(buffer)を見つけたので、これにより読み込みパフォーマンスが向上します。(私は、1つ、本当に速い2億2000万のint配列-float配列が5秒を読み取ることを確認します)

私はそれを見たことがないので、その記事を読みたいと思います。

別の問題:コード出力ファイルからの読み取り中にreadLongでエラーが発生する

証明のパフォーマンスの一部は、ネイティブバイトオーダーで値を格納することです。writeLong / readLongは常にビッグエンディアン形式を使用しますが、ネイティブのリトルエンディアン形式であるIntel/AMDシステムでははるかに低速です。

バイトオーダーをビッグエンディアンにして速度を落とすか、ネイティブオーダーを使用できます(DataInput / OutputStreamはビッグエンディアンのみをサポートします)

于 2012-04-12T18:47:26.357 に答える
1

私はそれを2.13GhZの16GBメモリでサーバーを実行しています[CPU]

問題がJavaコードに関係しているとは思えません。

ファイルシステムが非常に遅いように見えます(ローカルディスクから予想されるよりも少なくとも10倍遅い)。

私は2つのことをします:

  1. ネットワーク共有ではなく、実際にローカルディスクに書き込んでいることを再確認してください。一部の環境では、ホームディレクトリがNFSマウントであることに注意してください。
  2. システム管理者にマシンを調べて、ディスクが非常に遅い理由を確認するように依頼してください。私が彼らの立場にある場合は、ログを確認し、いくつかのベンチマークを実行することから始めます(たとえば、Bonnie ++を使用)。
于 2012-04-12T16:46:13.553 に答える