在读写文件时的表现,最好的是什么?序列化X Java.nio

时间:2012-03-14 22:16:43

标签: java performance serialization

我有一个带有1个int和4个双精度的对象。

我比较了使用序列化和FileChannel对象在文件中写入500万个这些对象的性能。

在序列化中使用以下方法读取和写入文件。

    public void print() throws IOException, ClassNotFoundException{     
    ObjectInputStream input = new ObjectInputStream(new FileInputStream(this.filePath) );                   
    try {           
        while(true) {               
            this.sb = (Sbit) input.readObject();
            //System.out.println(this.sb.toString());
        } 
    }
    catch ( EOFException eofException ) {
        return; 
    } 
    catch (IOException ioException) {
        System.exit( 1 );
    }
    finally {
        if( input != null )
            input.close();
    } 

}   

public void build() throws IOException {        
    ObjectOutputStream output = new ObjectOutputStream( new FileOutputStream(this.filePath) );
    try {           
        Random random = new Random();
        for (int i = 0; i<5000000; i++) {
            this.sb = new Sbit();
            this.sb.setKey(i);
            this.sb.setXMin( random.nextDouble() );
            this.sb.setXMax( random.nextDouble() );
            this.sb.setYMin( random.nextDouble() );
            this.sb.setYMax( random.nextDouble() );

            output.writeObject(this.sb);
        }           
    } 
    catch (IOException ioException) {
        System.exit( 1 );
    } 
    finally {
        try {
            if( output != null)
                output.close();
        }
        catch ( Exception exception ) {
            exception.printStackTrace();
            System.exit(1);
        }
    }       
} 

使用java.nio时是:

    public void print() throws IOException {    
    FileChannel file = new RandomAccessFile(this.filePath, "rw").getChannel();  
    ByteBuffer[] buffers = new ByteBuffer[5];
    buffers[0] = ByteBuffer.allocate(4);   // 4 bytes to int
    buffers[1] = ByteBuffer.allocate(8);   // 8 bytes to double
    buffers[2] = ByteBuffer.allocate(8);    
    buffers[3] = ByteBuffer.allocate(8);   
    buffers[4] = ByteBuffer.allocate(8);   

    while (true) {
        if(file.read(buffers[0]) == -1 )       // Read the int, 
            break;                                  // if its EOF exit the loop

        buffers[0].flip();

        this.sb = new Sbit();
        this.sb.setKey(buffers[0].getInt());

        if(file.read(buffers[1]) == -1) {   // Read the int primary value
            assert false;                   // Should not get here!
            break;                          // Exit loop on EOF
        }
        buffers[1].flip();

        this.sb.setXMin( buffers[1].getDouble() );

        if(file.read(buffers[2]) == -1) {   
            assert false;                   
            break;                          
        }
        buffers[2].flip();

        this.sb.setXMax( buffers[2].getDouble() );

        if(file.read(buffers[3]) == -1) {   
            assert false;                   
            break;                          
        }
        buffers[3].flip();

        this.sb.setYMin( buffers[3].getDouble() );
        if(file.read(buffers[4]) == -1) {   
            assert false;                   
            break;                          
        }
        buffers[4].flip();

        this.sb.setYMax( buffers[4].getDouble() );

        for(int i = 0; i < 5; i++)
            buffers[i].clear();

    } 

} 


public void build() throws IOException {    
    FileChannel file = new RandomAccessFile(this.filePath, "rw").getChannel();      

    Random random = new Random();
    for (int i = 0; i<5000000; i++) {
        this.sb = new Sbit();
        this.sb.setKey(i);
        this.sb.setXMin( random.nextDouble() );
        this.sb.setXMax( random.nextDouble() );
        this.sb.setYMin( random.nextDouble() );
        this.sb.setYMax( random.nextDouble() );

        ByteBuffer[] buffers = new ByteBuffer[5];
        buffers[0] = ByteBuffer.allocate(4);   // 4 bytes to into
        buffers[1] = ByteBuffer.allocate(8);   // 8 bytes to double
        buffers[2] = ByteBuffer.allocate(8);   
        buffers[3] = ByteBuffer.allocate(8);   
        buffers[4] = ByteBuffer.allocate(8);   

        buffers[0].putInt(this.sb.getKey()).flip(); 
        buffers[1].putDouble(this.sb.getXMin()).flip();
        buffers[2].putDouble(this.sb.getXMax()).flip();
        buffers[3].putDouble(this.sb.getYMin()).flip();
        buffers[4].putDouble(this.sb.getYMax()).flip();
        try {
            file.write(buffers);
        } 
        catch (IOException e)   {
            e.printStackTrace(System.err);
            System.exit(1);
        } 

        for(int x = 0; x < 5; x++)
            buffers[x].clear();

    }
}

但是我在java.nio上阅读了很多关于java.nio的内容并尝试使用它,因为它具有更好的性能。但那不是我的情况。

要编写的文件如下(java.nio):

文件大小: 175 MB 时间(以毫秒为单位): 57638

使用序列化:

文件大小: 200 MB 以毫秒为单位的时间: 34504

对于此文件的读取,如下(java.nio):

以毫秒为单位的时间: 78172

使用序列化:

以毫秒为单位的时间: 35288

我在java.nio中做错了吗?我想写与完成相同的二进制文件。还有另一种有效写文件的方法吗?实际上序列化对象是最好的方法吗?

谢谢。

3 个答案:

答案 0 :(得分:2)

您正在创建25,000,000个ByteBuffer对象,每个ByteBuffer最多为8个字节。那非常低效。

只需将一个 ByteBuffer分配给循环外的38个字节( for 语句之前)

在循环内部,您可以使用相同的ByteBuffer,如下所示:

  buffer.clear();

  buffer.putInt(this.sb.getKey()); 
  buffer.putDouble(this.sb.getXMin());
  buffer.putDouble(this.sb.getXMax());
  buffer.putDouble(this.sb.getYMin());
  buffer.putDouble(this.sb.getYMax());

  buffer.flip();

  try
  {
     file.write(buffer);
  }
  catch (IOException ex)
  {
     ex.printStackTrace();
     //etc...
  }

  buffer.flip();

尝试一下,如果您发现任何改进,请告诉我们。

答案 1 :(得分:1)

不要使用多个ByteBuffers,而是声明一个足够大的单字节缓冲区,以容纳您想要放入其中的所有数据。然后像现在一样将数据放入其中。完成后,翻转缓冲区并将其写出。当您准备好重新读取它时,将数据从磁盘读入字节缓冲区,翻转它,然后使用getInt / getDouble读取数据。

答案 2 :(得分:0)

我没有尝试过自行序列化,但使用kryo取得了良好的效果。它比标准Java序列化快得多。