我正在维护一个高性能的CSV解析器,并试图最大限度地利用最新技术来提高吞吐量。对于此特定任务,这意味着:
Nehalem闪存(我们拥有相对便宜的PCIExpress卡,1 TB的存储,可达到1 GB/s的持续读取performance)
CSV解析器的第一个实现是单线程的。文件读取,字符解码,字段分割,文本解析,所有这些都在同一个线程中。其结果是吞吐量约为50MB/s。不错,但远低于存储限制……
第二个实现使用一个线程读取文件(在字节级别),一个线程解码字符(从ByteBuffer到CharBuffer),使用多个线程解析字段(我指的是将分隔的文本字段解析为双精度、整数、日期...)。这个速度更快,在我们的机器上接近400MB/s。
但仍远低于我们的存储性能。这些SSD在未来会再次改善,我们不会在Java中发挥最大的作用。很明显,当前的限制是字符解码( CharsetDecoder.read(...) )。这就是瓶颈,在功能强大的Nehalem处理器上,它以400MB/s的速度将字节转换为字符,非常好,但这必须是单线程的。CharsetDecoder是有状态的,这取决于所使用的字符集,并且不支持多线程解码。
所以我向社区提出的问题是(感谢你到目前为止阅读了这篇文章):有人知道如何在Java中并行化字符集解码操作吗?
发布于 2010-08-09 18:00:19
有人知道如何在
中并行化字符集解码操作吗?
您可以打开多个输入流来完成此操作(我不确定如何使用NIO完成此操作,但这必须是可能的)。
这会有多难,取决于你解码的编码。对于目标编码,您需要一个定制的解决方案。如果编码有一个固定的宽度(例如Windows1252),那么一个字节==一个字符和解码是很容易的。
现代的可变宽度编码(如UTF-8和UTF-16)包含用于标识字符序列的第一个字节的规则,因此可以跳到文件的中间并开始解码(您必须注意上一个块的结尾,因此首先开始解码文件的结尾是明智的做法)。
一些传统的可变宽度编码可能没有这么好的设计,因此您将别无选择,只能从数据的开头解码并按顺序读取它。
如果可以,则将数据生成为UTF-16BE格式。然后你就可以省去解码,直接把两个字节读成一个字符。
如果文件是Unicode,请注意BOM处理,但我猜您已经熟悉了许多低级细节。
发布于 2010-08-09 18:03:49
很明显,当前的限制是字符解码( CharsetDecoder.read(...) )
你怎么知道的?您的监控/分析是否最终显示解码器线程正在使用您的某个核心的100%?
另一种可能性是操作系统无法以其理论上的最大速度驱动SSD。
如果UTF-8解码肯定是瓶颈,那么应该可以并行完成这项任务。但您肯定需要实现自己的解码器才能做到这一点。
发布于 2010-08-11 17:56:28
另一种(疯狂的)替代方案是将输入分成任意大小的块,忽略解码问题,然后并行解码每个块。但是,您希望确保块重叠(具有参数化的大小)。如果两个线程以相同的方式解码两个块的重叠区域(并且您的重叠对于指定的编码来说足够大),那么连接结果应该是安全的。重叠越大,需要的处理就越多,出错的概率就越小。此外,如果您知道编码是UTF-8或类似的简单编码,则可以将重叠设置得非常低(对于该客户端),并且仍然可以保证正确的操作。
如果第二个块被证明是错误的,你将不得不重做,所以不要并行处理大块是很重要的。如果并行执行两个以上的块,从头到尾进行“修复”将很重要,这样一个未对齐的块就不会导致下一个块(可能正确对齐)无效。
https://stackoverflow.com/questions/3438466
复制相似问题