2012-12-22 14 views
15

Wenn Sie ein neues XDocument mit XDocument.Load erstellen, öffnet es die XML-Datei und behält eine lokale Kopie, oder liest es das Dokument kontinuierlich von der Festplatte? Wenn es kontinuierlich liest, gibt es eine schnellere Möglichkeit, XML zu analysieren?XDocument Leistung

XDocument x = XDocument.Load("file.xml"); 

Antwort

10

Es gibt ein paar Messungen betrachten zu:

  1. Linear-Traversal-Geschwindigkeit (zB Lesen/Laden)
  2. On-Demand-Abfragegeschwindigkeit

die sofortige zu beantworten Frage:XDocument verwendet eine XmlReader, um das Dokument in den Speicher zu laden, indem jedes Element und gelesen wird Erstellen von entsprechenden XElement Instanzen (siehe Code unten). Als solches sollte es ziemlich schnell sein (schnell genug für die meisten Zwecke), aber es kann eine große Menge an Speicher belegen, wenn ein großes Dokument analysiert wird.

Eine rohe XmlReader ist eine ausgezeichnete Wahl für Traversal, wenn Ihre Bedürfnisse auf das beschränkt sind, was getan werden kann, ohne das Dokument im Speicher zu behalten. Es wird andere Methoden übertreffen, da keine signifikante Struktur in Bezug auf andere Knoten erzeugt oder aufgelöst wird (z. B. Verknüpfen von Eltern- und Kindknoten). On-Demand-Abfrage-Fähigkeit ist jedoch fast nicht vorhanden; Sie können auf Werte in jedem Knoten reagieren, aber Sie können das Dokument nicht als Ganzes abfragen. Wenn Sie das Dokument ein zweites Mal betrachten müssen, müssen Sie das Ganze noch einmal durchlaufen.

Zum Vergleich: Ein XDocument dauert länger, weil er neue Objekte instanziiert und grundlegende strukturelle Aufgaben ausführt. Es wird auch Speicher proportional zur Größe der Quelle verbrauchen. Im Gegenzug für diese Kompromisse erhalten Sie hervorragende Abfragefähigkeiten.

Es kann möglich sein, die Ansätze zu kombinieren, wie mentioned by Jon Skeet und hier gezeigt: Streaming Into LINQ to XML Using C# Custom Iterators and XmlReader.

Quelle für XDocument laden()

public static XDocument Load(Stream stream, LoadOptions options) 
{ 
    XmlReaderSettings xmlReaderSettings = XNode.GetXmlReaderSettings(options); 
    XDocument result; 
    using (XmlReader xmlReader = XmlReader.Create(stream, xmlReaderSettings)) 
    { 
     result = XDocument.Load(xmlReader, options); 
    } 
    return result; 
} 

// which calls... 

public static XDocument Load(XmlReader reader, LoadOptions options) 
{ 
    if (reader == null) 
    { 
     throw new ArgumentNullException("reader"); 
    } 
    if (reader.ReadState == ReadState.Initial) 
    { 
     reader.Read(); 
    } 
    XDocument xDocument = new XDocument(); 
    if ((options & LoadOptions.SetBaseUri) != LoadOptions.None) 
    { 
     string baseURI = reader.BaseURI; 
     if (baseURI != null && baseURI.Length != 0) 
     { 
      xDocument.SetBaseUri(baseURI); 
     } 
    } 
    if ((options & LoadOptions.SetLineInfo) != LoadOptions.None) 
    { 
     IXmlLineInfo xmlLineInfo = reader as IXmlLineInfo; 
     if (xmlLineInfo != null && xmlLineInfo.HasLineInfo()) 
     { 
      xDocument.SetLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition); 
     } 
    } 
    if (reader.NodeType == XmlNodeType.XmlDeclaration) 
    { 
     xDocument.Declaration = new XDeclaration(reader); 
    } 
    xDocument.ReadContentFrom(reader, options); 
    if (!reader.EOF) 
    { 
     throw new InvalidOperationException(Res.GetString("InvalidOperation_ExpectedEndOfFile")); 
    } 
    if (xDocument.Root == null) 
    { 
     throw new InvalidOperationException(Res.GetString("InvalidOperation_MissingRoot")); 
    } 
    return xDocument; 
} 

// which calls... 

