Java:多线程字符流解码

2022-09-02 22:35:41

我正在维护一个高性能的CSV解析器,并尝试充分利用最新技术来提高吞吐量。对于此特定任务,这意味着:

  • 闪存(我们拥有一个相对便宜的PCI-Express卡,1 TB的存储空间达到1 GB / s的持续读取性能)
  • 多核(我们拥有一个便宜的Nehalem服务器,有16个硬件线程)

CSV 解析器的第一个实现是单线程的。文件读取,字符解码,字段分割,文本解析,所有这些都在同一线程中。结果是吞吐量约为 50MB/s。还不错,但远低于存储限制...

第二个实现使用一个线程来读取文件(在字节级别),一个线程来解码字符(从ByteBuffer到CharBuffer),并使用多个线程来解析字段(我的意思是将脱离的文本字段解析为双精度,整数,日期...)。这工作得更快,在我们的盒子上接近400MB / s。

但仍然远低于我们的存储性能。这些SSD将来会再次改进,我们在Java中没有充分利用它。很明显,当前的限制是字符解码( CharsetDecoder.read(...) )。这就是瓶颈,在功能强大的Nehalem处理器上,它以400MB / s的速度将字节转换为字符,这相当不错,但这必须是单线程的。字符集解码器有点有状态,具体取决于使用的字符集,并且不支持多线程解码。

所以我对社区的问题是(感谢你到目前为止阅读这篇文章):有人知道如何在Java中并行化字符集解码操作吗?


答案 1

有谁知道如何在Java中并行化字符集解码操作?

你也许能够打开多个输入流来做到这一点(我不确定你会如何使用NIO来做到这一点,但这必须是可能的)。

这有多难取决于您要解码的编码。您将需要一个针对目标编码的定制解决方案。如果编码具有固定宽度(例如Windows-1252),则一个字节==一个字符,解码很容易。

现代可变宽度编码(如 UTF-8 和 UTF-16)包含用于标识字符序列的第一个字节的规则,因此可以跳转到文件的中间并开始解码(您必须注意上一个块的结尾,因此首先开始解码文件的末尾是明智的)。

一些传统的可变宽度编码可能设计得不是那么好,所以你别无选择,只能从数据开始解码并按顺序读取。

如果是一个选项,请将数据生成为 UTF-16BE。然后,您可以剪切解码并将两个字节直接读取到字符。

如果文件是Unicode,请注意BOM处理,但我猜你已经熟悉了许多低级细节。


答案 2

很明显,目前的限制是字符解码( CharsetDecoder.read(...) )

你怎么知道?您的监控/分析是否最终表明解码器线程正在使用100%的内核之一?

另一种可能性是操作系统无法以理论上的最大速度驱动SSD。

如果UTF-8解码绝对是瓶颈,那么应该可以并行完成任务。但是您肯定需要实现自己的解码器才能执行此操作。