1

サイズ 400000000 の大きな Long Array をファイルに書き込んで読み込もうとしています。私が使用しているコードは次のとおりです。

import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.io.RandomAccessFile ;
import java.util.* ;

class Checks {
public static FileChannel channel;
public static MappedByteBuffer mbb;

public static void main(String[] args){
    try{


      long k[] = new long[400000000] ;
          for(int i = 0 ; i < 400000000 ; i++){
            k[i] = i ;
          }

    channel = new RandomAccessFile("abc.dat", "rw").getChannel();
        mbb = channel.map(FileChannel.MapMode.READ_WRITE, 0, 1 << 24);
        mbb.order(ByteOrder.nativeOrder());

          for(int i = 0 ; i < 400000000 ;i++ ){
             getMbb().putLong(k[i]);
        }

        channel.close();

       long ks[] = new long[400000000] ;

        channel = new RandomAccessFile("abc.dat", "rw").getChannel();
        mbb = channel.map(FileChannel.MapMode.READ_WRITE, 0, 1 << 24);
        mbb.order(ByteOrder.nativeOrder());

        for(int r = 0 ; r < 400000000; r++){
            ks[r] = getMbb().getLong();
         }

             for(int r = 0 ; r < 400000000; r++){
                 if(k[r] != ks[r]){
                  System.out.println("Error at " + r);
                  break ;
                  }
              }


}
    catch(Exception e)
    {
        e.printStackTrace();
    }

   }

    public static ByteBuffer getMbb() throws IOException {
        if (mbb.remaining() <= 0) {
            mbb = channel.map(FileChannel.MapMode.READ_WRITE, channel.size(), 1 << 24);
            mbb.order(ByteOrder.nativeOrder());
        }
        return mbb;
    }
}

ただし、このコードは、書き込み配列と読み取り配列が同じではないというエラーを出しています。なぜこれが起こっているのですか?

4

4 に答える 4

2

チャネルを閉じる前に、マップされたバッファに加えられた変更をディスクに強制してみてください:

 mbb.force();

このように、ローカル デバイスに保存されているファイルを使用している場合、マップされたバッファーに加えられたすべての変更がディスクに反映されることが保証されます。

ファイルに追加するだけであることを考えると、目的を達成する別の方法は次のとおりです。

  channel = new RandomAccessFile("abc.dat", "rw").getChannel();

  ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE/8 * 1001);
  for(int i = 0 ; i < 400000000 ;i++ ){
     //write a number of byte to a ByteBuffer
      buffer.putLong(k[i]);
      if(i % 1000 == 0){
          channel.write(buffer);
          buffer.clear();
      }
   }
   channel.write(buffer);
   buffer.clear();

読むために:

    buffer.clear();
    int bytesCnt;
    while((bytesCnt = channel.read(buffer))!=-1){
    for(int r = 0 ; r < bytesCnt; r++){
        ks[r] = buffer.getLong();
     }
    }

パフォーマンスについて: I/O を実行するアプリケーションでは、シーク数が原因でパフォーマンスが低下します。したがって、シーク回数が少ないほど、パフォーマンスが高くなります。これは、可能な限り多くのデータを連続的に書き込むことと同じです(<=> ディスクへのフラッシュの数が少なくなります。フラッシュとは、実際にはシークを意味します) 。

あなたのシナリオでは、データはシーケンシャルな方法 (ファイルへの追加) でのみ書き込まれるため、心配する唯一の側面はディスクへのフラッシュの数です。この数は、バッファのサイズに反比例します。したがって、バッファのサイズをできるだけ大きくするようにしてください。

于 2012-08-15T20:31:24.727 に答える
2

これが私が提案するものです:

public static void main(String[] args) {

  File f = new File("abc.txt");

  DataOutputStream s = new DataOutputStream( new FileOutputStream(f));

  for ( int i = 0; i < 400000000; i++ ) {
    s.writeLong(i);
  }
  s.close();

  DataInputStream is = new DataInputStream( new FileInputStream(f));
  for (int i = 0; i < 400000000; i++ ) {
    if ( i != is.readLong() ) System.out.println("Error at " + i);
  }
}

これは、プログラムが行うすべてのことを行いますが、メモリを明示的に割り当てることなく、確実に、メモリ マップされたバッファを介してその割り当てを複製することはありません。この解決策が実際にあなたが本当にやりたいことに当てはまるかどうかはわかりません。

于 2012-08-15T21:21:17.580 に答える
1

あなたのgetMbb()方法は壊れていると思います。メモリ内のファイルのチャンクを再マップするたびに、channel.size(). これは、ファイルの作成中にのみ機能しますが、読み取り中は機能しません。ファイルを読み取るときは、ファイルの末尾のに来る、ランダムな内容を持つファイルの「領域」をマップします。

ファイル内の現在の場所を追跡するには、再マッピング コードを修正する必要があります。

于 2012-08-15T21:53:41.157 に答える
1

RandomAccessFile の代わりにDataInputStreamDataOutputStreamを使用するようにコードを少し変更しましたが、魅力的に機能します。

    try {
        long k[] = new long[400000000];
        for (int i = 0; i < k.length; i++) {
            k[i] = i;
        }

        DataOutputStream dos = new DataOutputStream(new FileOutputStream("abc.dat"));
        for (int i = 0; i < k.length; i++) {
            dos.writeLong(k[i]);
        }

        long ks[] = new long[k.length];

        DataInputStream dis = new DataInputStream(new FileInputStream("abc.dat"));

        for (int r = 0; r < ks.length; r++) {
            ks[r] = dis.readLong();
        }

        for (int r = 0; r < k.length; r++) {
            if (k[r] != ks[r]) {
                System.out.println("Error at " + r);
                break;
            }
        }
    } catch(Exception e) {
    } finally {
        // Make sure to close the streams
        dos.close();
        dis.close();
    }
于 2012-08-15T21:34:48.093 に答える