使用 DataContractSerializer 过滤大量 XmlNodes 非常慢
Filtering a big number of XmlNodes using DataContractSerializer is very slow
考虑下面的代码
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 和内存密集型?
在您的 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
,保存另一个副本。
考虑下面的代码
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 和内存密集型?
在您的 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
,保存另一个副本。