【问题标题】:Parallel XML Parsing in JavaJava 中的并行 XML 解析
【发布时间】:2011-05-11 15:36:26
【问题描述】:

我正在编写一个应用程序,它处理大量具有深层节点结构的 xml 文件 (>1000)。使用 woodstox(事件 API)大约需要 6 秒钟来解析一个包含 22.000 个节点的文件。

该算法被放置在一个与用户交互的过程中,其中只有几秒钟的响应时间是可以接受的。所以我需要改进如何处理xml文件的策略。

  1. 我的进程分析 xml 文件(仅提取几个节点)。
  2. 处理提取的节点并将新结果写入新数据流(生成带有修改节点的文档副本)。

现在我正在考虑一种多线程解决方案(在 16 Core+ 硬件上可以更好地扩展)。我想到了以下策略:

  1. 创建多个解析器并在 xml 源上并行运行它们。
  2. 重写我的解析算法线程保存以仅使用解析器的一个实例(工厂,...)
  3. 将 XML 源拆分为块并将这些块分配给多个处理线程 (map-reduce xml - serial)
  4. 优化我的算法(StAX 解析器比 woodstox 更好?)/使用具有内置并发性的解析器

我想同时提高整体性能和“每个文件”的性能。

您有解决此类问题的经验吗?最好的方法是什么?

【问题讨论】:

  • 不清楚这里需要最大化什么...单个文件的性能,或所有 1000 个文件的总性能。
  • 还有一个建议:如果您可以量化文件的大小,以允许计算吞吐量(每秒处理的兆字节数),它可以给出预期性能的概念。我在测试时使用 Woodstox 进行解析的速度通常为 10 - 40 MB/s;但我的硬盘只能提供 5 - 10 MB/s 的持续速度。

标签: java xml multithreading parallel-processing xml-parsing


【解决方案1】:
  1. 这一点很明显:只需创建几个解析器并在多个线程中并行运行它们。

  2. 看一下Woodstox Performance(暂时下来,试试google cache)。

  3. 如果您的 XML 的结构是可预测的,则可以这样做:如果它有很多相同的顶级元素。例如:

    <element>
        <more>more elements</more>
    </element> 
    <element>
        <other>other elements</other>
    </element>
    

    在这种情况下,您可以创建简单的拆分器来搜索&lt;element&gt; 并将此部分提供给特定的解析器实例。这是一种简化的方法:在现实生活中,我会使用 RandomAccessFile 来查找起点 (&lt;element&gt;),然后创建仅对文件的一部分进行操作的自定义 FileInputStream。

  4. 看看Aalto。创造伍德斯托克斯的人。这是该领域的专家 - 不要重新发明轮子。

【讨论】:

    【解决方案2】:

    我同意吉姆的观点。我认为,如果您想提高 1000 个文件的整体处理性能,您的计划是好的,除了在这种情况下无关的 #3。 但是,如果您想提高解析单个文件的性能,您就会遇到问题。我不知道如何在不解析的情况下拆分 XML 文件。每个块都是非法的 XML,你的解析器会失败。

    我相信提高整体时间对您来说已经足够了。在这种情况下,请阅读本教程: http://download.oracle.com/javase/tutorial/essential/concurrency/index.html 然后创建包含 XML 源的例如 100 个线程和队列的线程池。每个线程将只解析 10 个文件,这将在多 CPU 环境中带来显着的性能优势。

    【讨论】:

    • +1:虽然如果解析足够简单以至于主要问题是 IO,它可能不会提高性能。
    【解决方案3】:

    除了现有的好建议之外,还有一件相当简单的事情要做:使用游标 API (XMLStreamReader),而不是事件 API。事件 API 增加了 30-50% 的开销,而(仅 IMO)没有显着简化处理。实际上,如果您想要方便,我建议您改用StaxMate;它建立在 Cursor API 之上,不会增加大量开销(与手写代码相比最多 5-10%)。

    现在:我假设您已经对 Woodstox 进行了基本优化;但如果没有,请查看“3 Simple Rules for Fast XML-processing using Stax”。具体来说,您绝对应该:

    1. 确保只创建一次 XMLInputFactory 和 XMLOutputFactory 实例
    2. 关闭读取器和写入器以确保缓冲区回收(和其他有用的重用)按预期工作。

    我提到这一点的原因是,虽然它们没有功能上的差异(代码按预期工作),但它们可以产生很大的性能差异;虽然在处理较小的文件时更是如此。

    运行多个实例也很有意义;虽然通常每个核心最多有 1 个线程。但是,只有您的存储 I/O 能够支持这样的速度,您才会受益;如果磁盘是瓶颈,这将无济于事,并且在某些情况下可能会受到伤害(如果磁盘寻求竞争)。但值得一试。

    【讨论】:

      猜你喜欢
      • 2014-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-17
      • 2012-03-07
      相关资源
      最近更新 更多