为什么在某些 JVM/OS 组合上 java.nio.FileChannel transferTo() 和 transferFrom() 比逐字节传输(基于流或使用 ByteBuffer)更快???
这些方法是否使用直接内存访问 (DMA) 而不是为每个字节传输发出中断请求 (IRQ)?
为什么在某些 JVM/OS 组合上 java.nio.FileChannel transferTo() 和 transferFrom() 比逐字节传输(基于流或使用 ByteBuffer)更快???
这些方法是否使用直接内存访问 (DMA) 而不是为每个字节传输发出中断请求 (IRQ)?
这些方法是否使用直接内存访问 (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 决定如何传输字节。如果平台对这种传输有本机支持,它可能能够在不创建中间缓冲区的情况下这样做。如果没有这样的支持,JVM 仍然可以实现如上的循环。
示例:假设 SocketChannel 被告知直接从 FileChannel 读取数据transferFrom
。最近从 FileChannel 读取,其内容在 OS 文件缓存中。SocketChannel 可以直接指向 OS 文件缓存并从那里开始传输,而不是读取字节并将其复制到缓冲区中。至少淘汰了一轮抄袭。
现在进一步假设套接字(A)实际上连接到其他一些本地进程,例如使用一种称为 SocketChannel B 的管道。当 B 开始读取它从 A 获得的内容时,它可能实际上是直接从 OS 文件中读取再次缓存。如果那么 B 只是使用transferTo
另一个频道......你明白了。
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.
顺便说一句,transferTo() 和 transferFrom() 都是抽象的,所以这一切都取决于实际的实现
它确实使用 DMA/零拷贝,从而节省了从“From”缓冲区到 CPU 的传输,然后是从 CPU 到“to”缓冲区的传输。有关更详细的解释,请阅读IBM的这篇文章
在下面的文章中,关于零拷贝
https://www.ibm.com/developerworks/linux/library/j-zerocopy/
作者解释说,新方法在 Linux 上更有效,因为它们减少了上下文切换并减少了从内核到应用程序和返回的不必要的缓冲区复制。