我在用:
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("test.txt"),1024*1024*500))
写入一个大文件(约 2GB)。写入需要 26 秒。但是,当我用 10/20 替换 500 时,需要 19 秒。
从这里,我理解的是缓冲提供了更好的性能。如果是这样,那么为什么会发生这种情况?我通过每次运行 5 次来检查它,因此系统/IO 负载不是问题。
正如我在上一个问题中所说,有一个最佳缓冲区大小(通常约为 32 KB),当您使缓冲区大于此大小时,它会更慢而不是更快。默认缓冲区大小为 8 KB。
顺便说一句:你的 L2/L3 CPU 缓存有多大?(我怀疑大约 10 MB)您的主 L1 缓存大约是 32 KB?
通过使用适合最快缓存的缓冲区,您正在使用最快的内存。通过使用仅适合主内存的缓冲区,您正在使用最慢的内存(慢 10 倍)
在回答你的问题。
我所做的是假设ISO-8859-1
编码(byte) ch
,即一次将一个字节写入 ByteBuffer,可能是内存映射。
我有用于写入/读取long
和double
从 ByteBuffer 读取的方法,而不会产生任何垃圾。
使用这种方法,您可以每秒将大约 500 万行记录到磁盘。
缓冲区过大会降低性能。坚持大约 32-64 kb IMO
1024*1024*500
是 500 兆字节,给或取一点点。您基本上是在强制 JVM 分配一个 500mb 的连续内存块,而 JVM 可能必须执行 GC 循环才能做到这一点。
非常大的缓冲区(500 MB)也不好,因为对于操作系统来说,为那个巨大的字节缓冲区进行内存管理会更加困难。
将其与在您的房子里移动一张桌子而不是移动一个盒子进行比较。但是如果你的箱子变得太小,你将不得不去来回很多次。
不要忘记分配内存是一个O(n)
操作。
首先,您真的不需要那么大的缓冲区。通常 64K 甚至低至 8K 就足以获得下降 IO 性能。任何更大,您只是在浪费内存和 CPU,因为随着缓冲区越来越大,它会在 IO 层花费更多时间来写入大量数据。因此,在等待 IO 和仅写入内存之间进行权衡(如果您了解微积分,则为 min-max)。您不能将巨大的缓冲区推送到 IO 设备,因为它有一个内部固定大小的缓冲区。关键是尽可能地尝试匹配它,但要意识到这样做是相对不可能的,因为您不知道其他处理在做什么。最好的办法是尝试一些低 8K-16K 的东西,运行它,测量它。将缓冲区 32K 等加倍,运行它,测量它。如果你的速度有所提高,请再做一次。
因此,如果您在 26 秒内写入 2GB 数据,则吞吐量为 76MB/S 或 650Mbit/s。您可以通过将缓冲区大小降低到合理的值来改进它。
通过减少系统调用的数量,缓冲 I/O 可以在一定程度上提高性能。但是系统调用并没有那么昂贵(可能是一毫秒左右),过大的缓冲区可能会导致其他方面的问题。例如:
500 MB 的缓冲区使用大量内存,并可能增加 GC 开销,或增加系统的分页负载。
如果您在一次写入调用中写入 500 MB,则写入可能会使系统的缓冲区缓存饱和,并压倒其在应用程序级别执行其他操作时重叠磁盘写入的能力。
只需尝试使用(显着)较小的缓冲区。(如果不进行一些特定于应用程序的调整,我个人不会使用大于 8kb 的缓冲区。)