C# Linq性能解析XML文件
我需要解析一个XML文件(1~10MB);我使用的是C# Linq性能解析XML文件,c#,xml,performance,linq,C#,Xml,Performance,Linq,我需要解析一个XML文件(1~10MB);我使用的是XDocument 目前,我正在使用Linq查询XML文档,但我需要提高应用程序的性能,我想用旧式循环替换Linq查询,但没有任何改进 下面是一段最常见的查询代码: Stopwatch stopwatch = new Stopwatch(); XDocument xdoc = XDocument.Load("filename.xml"); string def = "Version"; XElement xelm; stopwatch.Sta
XDocument
目前,我正在使用Linq
查询XML文档,但我需要提高应用程序的性能,我想用旧式循环替换Linq
查询,但没有任何改进
下面是一段最常见的查询代码:
Stopwatch stopwatch = new Stopwatch();
XDocument xdoc = XDocument.Load("filename.xml");
string def = "Version";
XElement xelm;
stopwatch.Start();
for (int i = 0; i < 1000; i++)
xelm = xdoc.Descendants("def").Where(d => d.Attribute("name").Value == def).Single();
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
stopwatch.Restart();
for (int i = 0; i < 1000; i++)
{
foreach (var elm in xdoc.Descendants("def"))
{
if (elm.Attribute("name").Value == def)
{
xelm = elm;
break;
}
}
}
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
秒表秒表=新秒表();
XDocument xdoc=XDocument.Load(“filename.xml”);
string def=“Version”;
XElement Xelem;
秒表。开始();
对于(int i=0;i<1000;i++)
xelm=xdoc.subjects(“def”)。其中(d=>d.Attribute(“name”)。Value==def.Single();
秒表;
控制台写入线(秒表延时百万秒);
stopwatch.Restart();
对于(int i=0;i<1000;i++)
{
foreach(xdoc.substands(“def”)中的var elm)
{
if(elm.Attribute(“name”).Value==def)
{
xelm=elm;
打破
}
}
}
秒表;
控制台写入线(秒表延时百万秒);
两个版本的运行时间几乎相同,对我来说,这个结果非常奇怪,因为我认为Linq
的Where
方法在调用时必须创建一个新列表
为什么两个版本的性能相同?有没有改进原始代码的方法?LINQ使用,这意味着它只在需要时对集合进行一次迭代
当您必须操作大型数据集合时,延迟执行可以极大地提高性能,特别是在包含一系列链式查询或操作的程序中。在最好的情况下,延迟执行只允许通过源集合进行单个迭代
在这种情况下,没有从Where
子句生成新列表。事实上,LINQ语句的编译版本将以与foreach
语句几乎相同的方式运行
编辑:我关于提高性能的唯一想法是遵循Robert McKee的回答,即您应该使用.First()
而不是.Single()
,这样就不必遍历整个列表。除此之外,我不知道你还能做什么,除了使用不同的库或不同的语言。将.Single()
切换到.First()
,它的性能应该会更好。for循环几乎与.First()
的功能完全相同,而.Single
更像:
for (int i = 0; i < 1000; i++)
{
foreach (var elm in xdoc.Descendants("def"))
{
if (elm.Attribute("name").Value == def)
{
if (xelm!=null)
throw new InvalidOperationException();
xelm = elm;
}
}
if (xelm==null)
throw new InvalidOperationException();
}
for(int i=0;i<1000;i++)
{
foreach(xdoc.substands(“def”)中的var elm)
{
if(elm.Attribute(“name”).Value==def)
{
如果(xelm!=null)
抛出新的InvalidOperationException();
xelm=elm;
}
}
if(xelm==null)
抛出新的InvalidOperationException();
}
它将继续遍历文档,直到找到另一个匹配项(并引发异常)或到达文档末尾
您可以尝试使用PLINQ,但您需要进行广泛的性能测试,因为PLINQ的性能变化有很多因素,包括其运行的硬件和文档结构等
stopwatch.Start();
for (int i = 0; i < 1000; i++)
xelm = xdoc.AsParallel()
.Descendants("def")
.Where(d => d.Attribute("name").Value == def)
.Single();
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
stopwatch.Start();
对于(int i=0;i<1000;i++)
xelm=xdoc.AsParallel()
.后代(“def”)
.Where(d=>d.Attribute(“name”).Value==def)
.Single();
秒表;
控制台写入线(秒表延时百万秒);
要总结您的情况:
XDocument
XDocument
运行许多线性搜索,以按名称和属性值查找节点XDocument
上的和本身可以用于此目的
例如,以下类可用于按名称查找XElement
节点,并在添加或删除节点或更改节点名称时动态更新其查找表:
public class XDocumentRepository
{
readonly XDocument doc;
readonly Dictionary<XName, List<XElement>> elementsByName = new Dictionary<XName, List<XElement>>();
public XDocument Document { get { return doc; } }
public IEnumerable<XElement> ElementsByName(XName name)
{
return elementsByName.Items(name);
}
public XDocumentRepository(XDocument doc)
{
if (doc == null)
throw new ArgumentNullException();
this.doc = doc;
doc.Changing += new EventHandler<XObjectChangeEventArgs>(doc_Changing);
doc.Changed += new EventHandler<XObjectChangeEventArgs>(doc_Changed);
AddAll(doc.Root);
}
private void AddAll(XElement root)
{
foreach (var element in root.DescendantsAndSelf())
elementsByName.AddItem(element.Name, element);
}
private void RemoveAll(XElement root)
{
foreach (var element in root.DescendantsAndSelf())
elementsByName.RemoveItem(element.Name, element);
}
void doc_Changed(object sender, XObjectChangeEventArgs e)
{
XElement xSender = sender as XElement;
if (xSender != null && xSender.Document == doc)
{
switch (e.ObjectChange)
{
case XObjectChange.Add:
case XObjectChange.Remove:
AddAll(xSender);
break;
case XObjectChange.Name:
elementsByName.AddItem(xSender.Name, xSender);
break;
case XObjectChange.Value:
break;
default:
Debug.Assert(false, "unknown ObjectChange");
break;
}
}
// If an attribute value were changed, sender would be an XAttribute
}
void doc_Changing(object sender, XObjectChangeEventArgs e)
{
XElement xSender = sender as XElement;
if (xSender != null)
{
switch (e.ObjectChange)
{
case XObjectChange.Add:
case XObjectChange.Remove:
RemoveAll(xSender);
break;
case XObjectChange.Name:
elementsByName.RemoveItem(xSender.Name, xSender);
break;
case XObjectChange.Value:
break;
default:
Debug.Assert(false, "unknown ObjectChange");
break;
}
}
// If an attribute value were changed, sender would be an XAttribute
}
}
public static class DictionaryExtensions
{
public static void AddItem<TKey, TValueList, TValue>(this IDictionary<TKey, TValueList> listDictionary, TKey key, TValue value)
where TValueList : IList<TValue>, new()
{
if (listDictionary == null)
throw new ArgumentNullException();
TValueList values;
if (!listDictionary.TryGetValue(key, out values))
listDictionary[key] = values = new TValueList();
values.Add(value);
}
public static IEnumerable<TValue> Items<TKey, TValue>(this IDictionary<TKey, List<TValue>> listDictionary, TKey key)
{
if (listDictionary == null)
throw new ArgumentNullException();
List<TValue> list;
if (!listDictionary.TryGetValue(key, out list))
return Enumerable.Empty<TValue>();
return list;
}
public static bool RemoveItem<TKey, TValueList, TValue>(this IDictionary<TKey, TValueList> listDictionary, TKey key, TValue value)
where TValueList : IList<TValue>, new()
{
if (listDictionary == null)
throw new ArgumentNullException();
TValueList values;
if (!listDictionary.TryGetValue(key, out values))
return false;
return values.Remove(value);
}
}
注意,由于正在使用字典,返回元素的顺序是未定义的。如果有多个元素与任何给定的查询匹配,您可以使用将它们按文档顺序排序。您可能需要尝试一下。我在我的机器上试过这个,如果订单对你来说并不重要,我可以看到它是有益的,尽管我没有打印出所选的元素。根据实际代码的不同,您可能需要使用锁进行同步
stopwatch.Restart();
Parallel.For(0, 1000, i =>
{
foreach (var elm in xdoc.Descendants("def"))
{
if (elm.Attribute("name").Value == "def")
{
xelm = elm;
break;
}
}
});
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
请注意,使用Parallel.foreach并行化行“foreach(xdoc.substands(“def”)中的var elm)”在我的机器上似乎没有提供良好的性能,这可能是因为并行化的开销很高(我猜)。文档结构是众所周知的(每次都是相同的)?@Shnugo no,不幸的是,它可能会在运行时更改。如果
XDocument
太慢,请尝试使用XmlReader
扫描文件:如果枚举中有>1项,则引发异常,因此必须扫描整个集合。一旦找到第一个匹配项,您的手动循环就会中断。你想要哪一个?如果OP会看到显著的差异,我会感到惊讶:从循环(提前终止)的结果来看,应该没有什么差异。真的。我想知道它是否从未找到匹配项,并且在返回null之前都在遍历整个文档。Single
,因此它必须只找到一个匹配项。@dbc
stopwatch.Restart();
Parallel.For(0, 1000, i =>
{
foreach (var elm in xdoc.Descendants("def"))
{
if (elm.Attribute("name").Value == "def")
{
xelm = elm;
break;
}
}
});
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);