论坛首页 Java企业应用论坛

高效的RandomAccessFile【续】

浏览 5286 次
该帖已经被评为精华帖
作者 正文
   发表时间:2011-08-16  

优化BufferedRandomAccessFile。

 

优化原则:

  •     调用频繁的语句最需要优化,且优化的效果最明显。
  •     多重嵌套逻辑判断时,最可能出现的判断,应放在最外层。
  •     减少不必要的NEW。


这里举一典型的例子:

 

   public void seek(long pos) throws IOException {
		...
         this.bufstartpos =  pos * bufbitlen / bufbitlen; // bufbitlen指buf[]的位长,例:若bufsize=1024,则bufbitlen=10。
                ...
  }
 

seek函数使用在各函数中,调用非常频繁,上面加重的这行语句根据pos和bufsize确定buf[]对应当前文件的映射位置,用"*"、"/"确定,显然不是一个好方法。

 

  • 优化一:this.bufstartpos = (pos << bufbitlen) >> bufbitlen;
  • 优化二:this.bufstartpos = pos & bufmask; // this.bufmask = ~((long)this.bufsize - 1);

两者效率都比原来好,但后者显然更好,因为前者需要两次移位运算、后者只需一次逻辑与运算(bufmask可以预先得出)。

至此优化基本实现,逐字节COPY一个12兆的文件,(这里牵涉到读和写,结合缓冲读,用优化后BufferedRandomAccessFile试一下读/写的速度):

 

耗用时间(秒)
RandomAccessFile RandomAccessFile 95.848
BufferedInputStream + DataInputStream BufferedOutputStream + DataOutputStream 2.935
BufferedRandomAccessFile BufferedOutputStream + DataOutputStream 2.813
BufferedRandomAccessFile BufferedRandomAccessFile 2.453
BufferedRandomAccessFile优 BufferedRandomAccessFile优 2.197

 

可见优化尽管不明显,还是比未优化前快了一些,也许这种效果在老式机上会更明显。

以上比较的是顺序存取,即使是随机存取,在绝大多数情况下也不止一个BYTE,所以缓冲机制依然有效。而一般的顺序存取类要实现随机存取就不怎么容易了。


需要完善的地方

 

提供文件追加功能:

 

public boolean append(byte bw) throws IOException {
   return this.write(bw, this.fileendpos + 1);
}

 

提供文件当前位置修改功能:

 

public boolean write(byte bw) throws IOException {
   return this.write(bw, this.curpos);
}

 

返回文件长度(由于BUF读写的原因,与原来的RandomAccessFile类有所不同):

 

public long length() throws IOException {
   return this.max(this.fileendpos + 1, this.initfilelen);
}

 

返回文件当前指针(由于是通过BUF读写的原因,与原来的RandomAccessFile类有所不同):

 

public long getFilePointer() throws IOException {
   return this.curpos;
}

 

提供对当前位置的多个字节的缓冲写功能:

 

public void write(byte b[], int off, int len) throws IOException {
        long writeendpos = this.curpos + len - 1;
        if (writeendpos <= this.bufendpos) { // b[] in cur buf
            System.arraycopy(b, off, this.buf, (int)(this.curpos - this.bufstartpos), len);
            this.bufdirty = true;
            this.bufusedsize = (int)(writeendpos - this.bufstartpos + 1);
        } else { // b[] not in cur buf
            super.seek(this.curpos);
            super.write(b, off, len);
        }
        if (writeendpos > this.fileendpos)
            this.fileendpos = writeendpos;
        this.seek(writeendpos+1);
}
public void write(byte b[]) throws IOException {
        this.write(b, 0, b.length);
}

 

提供对当前位置的多个字节的缓冲读功能:

 

public int read(byte b[], int off, int len) throws IOException {
    long readendpos = this.curpos + len - 1;
    if (readendpos <= this.bufendpos && readendpos <= this.fileendpos ) { // read in buf
     	System.arraycopy(this.buf, (int)(this.curpos - this.bufstartpos), b, off, len);
    } else { // read b[] size > buf[]
   	if (readendpos > this.fileendpos) { // read b[] part in file
      	len = (int)(this.length() - this.curpos + 1);
    }
       super.seek(this.curpos);
       len = super.read(b, off, len);
       readendpos = this.curpos + len - 1;
   }
       this.seek(readendpos + 1);
       return len;
}
public int read(byte b[]) throws IOException {
   return this.read(b, 0, b.length);
}
public void setLength(long newLength) throws IOException {
   if (newLength > 0) {
       this.fileendpos = newLength - 1;
   } else {
       this.fileendpos = 0;
   }
   super.setLength(newLength);
}
    
public void close() throws IOException {
   this.flushbuf();
   super.close();
}
 

至此完善工作基本完成,试一下新增的多字节读/写功能,通过同时读/写1024个字节,来COPY一个12兆的文件,(这里牵涉到读和写,用完善后BufferedRandomAccessFile试一下读/写的速度):

 

耗用时间(秒)
RandomAccessFile RandomAccessFile 95.848
BufferedInputStream + DataInputStream BufferedOutputStream + DataOutputStream 2.935
BufferedRandomAccessFile BufferedOutputStream + DataOutputStream 2.813
BufferedRandomAccessFile BufferedRandomAccessFile 2.453
BufferedRandomAccessFile优 BufferedRandomAccessFile优 2.197
BufferedRandomAccessFile完 BufferedRandomAccessFile完 0.401


与MappedByteBuffer+RandomAccessFile的对比?

 

JDK1.4+提供了NIO类 ,其中MappedByteBuffer类用于映射缓冲,也可以映射随机文件访问,可见JAVA设计者也看到了RandomAccessFile的问题, 并加以改进。怎么通过MappedByteBuffer+RandomAccessFile拷贝文件呢?下面就是测试程序的主要部分:

 

RandomAccessFile rafi = new RandomAccessFile(SrcFile, "r");
RandomAccessFile rafo = new RandomAccessFile(DesFile, "rw");
FileChannel fci = rafi.getChannel();
FileChannel fco = rafo.getChannel();
long size = fci.size();
MappedByteBuffer mbbi = fci.map(FileChannel.MapMode.READ_ONLY, 0, size);
MappedByteBuffer mbbo = fco.map(FileChannel.MapMode.READ_WRITE, 0, size);
long start = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
    byte b = mbbi.get(i);
    mbbo.put(i, b);
}
fcin.close();
fcout.close();
rafi.close();
rafo.close();
System.out.println("Spend: "+(double)(System.currentTimeMillis()-start) / 1000 + "s");
 

试一下JDK1.4的映射缓冲读/写功能,逐字节COPY一个12兆的文件,(这里牵涉到读和写):

 

耗用时间(秒)
RandomAccessFile RandomAccessFile 95.848
BufferedInputStream + DataInputStream BufferedOutputStream + DataOutputStream 2.935
BufferedRandomAccessFile BufferedOutputStream + DataOutputStream 2.813
BufferedRandomAccessFile BufferedRandomAccessFile 2.453
BufferedRandomAccessFile优 BufferedRandomAccessFile优 2.197
BufferedRandomAccessFile完 BufferedRandomAccessFile完 0.401
MappedByteBuffer+ RandomAccessFile MappedByteBuffer+ RandomAccessFile 1.209

 

确实不错,看来NIO有了极大的进步。建议采用 MappedByteBuffer+RandomAccessFile的方式。

   发表时间:2011-09-18  
优化一:this.bufstartpos = (pos << bufbitlen) >> bufbitlen;

这个是为什么?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics