确定合适的缓冲区大小

Sna*_*Doc 5 java buffer bytebuffer

我正在使用 ByteBuffer.allocateDirect() 分配一些缓冲内存以将文件读入内存,然后最终散列该文件字节并从中获取文件散列 (SHA)。输入文件的大小范围很大,从几 KB 到几 GB 不等。

我已经阅读了几个关于选择缓冲区大小的线程和页面(甚至一些在 SO 上)。有些人建议尝试选择本机 FileSystem 使用的一个,以尽量减少对部分块等进行读取操作的机会。比如4100字节的缓冲区,NTFS默认是4096,所以多出来的4位需要单独读操作,非常浪费。

所以坚持使用 2、1024、2048、4096、8192 等的幂。我看到一些推荐的缓冲区大小为 32KB,其他建议将缓冲区设置为输入文件的大小(对于小文件可能没问题,但是什么关于大文件?)。

坚持使用原生块大小的缓冲区有多重要?现代来说(假设现代 SATA 驱动器或更好的驱动器缓存至少有 8Mb,以及其他现代操作系统“魔法”来优化 I/O)缓冲区大小有多重要,我应该如何最好地确定将我的大小设置为多少?我可以静态设置它,还是动态确定它?感谢您的任何见解。

par*_*fal 5

回答您的直接问题:(1) 文件系统倾向于使用 2 的幂,因此您也想这样做。(2) 您的工作缓冲区越大,任何尺寸错误的影响就越小。

如您所说,如果您分配 4100 并且实际块大小为 4096,您将需要两次读取来填充缓冲区。相反,如果您有一个 1,000,000 字节的缓冲区,那么高或低一个块并不重要(因为填充该缓冲区需要 245 个 4096 字节的块)。此外,更大的缓冲区意味着操作系统有更好的机会对读取进行排序。

也就是说,我不会为此使用 NIO。相反,我会使用一个简单的BufferedInputStream,我的read()s可能有一个 1k 缓冲区。

NIO 的主要好处是将数据保留在 Java 堆之外。例如,如果您正在读取和写入文件,则使用InputStream操作系统将数据读入 JVM 管理的缓冲区的方法,JVM 将其复制到堆上缓冲区,然后再次将其复制到堆外缓冲区,然后操作系统读取该堆外缓冲区以写入实际的磁盘块(通常会添加自己的缓冲区)。在这种情况下,NIO 将消除本机堆副本。

但是,要计算散列,您需要在 Java 堆中拥有数据,而MacSPI会将其移动到那里。所以你不会得到 NBI 将数据保持在堆外的好处,而且 IMO 的“旧 IO”更容易编写。

但不要忘记,InputStream.read()保证阅读所有你要求的字节数。