How to implement a buffered / batched FileChannel

2019-08-19 03:17发布

This does not look trivial, specially for a read/write buffered FileChannel. Is there anything opensource implemented somewhere that I can base my implementation on?


To be clear for those who did not understand:

FileChannel does buffereing in the OS level and I want to do buffering in the Java level. Read here to understand: FileChannel#force and buffering


@Peter I want to write a huge file to disk from a fast message stream. Buffering and batching are the way to go. So I want to batch in Java and then call FileChannel.write.

3条回答
▲ chillily
2楼-- · 2019-08-19 03:57

I recommend using a BufferedOutputStream wrapping a FileOutputStream. I do not believe you will see any performance improvement by mucking with ByteBuffer and FileChannel, and that you'll be left with a lot of hard-to-maintain code if you go that route.

The reasoning is quite simple: regardless of the approach you take, the steps involved are the same:

  1. Generate bytes. You don't say how you plan to do this, and it could introduce an additional level of temporary buffering into the equation. But regardless, the Java data has to be turned into bytes.
  2. Accumulate bytes into a buffer. You want to buffer your data before writing it, so that you're not making lots of small writes. That's a given. But where that buffer lives is immaterial.
  3. Move bytes from Java heap to C heap, across JNI barrier. Writing a file is a native operation, and it doesn't read directly from the Java heap. So whether you buffer on the Java heap and then move the buffered bytes, or buffer in a direct ByteBuffer (and yes, you want a direct buffer), you're still moving the bytes. You will make more JNI calls with the ByteBuffer, but that's a marginal cost.
  4. Invoke fwrite, a kernel call that copies bytes from the C heap into a kernel-maintained disk buffer.
  5. Write the kernel buffer to disk. This will outweigh all the other steps combined, because disks are slow.

There may be a few microseconds gained or lost depending on exactly how you implement these steps, but you can't change the basic steps.

The FileChannel does give you the option to call force(), to ensure that step #5 actually happens. This is likely to actually decrease your overall performance, as the underlying fsync call will not return until the bytes are written. And if you really want to do it, you can always get the channel from the underlying stream.

Bottom line: I'm willing to bet that you're actually IO-bound, and there's no cure for that save better hardware.

查看更多
我欲成王,谁敢阻挡
3楼-- · 2019-08-19 04:02

I would have two threads, the producer thread produces ByteBuffers and appends them to the tail a queue, the consumer thread remove some ByteBuffers from the head of the queue each time, and call fileChannel.write(ByteBuffer[]).

查看更多
The star\"
4楼-- · 2019-08-19 04:03

FileChannel only works with ByteBuffers so it is naturally buffered. If you need additional buffering to can copy data from ByteBuffer to ByteBuffer but I am not sure why you would want to.

FileChannel does buffereing in the OS level

FileChannel does tell the OS what to do. The OS usually has a disk cache but FileChannel has no idea whether this is the case or not.

I want to do buffering in the Java level

You are in luck, because you don't have a choice. ;) This is the only option.

查看更多
登录 后发表回答