【问题标题】:VB.net read large XML file in parallelVB.net 并行读取大型 XML 文件
【发布时间】:2020-07-20 01:36:56
【问题描述】:

故事: 我有一个大约 70+ GB 的大型 XML 文件,我需要每周解析一次我的数据库。目前我在 vb.net 中有一个使用 XmlReader 的工作解析器。我目前以大约 5.000 个节点/秒的速度达到最大值,并且节点数 >10.000.000 并且还在增加,这需要一段时间才能完成。该程序使用普通 SSD 在车库中我自己的服务器上运行。我认为限制因素是 SSD,所以我最近升级到了三星 EVO 970 M.2 SSD,读/写速度提高了 6 倍。问题是我没有看到任何明显的性能提升。回过头来看,很明显瓶颈在其他地方..

想法: 我开始调查。我实现了 2 个单独的线程,每个线程从头开始读取文件,相隔几秒钟。每个线程仍然读取和处理大约 5.000 个节点/秒,因此我现在处理 10.000 个节点/秒非常有效。但是问题是我对每个节点进行了两次解析,这消除了目的。下一个想法是让一个线程从一开始就读取+处理数据。第二个线程也会从文件的开头读取,但是这个线程会在开始处理数据之前跳过文件的前半部分。使用 XMLReader.ReadToNextSibling() 我能够以 10.000 个节点/秒的速度“跳过”6.250.000 个节点。这实质上意味着,当第二个线程完成“跳过”并从 6.250.000 个节点开始解析时,第一个线程将处理大约 3.125.000 个节点。此时,将有大约 6.875.000 个节点在2 个线程和解析器将从此时开始处理大约 10.000 个节点/秒。本质上,我想增加线程,直到遇到另一个瓶颈。这种方法非常原始,并且“浪费”了大量时间阅读和跳过相同的节点。我尝试了 XmlReaderSettings.LineNumberOffset,但是我无法让它工作,而且无论偏移量如何,它似乎总是从头开始读取。

Dim settings = New Xml.XmlReaderSettings()
settings.LineNumberOffset = 100000

Dim XMLReader = Xml.XmlReader.Create("C:\largexml.xml", settings)

问题: 关于并行读取大型 XML 文件以及可能的瓶颈或优化的任何想法。有没有比这样使用 ReadToNextSibling 更快的“跳过”n 个元素的方法?

Dim Count As Integer = 0
While Count < 5000000
   XMLReader.ReadToNextSibling("ns")
   Count += 1
End While

我已经对当前的“跳过”解决方案进行了一些计算,并且由于我必须在每个线程中读取相同的数据,因此速度奖励与 +50% 的速度提升是渐近的。这是图表,以防有人遇到同样的问题并正在考虑类似的幼稚解决方案。这是预期的性能提升/线程。

【问题讨论】:

  • 那么节点是否等同于数据库记录?您是否正在读取节点并将结果直接发送到数据库?如何发送记录(顺序插入或批量复制)?你用的是什么数据库?
  • 这里所有的测试都没有解析到数据库。在生产中,我将 XML 文件中的 5.000 个节点保存在 DataTable 中,然后将其批量插入 MSSQL 服务器。然而,这几乎是立即发生的,在我当前的测试中,我只需将 5.000 行插入到我的应用程序中的 DataTable 中,然后在不推送到数据库的情况下清除它。所以目前的瓶颈不在于数据库。
  • 您是否尝试过将StreamReader's buffer size 从默认的 128 个字符增加到 1024 个字符,看看是否有帮助?您也可以尝试增加底层流的缓冲区。
  • @TnTinMn 这不是默认值,而是允许的最小值。 StreamReader 缓冲区的默认值为1024 字节(文件流缓冲区为4096),XMLReader 为4096,或8192,具体取决于流大小。
  • @Jimi,感谢您提示我检查。看起来我应该检查源代码而不是我引用的文档。在任何情况下,为 XMLReader 提供 StreamReader 可能仍然会有所帮助。创建具有较大缓冲区大小的。因此,而不是您所说的 8192 最大默认值,也许使用该值的两倍。他们赋予我们这种能力是有原因的。

标签: xml vb.net xmlreader


【解决方案1】:

看看下面的代码是否更快。 XElement.ReadFrom() 读取同级,因此它只读取一个项目一次。在任务管理器中检查代码运行时的内存使用情况。如果内存不足,则会使用磁盘上的交换空间,这确实会降低应用程序的速度。

Imports System.Xml
Imports System.Xml.Linq
Module Module1
    Const FILENAME As String = "C:\largexml.xml"
    Sub Main()
        Dim reader As XmlReader = XmlReader.Create(FILENAME)
        While Not reader.EOF
            If reader.Name <> "ns" Then
                reader.ReadToFollowing("ns")
            End If
            If Not reader.EOF Then
                Dim ns As XElement = XElement.ReadFrom(reader)
            End If
        End While

    End Sub

End Module

【讨论】:

  • 感谢您的回复。很好的建议。我明天会尝试这个实现并报告结果。我当前的解决方案有几个 mb 的持续内存使用。我希望能够利用服务器上当前更多的 16 GB 内存。理想情况下,它会将文件分块加载到内存中,比如说 8hb,但此时我很绝望,如果 ram 是问题所在,那么我愿意用 128 GB 升级我的服务器。那么理论上整个文件应该可以一次加载到内存中。
  • 我在巨大的 xml 中使用了很多此代码,但我在其中遇到了内存不足异常。一个案例必须解析一个 GML 图形文件,其中兄弟姐妹非常大,我仍然有 Out of Memory 异常并且必须读取较小的部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多