Some hard numbers about XmlDocument, XDocument and XmlReader (x86 versus x64)

In my earlier blog I had summarized from experience and material on the web that was born out by my own observations. I decided to construct a very simple demo program as shown at the bottom of this blog. I loaded the same file with each approach and counted the nodes in a Xml file. The box was quad-x64 core with 12 Gigs of ram.

 

The results are very information for guidance.

x86 Memory (K) Msec Memory Delta
No Op 3196 1.95  
XmlDocument 43912 1221.70 40716
XDocument 34700 836.90 31504
XmlReader 3696 299.80 500
x64 Memory (K) Msec Memory Delta
No Op 3184 0.98  
XmlDocument 74900 1076.20 71716
XDocument 48252 782.23 45068
XmlReader 5280 301.76 2096

 

First, as expected x64 was faster by about 20% – not a huge amount. What was very interesting is that the memory consumption – from 43% more to 319% more; in other words a web application that uses XML heavy that runs in 3Gigs of memory if compiled as x86 could require 12Gigs of memory if compiled as x64 for perhaps a 20% increase of performance. It makes more sense to run two instances of the web site compiled as x86 and double the performance using only 6 Gigs of memory.

 

As expected,  XmlReader was the best performing:

  • A 3 Gig Xml-Heavy web application using XmlDocument would run THREE times faster using only 100 megs of memory in X64 --- yes,  1/35 of the memory needs. In x86, it’s more shocking: only 36 megs of memory would be needed!
  • For XDocument, the magnitude is not as severe, but still a magnitude different!

If you wish to try the code, here is the Console app code. The memory used is obtained by looking at Task Manager(I did not clean up objects intentionally so task manager would show the peak memory).

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Xml;
using System.IO;
using System.Text;

namespace XmlPerformance
{
class Program
{
static void Main(string[] args)
{
    int option=0;
    int cnt = 0;
    FileInfo xmlFile=null;
    foreach (var arg in args)
    {
        if (arg.Length == 1)
        {
            int.TryParse(arg, out option);
        }
        else if(xmlFile==null || ! xmlFile.Exists)
        {
            xmlFile = new FileInfo(arg);
        }
    }
    if (xmlFile.Exists)
    {
        DateTime start = DateTime.Now;
        switch (option)
        {

            case 1:
                XmlDocument dom = new XmlDocument();
                dom.Load(xmlFile.FullName);
                cnt = dom.SelectNodes("//*").Count;
                break;
            case 2:
                XDocument doc = XDocument.Load(xmlFile.FullName);
                
                foreach (XElement e in doc.Descendants())
                {
                    cnt++;
                }
                
                break;
            case 3:
                XmlReader rdr = XmlReader.Create(xmlFile.FullName);
                while (!rdr.EOF)
                {
                    rdr.Read();
                    if (rdr.NodeType == XmlNodeType.Element)
                    {
                        cnt++;
                    }
                } 
                break;
            default:
                break;
        }
        DateTime end = DateTime.Now;
        TimeSpan elapse=end-start;                
        Console.WriteLine(String.Format("{0} msec for {1} elements. File Size is:{2}",elapse.TotalMilliseconds,cnt,xmlFile.Length));
        Console.ReadKey();
    }
}
}
}

A simple cmd file was used:

XmlPerformance 0 L:\PortableDrive\sussen\QuickTest\bin\Debug\Results.xml
XmlPerformance 1 L:\PortableDrive\sussen\QuickTest\bin\Debug\Results.xml
XmlPerformance 2 L:\PortableDrive\sussen\QuickTest\bin\Debug\Results.xml
XmlPerformance 3 L:\PortableDrive\sussen\QuickTest\bin\Debug\Results.xml

Comments

  1. A good test involves warm up, processor afinity/priority change, RELEASE config and a Run Without Debug start. Here's the right test code:

    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Threading;
    using System.Xml;
    using System.Xml.Linq;

    namespace XmlPerformance
    {
    class Program
    {
    static FileInfo _xmlFile;
    //static Stream _stream;

    static void Main(string[] args)
    {
    Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2); // Uses the second Core or Processor for the Test
    Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime; // Prevents "Normal" processes from interrupting Threads
    Thread.CurrentThread.Priority = ThreadPriority.Highest; // Prevents "Normal" Threads from interrupting this thread

    _xmlFile = new FileInfo(@"C:\file.xml");
    //_stream = File.OpenRead(@"C:\file.xml");

    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine("Warmup");
    var stopWatch = new Stopwatch();
    stopWatch.Start();

    while (stopWatch.ElapsedMilliseconds < 1200) // A Warmup of 1000-1500 mS
    // stabilizes the CPU cache and pipeline.
    {
    WarmUp(TestXmlDocument);
    WarmUp(TestXDocument);
    WarmUp(TestXmlReader);
    }
    stopWatch.Stop();


    for (int i = 0; i < 5; i++)
    {
    Console.WriteLine();
    Console.WriteLine("Test set " + i);
    Console.WriteLine();
    RunTest("Testing XmlDocument...", TestXmlDocument);
    RunTest("Testing XDocument...", TestXDocument);
    RunTest("Testing XmlReader...", TestXmlReader);
    Console.WriteLine();
    }

    Console.ReadKey();
    }

    private static void TestXmlDocument()
    {
    var dom = new XmlDocument();
    dom.Load(_xmlFile.FullName);
    int i = dom.SelectNodes("//*").Count;
    }

    private static void TestXDocument()
    {
    int i = 0;
    XDocument doc = XDocument.Load(_xmlFile.FullName);

    foreach (XElement e in doc.Descendants())
    i++;
    }

    private static void TestXmlReader()
    {
    int i = 0;
    XmlReader rdr = XmlReader.Create(_xmlFile.FullName);

    while (!rdr.EOF)
    {
    rdr.Read();
    if (rdr.NodeType == XmlNodeType.Element)
    {
    i++;
    }
    }
    }

    private static void RunTest(string header, TestMethod testMethod)
    {
    var stopWatch = new Stopwatch();

    if (header != null)
    Console.WriteLine(header);

    stopWatch.Start();
    testMethod();
    stopWatch.Stop();

    if (header != null)
    {
    Console.WriteLine("Executed in {0} ticks", stopWatch.ElapsedTicks);
    Console.WriteLine();
    }
    }

    private static void WarmUp(TestMethod testMethod)
    {
    RunTest(header: null, testMethod: testMethod);
    }

    delegate void TestMethod();
    }
    }

    ReplyDelete
  2. With the code i posted, the performance difference is smaller

    ReplyDelete
  3. You know about the differences between SAX and DOM?

    ReplyDelete

Post a Comment

Popular posts from this blog

Yet once more into the breech (of altered programming logic)

Simple WP7 Mango App for Background Tasks, Toast, and Tiles: Code Explanation

How to convert SVG data to a Png Image file Using InkScape