6

次のコードをベンチマークとして使用すると、システムはわずか 1 秒で 10,000 行をディスクに書き込むことができます。

void withSync() {
    int f = open( "/tmp/t8" , O_RDWR | O_CREAT );
    lseek (f, 0, SEEK_SET );
    int records = 10*1000;
    clock_t ustart = clock();
    for(int i = 0; i < records; i++) {
        write(f, "012345678901234567890123456789" , 30);
        fsync(f);
    }
    clock_t uend = clock();
    close (f);
    printf("   sync() seconds:%lf   writes per second:%lf\n", ((double)(uend-ustart))/(CLOCKS_PER_SEC), ((double)records)/((double)(uend-ustart))/(CLOCKS_PER_SEC));
}

上記のコードでは、10,000 レコードを書き込み、ディスクにフラッシュするのを一瞬で行うことができます。出力は以下のとおりです。

sync() seconds:0.006268   writes per second:0.000002

Java 版では、10,000 レコードを書き込むのに 4 秒以上かかります。これは Java の単なる制限ですか、それとも何か不足していますか?

public void testFileChannel() throws IOException {
    RandomAccessFile raf = new RandomAccessFile(new File("/tmp/t5"),"rw");
    FileChannel c = raf.getChannel();
    c.force(true);
    ByteBuffer b = ByteBuffer.allocateDirect(64*1024);
    long s = System.currentTimeMillis();
    for(int i=0;i<10000;i++){            
        b.clear();
        b.put("012345678901234567890123456789".getBytes());
        b.flip();
        c.write(b);
                    c.force(false);
    }
    long e=System.currentTimeMillis();
    raf.close();
    System.out.println("With flush "+(e-s));

}

これを返します:

With flush 4263

Javaでレコードをディスクに書き込む正しい/最速の方法を理解するのを手伝ってください。

注:最終的には、このファイルに対するランダムな読み取り/書き込みアクセスが必要になるため、このRandomAccessFileクラスを a と組み合わせて使用​​しています。ByteBuffer

4

4 に答える 4

5

実際、テストが遅くないことに驚いています。force の動作は OS に依存しますが、広くはデータを強制的にディスクに書き込みます。SSD を使用している場合、1 秒あたり 40K の書き込みを達成できる可能性がありますが、HDD では達成できません。C の例では、最速の SSD でさえ 235K IOPS を超えるパフォーマンスを発揮できないため、データをディスクにコミットしていないことは明らかです (メーカーは、それよりも高速にならないことを保証しています:D)。

データを毎回ディスクにコミットする必要がある場合は、データが遅くなり、ハードウェアの速度に完全に依存することが予想されます。データを OS にフラッシュするだけで、プログラムがクラッシュしても OS がクラッシュしない場合でも、データを失うことはなく、無理なくデータを書き込むことができます。より高速なオプションは、メモリ マップ ファイルを使用することです。これにより、各レコードのシステム コールなしでランダム アクセスが可能になります。

私はライブラリJava Chronicleを持っています。このライブラリは、ランダム アクセスを使用してテキストまたはバイナリ形式で 80 ns のレイテンシで 1 秒あたり 500 万から 2000 万のレコードを読み書きでき、プロセス間で共有できます。これは、すべてのレコードでデータをディスクにコミットするわけではないため、これほど高速にしか機能しませんが、JVM が任意の時点でクラッシュした場合、クロニクルに書き込まれたデータが失われないことをテストできます。

于 2012-11-09T07:32:16.510 に答える
1

このコードは、あなたが C で書いたものに似ています。私のマシンでは 5 ミリ秒しかかかりません。書き込みごとに本当にフラッシュする必要がある場合は、約 60 ミリ秒かかります。元のコードは、このマシンで約 11 秒かかりました。ところで、出力ストリームを閉じてもフラッシュされます。

public static void testFileOutputStream() throws IOException {
  OutputStream os = new BufferedOutputStream( new FileOutputStream( "/tmp/fos" ) );
  byte[] bytes = "012345678901234567890123456789".getBytes();
  long s = System.nanoTime();
  for ( int i = 0; i < 10000; i++ ) {
    os.write( bytes );
  }
  long e = System.nanoTime();
  os.close();
  System.out.println( "outputstream " + ( e - s ) / 1e6 );
}
于 2012-11-09T08:04:10.170 に答える
0

これはあなたのCバージョンに最も似ていると思います。Java の例のダイレクト バッファは、C バージョンよりも多くのバッファ コピーを引き起こしていると思います。これは、私の(古い)ボックスで約 2.2 秒かかります。

  public static void testFileChannelSimple() throws IOException {
    RandomAccessFile raf = new RandomAccessFile(new File("/tmp/t5"),"rw");
    FileChannel c = raf.getChannel();
    c.force(true);
    byte[] bytes = "012345678901234567890123456789".getBytes();
    long s = System.currentTimeMillis();
    for(int i=0;i<10000;i++){
      raf.write(bytes);
      c.force(true);
    }
    long e=System.currentTimeMillis();
    raf.close();
    System.out.println("With flush "+(e-s));
  }
于 2012-11-12T02:48:50.877 に答える
0

fputsに相当するJavaはfile.write("012345678901234567890123456789");、4つの関数を呼び出しており、Cでは1つだけです。遅延は明らかです

于 2012-11-09T06:08:42.563 に答える