【问题标题】:Filtering a big number of XmlNodes using DataContractSerializer is very slow使用 DataContractSerializer 过滤大量 XmlNode 非常慢
【发布时间】:2017-01-17 23:33:35
【问题描述】:

考虑下面的代码

List<CustomConversionData> Filter(XmlNodeList nodeList)
{
    var filteredResults= new List<CustomConversionData>();
    //Deserailze the data:
    foreach (XmlNode item in nodeList)
    {
      try
      {
        CustomConversionData obj = Deserialize<CustomConversionData>(item.ParentNode.OuterXml);
        filteredResults.Add(obj);

      }
      catch
      {
          try
          {                                            
              CustomConversionData obj = Deserialize<CustomConversionData>(item.OuterXml);
              filteredResults.Add(obj);
          }
          catch (Exception e) {
          }
        }
    }
   return filteredResults;
}

以及反序列化的方法

public T Deserialize<T>(string rawXml)
{
    using (XmlReader reader = XmlReader.Create(new StringReader(rawXml)))
    {
        DataContractSerializer formatter =
            new DataContractSerializer(typeof(T));
        return (T)formatter.ReadObject(reader);
    }
}

当我为包含 8000 个节点的 nodeList 运行此程序时,大约需要 6 个小时。我一直在寻找一种减少时间的方法,一开始我想也许我可以为每次迭代创建一个任务,但它变得比以前慢了,我认为这是因为在任务之间切换的开销。

我想知道提高这段代码性能的最佳方法是什么,因为它似乎非常占用 CPU 和内存?

【问题讨论】:

  • 我需要查看 xml 的几个部分,其中包含重复的主标记。 6 小时对于这么小的文件来说太长了。

标签: c# xml performance datacontractserializer


【解决方案1】:

在您的 Deserialize 方法中,我会将 formatter 设为静态成员,因为它每次都是相同的(我不确定它是否在内部缓存)。然后使用.ReadObject(new StringReader(rawXml)) 来节省引入XmlReader 所增加的复杂性。

然后就变得棘手了。尽量不要使用异常处理来控制你的逻辑,所以先做一些其他检查,而不是让它抛出并捕获它。

最后,我认为最大的胜利不是采用XmlNodeList,而是采用Stream 并创建一个XmlReader 来扫描XML 并仅在需要时反序列化它需要的内容。拥有所有这些对象图的前期成本将会快速增加。

编辑:另一个建议,将签名更改为IEnumerable&lt;CustomConversionData&gt;yield return,您将在其中使用.Add(...),这样消费代码可以流式传输结果,从而降低峰值内存使用率。

Edit2:每次首先选择ParentNode 让我觉得很奇怪。如果XmlNodeList 来自.ChildNodes 调用,那么您将一遍又一遍地反序列化相同的东西。如果它来自SelectNodes("..."),那么您可能会更聪明地选择具有 XPath 的正确节点,而不必获取父节点。如果您仍然需要这样做,请在此处创建一个XmlReader,检查元素名称,然后根据该名称决定是否需要父元素。如果你有正确的元素,那么你可以将XmlReader 传递给Derserialize,保存另一个副本。

【讨论】:

  • 谢谢 Stuart,我实际上进一步将 nodeList 分解为由 8000 个节点组成的 50 个块并同时处理它们,从而将时间从 6 小时减少到 7 分钟。我会做你的建议,因为目前我可以看到使用 TPL 的 CPU 和内存使用率很高,需要进一步改进
  • 嗨 Stuart,.ReadObject 不接受 StringReader 所以我不能使用 .ReadObject(new StringReader(rawXml))
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-09
  • 2013-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-16
  • 2017-06-27
相关资源
最近更新 更多