【发布时间】:2018-11-21 02:48:05
【问题描述】:
我正在开发一个 REST API,它有一个端点来下载大小可能 > 2 GB 的文件。我读过如果操作系统支持,Java 的FileChannel.transferTo(...) 将使用零拷贝。在我的 MacBook Pro OS 10.11.6 上开发期间,我的服务器在 localhost 上运行。
我比较了以下两种将文件写入响应流的方法:
- 使用
transferTo将固定数量的字节从FileChannel复制到WritableByteChannel - 从
FileInputStream读取固定数量的字节到一个字节数组(大小为4096)并循环写入OutputStream。
使用这两种方法处理 5.2GB 文件所需的时间在 20 到 23 秒之间。我尝试了transferTo,将单次传输中的固定字节数设置为以下值:4KB(即 4 * 1024)、1MB 和 50MB。 3 种情况下的写作时间都在同一范围内。
所用时间是从进入 while 循环之前到退出 while 循环之后测量的,其中从文件中读取字节。这一切都在服务器端。网络跳跃时间不计算在内。
关于原因可能是什么的任何想法?我很确定 MacOS 10.11.6 应该支持零拷贝(即sendfile 系统调用)。
编辑 (2018 年 6 月 18 日):
我发现以下 2015 年的博客文章,说 MacOS X 上的 sendfile 已损坏。难道这个问题还存在?
https://blog.phusion.nl/2015/06/04/the-brokenness-of-the-sendfile-system-call/
【问题讨论】:
-
在 OS 上找到的非常有趣的文章。我认为原因隐藏在这个 git repos 中,github.com/ecdavis/pants/issues/43
-
感谢 Github 问题链接。那是从 2013 年开始的,比我的问题编辑(2015 年)中的博客文章链接早。所以我仍然不知道在 MacOS X 上 sendfile 是否仍然损坏。我想我会在 Mac 论坛(Apple 官方或其他一些好的论坛)中询问
-
FileChannel.transferTo 仅在传输套接字时使用 sendfile,如在 SocketChannel 中。
-
@AlanBateman - 来自
FileChanneljavadoc docs.oracle.com/javase/8/docs/api/java/nio/channels/… :此方法可能比从该通道读取并写入目标通道的简单循环更有效。许多操作系统可以直接将字节从文件系统缓存传输到目标通道,而无需实际复制它们。 -
@AlanBateman - 抱歉,我想我没有仔细阅读您的评论。我以为你说只有写到
SocketChannel才使用sendfile。但是,我不明白您所说的 "transferring a socket" 是什么意思。