6

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

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

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

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

4

1 回答 1

6

回答您的直接问题:(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()只是不要忘记,不能保证读取您要求的所有字节。

于 2013-04-17T19:08:17.787 回答