首页 > 解决方案 > FileChannel 关闭太慢。预先分配空间会改善事情吗?

问题描述

我使用 FileChannel 将 4GB 文件写入旋转磁盘,尽管我调整了缓冲区大小以最大化写入速度并每秒刷新一次通道,但文件通道关闭可能需要 200 毫秒。这是足够的时间,我从中读取的队列溢出并开始丢弃数据包。

我使用直接字节缓冲区,但我很难理解这里发生了什么。我有可移动磁盘并且写入缓存已被禁用,所以我不希望操作系统缓冲数据?

光盘的基准速度约为 80 MB/秒,但我看到文件通道关闭时间很长,即使以约 40 MB/秒的速度写入也是如此。

我很欣赏随着光盘装满,写入性能会下降,但这些光盘是空的。

我可以做任何调整来消除关闭文件通道时的长时间延迟。我是否应该预先分配文件空间并使用 .lock 扩展名编写文件,然后在文件完成后进行重命名?

只是希望完成高吞吐量 IO 的人可以提供一些关于可能选项的指针,这些选项超出使用 NIO 编写文件时通常记录的内容。

代码在下面,我看不出任何立即错误。

公共最终类 DataWriter 实现 Closeable {

private static final Logger LOG = Logger.getLogger("DataWriter");

private static final long MB = 1024 * 1024;

private final int flushPeriod;

private FileOutputStream fos;
private FileChannel fileChannel;
private long totalBytesWritten;
private long lastFlushTime;
private final ByteBuffer buffer;
private final int bufferSize;
private final long startTime;
private long totalPackets = 0;
private final String fileName;

public DataWriter(File recordFile, int bSize, int flushPeriod) throws IOException {
    this.flushPeriod = flushPeriod;
    if (!recordFile.createNewFile()) {
        throw new IllegalStateException("Record file has not been created");
    }

    totalBytesWritten = 0;
    fos = new FileOutputStream(recordFile);
    fileChannel = fos.getChannel();
    buffer = ByteBuffer.allocateDirect(bSize);
    bufferSize = bSize;
    startTime = System.currentTimeMillis();
    this.fileName = recordFile.getAbsolutePath();
}

/**
 * Appends the supplied ByteBuffer to the main buffer if there is space 
 * @param packet
 * @return
 * @throws IOException
 */
public int write(ByteBuffer packet) throws IOException {
    int bytesWritten = 0;
    totalPackets++;

    //If the buffer cannot accommodate the supplied buffer then write straight out
    if(packet.limit() > buffer.capacity()) {
        bytesWritten = writeBuffer(packet);
        totalBytesWritten += bytesWritten;  
    } else {

       //write the currently filled buffer if no space exists to accomodate the current buffer
        if(packet.limit() > buffer.remaining()) {
           buffer.flip();
           bytesWritten = writeBuffer(buffer);
           totalBytesWritten += bytesWritten;   
        } 

        buffer.put(packet);                 
    }

    if(System.currentTimeMillis()-lastFlushTime > flushPeriod) {
        fileChannel.force(true);
        lastFlushTime=System.currentTimeMillis();
    }

    return bytesWritten;

}

public long getTotalBytesWritten() {
    return totalBytesWritten;
}

/**
 * Writes the buffer and then clears it
 * @throws IOException
 */
private int writeBuffer(ByteBuffer byteBuffer) throws IOException {
    int bytesWritten = 0;
    while(byteBuffer.hasRemaining()) {
        bytesWritten += fileChannel.write(byteBuffer);
    }

    //Reset the buffer ready for writing
    byteBuffer.clear();
    return bytesWritten;
}

@Override
public void close() throws IOException {
    //Write the buffer if data is present
    if(buffer.position() != 0) {
        buffer.flip();
        totalBytesWritten += writeBuffer(buffer);
        fileChannel.force(true);
    }

    long time = System.currentTimeMillis() - startTime;
    if(LOG.isDebugEnabled()) {
      LOG.debug(  totalBytesWritten + " bytes written in " + (time / 1000d) + " seconds using ByteBuffer size ["+bufferSize/1024+"] KB");
      LOG.debug(  (totalBytesWritten / MB) / (time / 1000d) + " MB per second written to file " + fileName);
      LOG.debug(  "Total packets written ["+totalPackets+"] average packet size ["+totalBytesWritten / totalPackets+"] bytes");
    }

    if (fos != null) {
        fos.close();
        fos = null;
    }
}

}

标签: javanio

解决方案


推荐阅读