为什么java.nio.FileChannel transferTo()和transferFrom()比某些JVM / OS组合上的逐字节传输(基于流或使用ByteBuffer)更快?
这些方法是否使用直接内存访问(DMA)而不是为每个字节传输发出中断请求(IRQ)?
答案 0 :(得分:5)
这些方法是否使用直接内存访问(DMA)而不是为每个字节传输发出中断请求(IRQ)?
细节取决于实际实现,并且不需要使用任何特定机制。这在transferTo
(强调我的)的文档中暗示:
此方法可能比从此通道读取并写入目标通道的简单循环更有效。 许多操作系统可以直接从文件系统缓存向目标通道传输字节,而无需实际复制它们。
'可能','很多' ...所以没有保证。
假设它的可能使用该方法的效率更高是有意义的,如果只是稍微那样,因为您允许JVM快捷通过本机代码(如果使用支持的通道)类型)。简单的循环'他们描述的作品如下:
ByteBuffer buf = ByteBuffer.allocateDirect(BUF_SIZE);
while ( /* read more condition */ ) {
source.read(buf);
buf.flip();
target.write(buf);
buf.compact();
}
请注意,即使此代码段使用直接缓冲区,您仍然需要重新调查Java以进行缓冲区管理(读取,翻转,写入,压缩)。优化编译器可以能够忽略其中的一部分,但它可能会胜出。
然而,使用transferTo
/ transferFrom
将其留给JVM来决定如何传输字节。如果平台本身支持此类传输,则可以在不创建中间缓冲区的情况下执行此操作。如果没有此类支持,the JVM can still implement a loop such as above。
示例:假设SocketChannel通过transferFrom
直接从FileChannel读取数据。最近读取了FileChannel,其内容位于OS文件缓存中。 SocketChannel可以直接指向OS文件缓存并从那里开始传输,而不是将字节读取并复制到缓冲区中。至少完成了一轮复制。
现在进一步假设套接字(A)实际上连接到其他一些本地进程,例如使用一种名为SocketChannel B的管道。当B开始读取它从A获取的内容时,它实际上可能是直接从OS文件缓存再次。如果那么B只使用transferTo
到另一个频道......你明白了。
答案 1 :(得分:0)
FileChannel API中有一些解释
This method is potentially much more efficient than a simple loop that reads from the source channel and writes to this channel. Many operating systems can transfer bytes directly from the source channel into the filesystem cache without actually copying them.
BTW transferTo()和transferFrom()都是抽象的,所以这一切都取决于实际的实现
答案 2 :(得分:0)
它确实使用DMA /零拷贝,从而节省从“从”缓冲区到CPU的传输,然后从CPU传输到“到”缓冲区。有关更详细的说明,请阅读IBM的this文章
答案 3 :(得分:0)
在以下文章中,零拷贝
https://www.ibm.com/developerworks/linux/library/j-zerocopy/
作者解释说,新方法在Linux上更有效,因为它们减少了上下文切换,减少了从内核到应用程序和返回的不必要的缓冲区复制。