Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/290.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Linq性能解析XML文件_C#_Xml_Performance_Linq - Fatal编程技术网

C# Linq性能解析XML文件

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

我需要解析一个XML文件(1~10MB);我使用的是
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);