确定适当的缓冲区大小

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

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

因此坚持使用2,1024,2048,4096,8192等的function。我看到一些推荐的缓冲区大小为32KB,其他建议使缓冲区大小为输入文件(对于小文件可能很好,但是关于大文件?)。

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

要回答你的直接问题:(1)文件系统倾向于使用2的幂,所以你想要做同样的事情。 (2)工作缓冲区越大,误差小的影响就越小。

如你所说,如果你分配4100并且实际的块大小是4096,你需要两次读取来填充缓冲区。 相反,如果你有一个1,000,000字节的缓冲区,那么一个块高或低并不重要(因为它需要245个4096字节的块来填充该缓冲区)。 此外,较大的缓冲区意味着OS具有更好的订购读取的机会。

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

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

但是,要计算哈希值,您需要将数据放在Java堆中,然后Mac SPI 将其移动到那里 。 所以你没有得到NBI将数据保持在堆外的好处,而IMO“旧IO”更容易编写。

只是不要忘记InputStream.read() 不能保证读取你要求的所有字节。