使用DataContractSerializer过滤大量XmlNode非常慢

时间:2017-01-17 23:33:36

标签: c# xml performance datacontractserializer

考虑以下代码

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和内存?

1 个答案:

答案 0 :(得分:2)

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

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

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

修改:另一个建议,将签名更改为IEnumerable<CustomConversionData>yield return,这样您可以执行.Add(...),这样消费代码就可以对结果进行流式传输,从而保持峰值内存使用率下降。

Edit2:每次首先选择ParentNode为奇数。如果XmlNodeList来自.ChildNodes来电,那么您将一遍又一遍地反序列化相同的内容。如果它来自SelectNodes("...")那么您可能能够更聪明地选择正确的节点与XPath开始而不必获得父节点。如果您仍然需要这样做,请在此处创建XmlReader,检查元素名称,并从中确定是否需要父项。如果你有正确的元素,那么你可以将XmlReader传递给Derserialize,保存另一个副本。