2012-12-22 18 views
15

Kiedy tworzysz nowy XDocument przy użyciu XDocument.Load, czy otwiera on plik XML i zachowuje lokalną kopię, czy też ciągle czyta dokument z dysku twardego? Jeśli ciągle czyta, czy istnieje szybszy sposób na analizowanie kodu XML?Wydajność XDocument

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

Odpowiedz

10

Istnieje kilka pomiarów do rozważenia:

  1. Liniowa prędkość przejścia (np odczyt/ładowanie)
  2. Na żądanie prędkość zapytanie

Aby odpowiedzieć na natychmiastowe pytanie:XDocument używa XmlReader do załadowania dokumentu do pamięci poprzez odczytanie każdego elementu i tworzenie odpowiednich instancji XElement (patrz poniższy kod). Jako taki powinien być dość szybki (wystarczająco szybki dla większości celów), ale może pochłaniać dużą ilość pamięci podczas analizowania dużego dokumentu.

Surowy XmlReader to doskonały wybór do przemierzania, jeśli twoje potrzeby są ograniczone do tego, co można zrobić bez zatrzymywania dokumentu w pamięci. Będzie przewyższać inne metody, ponieważ żadna istotna struktura nie jest tworzona ani rozwiązywana w odniesieniu do innych węzłów (np. Łączenie węzłów nadrzędnych i potomnych). Jednak zdolność zapytania na żądanie prawie nie istnieje; możesz reagować na wartości znalezione w każdym węźle, ale nie możesz wysyłać zapytania do dokumentu jako całości. Jeśli zajrzysz do dokumentu po raz drugi, musisz ponownie przemierzyć całą sprawę.

Dla porównania, przetransportowanie XDocument trwa dłużej, ponieważ tworzy nowe obiekty i wykonuje podstawowe zadania konstrukcyjne. Spowoduje to również zużycie pamięci proporcjonalnej do wielkości źródła. W zamian za te kompromisy zyskujesz doskonałe możliwości wyszukiwania.

Możliwe jest łączenie podejść, takich jak mentioned by Jon Skeet i pokazanych tutaj: Streaming Into LINQ to XML Using C# Custom Iterators and XmlReader.

źródło XDocument load()

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

I nie sądzę, że ciągle czyta; fajną rzeczą w metodzie XDocument.Load jest to, że używa ona XmlReader do odczytu XML do drzewa XML. Odtąd właśnie stworzyłeś drzewo, które najprawdopodobniej jest przechowywane w Twojej pamięci jako drzewo, które nie czyta już dokumentu w sposób ciągły. Manipuluje drzewem, a ponieważ jest drzewem, wszystkie twoje czytanie i modyfikacje są wykonywane dużo szybciej. Mimo że nie implementuje ona IDisposable, jest automatycznie usuwana.

6

Spowoduje to zanalizowanie przychodzącego strumienia (bez względu na to, czy pochodzi on z pliku lub ciągu znaków) po wywołaniu Load(), a następnie zachowa lokalną instancję dokumentu w pamięci. Ponieważ źródłem może być cokolwiek (może być NetworkStream, DataReader, ciąg znaków wprowadzony przez użytkownika) nie mógł wrócić i spróbować ponownie odczytać dane, ponieważ nie wiedziałoby ono o jego stanie (strumienie są zamykane itp.).

Z drugiej strony, jeśli naprawdę zależy Ci na szybkości, XDocument nie jest fasteterem (wszystko, co jest łatwiejsze w obsłudze), ponieważ najpierw musi przeanalizować dokument, a następnie zachować go w pamięci. Jeśli pracujesz z naprawdę dużymi dokumentami przy użyciu podejścia z System.Xml.XmlReader jest zwykle o wiele szybszy, ponieważ może czytać dokument jako strumień i nie musi przechowywać niczego poza bieżącym elementem.Ten benchmark pokazuje kilka interesujących danych na ten temat.

+0

Dzięki temu jest to bardzo pomocne. Używałem go w XNA i myślę, że potrzebuję zmniejszyć ilość razy, które nazywam XDocument.Load, ponieważ robię to w każdym obiekcie i nie muszę tego robić. – redcodefinal

+1

Link testowy nie działa. – nawfal

+0

Zaktualizowany nowym łączem do tego samego artykułu. –