java - 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;
}
}
}
解决方案
推荐阅读
- deployment - 如何在公共场所确保 Coral Dev Board 的安全?
- neural-network - 这是计算二维特征图协方差矩阵的正确方法吗?
- javascript - 如何在 AJAX 和 JSON 中显示多个 HTML 实体?
- flutter - Flutter - 如何将 TextStyle 添加到 BottomNavigationBarItem 中的标签?
- python - 从两个不同的变量中找到平均值
- mongodb - Mongodb (code=exited, status=14) 失败但没有任何明确的错误
- javascript - javascript如何处理单线程的多个请求?
- kdb - 为键控表中的所有列选择不同的
- spring-boot - @EnableBatchProcessing 和 @DataJpaTest 在带有休眠的 Spring Boot 应用程序中
- powershell - 所有子文件夹中每个文件中的 Powershell 更新字符串