【问题标题】:Deciding on when to use XmlDocument vs XmlReader决定何时使用 XmlDocument 与 XmlReader
【发布时间】:2010-12-03 01:38:10
【问题描述】:

我正在优化一个自定义对象 -> XML 序列化实用程序,一切都已完成并且可以正常工作,这不是问题。

它通过将文件加载到XmlDocument 对象中,然后递归遍历所有子节点来工作。

我想也许使用XmlReader 而不是XmlDocument 加载/解析整个事情会更快,所以我也实现了那个版本。

算法完全相同,我使用包装类来抽象处理XmlNodeXmlReader 的功能。例如,GetChildren 方法 yield 返回子树 XmlNode 或子树 XmlReader

所以我编写了一个测试驱动程序来测试这两个版本,并使用了一个重要的数据集(一个包含大约 1,350 个元素的 900kb XML 文件)。

但是,使用 JetBrains dotTRACE,我看到XmlReader 版本实际上比XmlDocument 版本慢!当我遍历子节点时,XmlReader 读取调用似乎涉及一些重要的处理。

所以我说了这么多来问这个:

XmlDocumentXmlReader 的优点/缺点是什么,在什么情况下应该使用它们?

我的猜测是有一个文件大小阈值,XmlReader 在性能上变得更经济,并且内存密集型更少。但是,该阈值似乎高于 1MB。

我每次都调用ReadSubTree来处理子节点:

public override IEnumerable<IXmlSourceProvider> GetChildren ()
{
    XmlReader xr = myXmlSource.ReadSubtree ();
    // skip past the current element
    xr.Read ();

    while (xr.Read ())
    {
        if (xr.NodeType != XmlNodeType.Element) continue;
        yield return new XmlReaderXmlSourceProvider (xr);
    }
}

该测试适用于单个级别的许多对象(即宽和浅) - 但我想知道当 XML 深和宽时XmlReader 票价如何? IE。我正在处理的 XML 很像数据对象模型、1 个父对象到许多子对象等:1..M..M..M

我也事先不知道我正在解析的 XML 的结构,所以我无法对其进行优化。

【问题讨论】:

  • 我一直想知道为什么会有 XmlDocument 和 XmlReader...
  • 其实XMLDocument和XMLReader还有另外一种选择。您现在可以使用 LINQ to XML,但实际上 XMLReader 在大多数方面都更有效。
  • 等等。您的 GetChildren 方法返回 XmlReader?你的意思是,每次处理子节点时都调用XmlReader.Create()
  • 大多数使用XmlReader 的代码不使用ReadSubtree,所以这是一个不好的比较。此外,您需要为xr 设置一个using 块。
  • 使用后将 XML 文档设为 NULL 或使用 Blocks 在里面使用 xml 文档。

标签: c# xml-serialization xmldocument xmlreader


【解决方案1】:

另一个考虑因素是 XMLReader 对于处理格式不完美的 XML 可能更加健壮。我最近创建了一个使用 XML 流的客户端,但该流在某些元素中包含的 URI 中没有正确转义特殊字符。 XMLDocument 和 XPathDocument 根本拒绝加载 XML,而使用 XMLReader 我能够从流中提取我需要的信息。

【讨论】:

    【解决方案2】:

    我一般不是从最快的角度来看,而是从内存利用率的角度来看待它。对于我使用的场景(典型的企业集成),所有实现都足够快。

    然而,我失败的地方,有时是惊人的,没有考虑到我正在使用的 XML 的一般大小。如果您事先考虑一下,您可以省去一些悲伤。

    XML 在加载到内存时往往会膨胀,至少对于像 XmlDocumentXPathDocument 这样的 DOM 阅读器来说是这样。 10:1之类的?确切的数量很难量化,但如果它在磁盘上是 1MB,那么它在内存中将是 10MB,或者更多,例如。

    使用任何读取器将整个文档全部加载到内存中的进程 (XmlDocument/XPathDocument) 可能会遭受大对象堆碎片,这最终会导致 OutOfMemoryExceptions(即使有可用内存)导致服务/进程不可用。

    由于大小超过 85K 的对象最终位于大对象堆上,并且您使用 DOM 阅读器获得了 10:1 的大小爆炸,您可以看到在您的 XML 文档被从大对象堆中分配。

    XmlDocument 非常易于使用。它唯一真正的缺点是它将整个 XML 文档加载到内存中进行处理。它使用起来非常简单。

    XmlReader 是一个基于流的阅读器,因此您的进程内存利用率通常会保持平稳,但更难使用。

    XPathDocument 往往是更快的 XmlDocument 只读版本,但仍然存在内存“膨胀”问题。

    【讨论】:

    • 将 XML 文档(无论多么大)加载到内存中不会导致大对象。但是,将 XML 作为字符串保存!单个对象的大小对 GC 对内存进行碎片整理的能力很重要,但对象图的总大小对内存使用很重要。
    • FWIW 我刚刚在 XDocument、XMLReader 和 XmlDocument 之间做了一个基准测试。要执行类似的路径,它们分别花费了 0.004、0.001 和 0.692 秒。
    【解决方案3】:

    编码差异是因为混合了两种不同的测量值。 UTF-32 每个字符需要 4 个字节,本质上比单字节数据慢。

    如果您查看大型 (100K) 元素测试,您会发现无论使用何种加载方法,每种情况的时间都会增加约 70mS。

    这是一个(几乎)恒定的差异,具体由每个字符的开销引起,

    【讨论】:

      【解决方案4】:

      XmlDocument 是整个 XML 文档的内存表示。因此,如果您的文档很大,那么它会比使用 XmlReader 读取它消耗更多的内存。

      这是假设当您使用 XmlReader 时,您会一个接一个地读取和处理元素,然后将其丢弃。如果您使用 XmlReader 并在内存中构建另一个中间结构,那么您将遇到同样的问题,并且您违背了它的目的。

      Google 搜索“SAX versus DOM”以详细了解两种处理 XML 模型之间的区别。

      【讨论】:

      • 令人讨厌的事情是完全没有迹象表明文档在哪里(球场)变得“大”,并且 XmlReader 开始产生任何可观的大小优势。是 1KB、1MB 还是更多?我确定答案是“视情况而定”,但在没有任何线索的情况下,我们只能根据具体情况通过实验确定这些事情,除非需要能够处理任意大的数据(那么 XmlReader 是明确的选择)。
      【解决方案5】:

      有一个大小阈值,在该阈值时 XmlDocument 变得更慢,最终无法使用。但阈值的实际值将取决于您的应用程序和 XML 内容,因此没有硬性规定。

      如果您的 XML 文件可以包含大型列表(比如数万个元素),那么您绝对应该使用 XmlReader。

      【讨论】:

        猜你喜欢
        • 2012-10-20
        • 2012-02-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-08
        • 1970-01-01
        • 2011-06-16
        相关资源
        最近更新 更多