internal void ReadContentFrom(XmlReader r, LoadOptions o) 
{ 
    if ((o & (LoadOptions.SetBaseUri | LoadOptions.SetLineInfo)) == LoadOptions.None) 
    { 
     this.ReadContentFrom(r); 
     return; 
    } 
    if (r.ReadState != ReadState.Interactive) 
    { 
     throw new InvalidOperationException(Res.GetString("InvalidOperation_ExpectedInteractive")); 
    } 
    XContainer xContainer = this; 
    XNode xNode = null; 
    NamespaceCache namespaceCache = default(NamespaceCache); 
    NamespaceCache namespaceCache2 = default(NamespaceCache); 
    string text = ((o & LoadOptions.SetBaseUri) != LoadOptions.None) ? r.BaseURI : null; 
    IXmlLineInfo xmlLineInfo = ((o & LoadOptions.SetLineInfo) != LoadOptions.None) ? (r as IXmlLineInfo) : null; 
    while (true) 
    { 
     string baseURI = r.BaseURI; 
     switch (r.NodeType) 
     { 
     case XmlNodeType.Element: 
     { 
      XElement xElement = new XElement(namespaceCache.Get(r.NamespaceURI).GetName(r.LocalName)); 
      if (text != null && text != baseURI) 
      { 
       xElement.SetBaseUri(baseURI); 
      } 
      if (xmlLineInfo != null && xmlLineInfo.HasLineInfo()) 
      { 
       xElement.SetLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition); 
      } 
      if (r.MoveToFirstAttribute()) 
      { 
       do 
       { 
        XAttribute xAttribute = new XAttribute(namespaceCache2.Get((r.Prefix.Length == 0) ? string.Empty : r.NamespaceURI).GetName(r.LocalName), r.Value); 
        if (xmlLineInfo != null && xmlLineInfo.HasLineInfo()) 
        { 
         xAttribute.SetLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition); 
        } 
        xElement.AppendAttributeSkipNotify(xAttribute); 
       } 
       while (r.MoveToNextAttribute()); 
       r.MoveToElement(); 
      } 
      xContainer.AddNodeSkipNotify(xElement); 
      if (r.IsEmptyElement) 
      { 
       goto IL_30A; 
      } 
      xContainer = xElement; 
      if (text != null) 
      { 
       text = baseURI; 
       goto IL_30A; 
      } 
      goto IL_30A; 
     } 
     case XmlNodeType.Text: 
     case XmlNodeType.Whitespace: 
     case XmlNodeType.SignificantWhitespace: 
      if ((text != null && text != baseURI) || (xmlLineInfo != null && xmlLineInfo.HasLineInfo())) 
      { 
       xNode = new XText(r.Value); 
       goto IL_30A; 
      } 
      xContainer.AddStringSkipNotify(r.Value); 
      goto IL_30A; 
     case XmlNodeType.CDATA: 
      xNode = new XCData(r.Value); 
      goto IL_30A; 
     case XmlNodeType.EntityReference: 
      if (!r.CanResolveEntity) 
      { 
       goto Block_25; 
      } 
      r.ResolveEntity(); 
      goto IL_30A; 
     case XmlNodeType.ProcessingInstruction: 
      xNode = new XProcessingInstruction(r.Name, r.Value); 
      goto IL_30A; 
     case XmlNodeType.Comment: 
      xNode = new XComment(r.Value); 
      goto IL_30A; 
     case XmlNodeType.DocumentType: 
      xNode = new XDocumentType(r.LocalName, r.GetAttribute("PUBLIC"), r.GetAttribute("SYSTEM"), r.Value, r.DtdInfo); 
      goto IL_30A; 
     case XmlNodeType.EndElement: 
     { 
      if (xContainer.content == null) 
      { 
       xContainer.content = string.Empty; 
      } 
      XElement xElement2 = xContainer as XElement; 
      if (xElement2 != null && xmlLineInfo != null && xmlLineInfo.HasLineInfo()) 
      { 
       xElement2.SetEndElementLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition); 
      } 
      if (xContainer == this) 
      { 
       return; 
      } 
      if (text != null && xContainer.HasBaseUri) 
      { 
       text = xContainer.parent.BaseUri; 
      } 
      xContainer = xContainer.parent; 
      goto IL_30A; 
     } 
     case XmlNodeType.EndEntity: 
      goto IL_30A; 
     } 
     break; 
     IL_30A: 
     if (xNode != null) 
     { 
      if (text != null && text != baseURI) 
      { 
       xNode.SetBaseUri(baseURI); 
      } 
      if (xmlLineInfo != null && xmlLineInfo.HasLineInfo()) 
      { 
       xNode.SetLineInfo(xmlLineInfo.LineNumber, xmlLineInfo.LinePosition); 
      } 
      xContainer.AddNodeSkipNotify(xNode); 
      xNode = null; 
     } 
     if (!r.Read()) 
     { 
      return; 
     } 
    } 
    goto IL_2E1; 
    Block_25: 
    throw new InvalidOperationException(Res.GetString("InvalidOperation_UnresolvedEntityReference")); 
    IL_2E1: 
    throw new InvalidOperationException(Res.GetString("InvalidOperation_UnexpectedNodeType", new object[] 
    { 
     r.NodeType 
    })); 
} 
1

Das glaube ich nicht kontinuierlich liest; Das Schöne an der Methode XDocument.Load ist, dass sie XmlReader verwendet, um das XML in eine XML-Struktur zu lesen. Und da Sie jetzt einen Baum erstellt haben, der wahrscheinlich in Ihrem Speicher als Baum gespeichert ist, liest er das Dokument nicht mehr ständig. Es manipuliert den Baum und da es ein Baum ist, werden alle Ihre Lese- und Änderungsvorgänge viel schneller erledigt. Obwohl es IDisposable nicht implementiert, wird es automatisch entsorgt.

6

Es analysiert den eingehenden Datenstrom (ob es aus einer Datei oder einer Zeichenfolge ist egal), wenn Sie Load() aufrufen und dann eine lokale Instanz des Dokuments im Speicher behalten. Da die Quelle alles sein kann (ein NetworkStream, ein DataReader, eine vom Benutzer eingegebene Zeichenkette), kann sie nicht zurückgehen und versuchen, die Daten erneut zu lesen, da sie den Zustand nicht kennt).

Wenn Sie wirklich Geschwindigkeit wollen, XDocument ist nicht der schnellste (obwohl es einfacher zu arbeiten ist), da es zuerst das Dokument analysieren und dann im Speicher behalten muss. Wenn Sie mit wirklich großen Dokumenten arbeiten, ist die Verwendung eines Ansatzes mit System.Xml.XmlReader in der Regel viel schneller, da das Dokument als Stream gelesen werden kann und nichts außer dem aktuellen Element beibehalten werden muss.Diese benchmark zeigt einige interessante Zahlen dazu.

+0

Danke das ist wirklich hilfreich. Ich benutzte es in XNA, und ich denke, ich muss die Anzahl der Male, die ich XDocument.Load aufrufen, abschwächen, da ich es in jedem Objekt mache und ich brauche es nicht. – redcodefinal

+1

Der Benchmark-Link ist tot. – nawfal

+0

Aktualisiert mit neuem Link zum selben Artikel. –