Statically Compiled Queries

Introduction

This blog is inactive.
New blog: EricWhite.com/blog

Blog TOCOne of the most important performance benefits that you will note when comparing LINQ to XML and XmlDocument is that queries in LINQ to XML are statically compiled, whereas XPath queries must be interpreted at runtime. You need not do anything to take advantage of this feature of LINQ to XML, but it is helpful to understand this when analyzing the two technologies with regards to performance. This topic explains the difference.

Statically Compiled Queries Explained

The following example shows how to get the descendant elements with a specified name, and with an attribute with a specified value.

The equivalent XPath expression is:

.//Address[@Type='Shipping']

 

[c#]

XDocument po = XDocument.Load("PurchaseOrders.xml");

 

IEnumerable<XElement> list1 =

    from el in po.Descendants("Address")

    where (string)el.Attribute("Type") == "Shipping"

    select el;

 

foreach (XElement el in list1)

    Console.WriteLine(el);

 

The query expression in this example is effectively re-written by the compiler to method syntax. The following example produces the same results as the previous one:

 

[c#]

XDocument po = XDocument.Load("PurchaseOrders.xml");

 

IEnumerable<XElement> list1 =

    po

    .Descendants("Address")

    .Where(el => (string)el.Attribute("Type") == "Shipping");

 

foreach (XElement el in list1)

    Console.WriteLine(el);

 

The System.Linq.Enumerable.Where method is an extension method. The above query is effectively compiled as though it were written as follows:

 

[c#]

XDocument po = XDocument.Load("PurchaseOrders.xml");

 

IEnumerable<XElement> list1 =

    System.Linq.Enumerable.Where(

        po.Descendants("Address"),

        el => (string)el.Attribute("Type") == "Shipping");

 

foreach (XElement el in list1)

    Console.WriteLine(el);

 

This example produces exactly the same results as the previous two examples.

From this, you can see that queries are effectively compiled to statically linked method calls. Combined with the deferred execution semantics of iterators, the query results in code that performs well.

Note These examples are representative of the code that the compiler might write. The actual implementation may differ in detail from these examples, but the performance profile would be the same or similar to these examples.

Execution of XPath Expressions Explained

The following example uses XmlDocument to accomplish the exact same query:

[c#]

XmlReader reader = XmlReader.Create("PurchaseOrders.xml");

XmlDocument doc = new XmlDocument();

doc.Load(reader);

XmlNodeList nl = doc.SelectNodes(".//Address[@Type='Shipping']");

foreach (XmlNode n in nl)

    Console.WriteLine(n.OuterXml);

reader.Close();

 

It produces effectively the same output as the examples using LINQ to XML. (The only difference is that LINQ to XML indents the printed XML, whereas XmlDocument does not.)

The SelectNodes internally must do a certain amount of work every time it is called:

· It must parse the string that contains the XPath expression, breaking the string into tokens.

· It must validate the tokens, making sure that the XPath expression is valid.

· It translates the expression into an internal expression tree.

· It iterates through the nodes, selecting the nodes based on the evaluation of the expression.

This is significantly more work than the work done by the corresponding LINQ to XML query. The specific difference in the work done by each type of query varies based on the type of query, but in general, the amount of work done by the LINQ to XML query will be less than the amount of work done when evaluating the XPath expression.