Introduction
In this post, I will analyze the ways in which you can add nodes to an XmlDocument
and discuss which way is the fastest, using the .NET 1.1 Framework.
How Can We Add Nodes to an XmlDocument?
There are four ways for you to add nodes to an XmlDocument
:
XmlDocument.InsertAfter
XmlDocument.InsertBefore
XmlDocument.Append
XmlDocument.PrependChild
So, which method is the fastest?
To answer that question, I've created a test program that uses the content of an XML file (11KB) to create an XmlDocument
in memory. Then the user can choose which method he wants to test. The results are measured in nanoseconds.
How Did You Make the Performance Test?
One could easily say that the easiest way to make a performance test is something like this:
DateTime before = DateTime.Now;
DateTime res = before.Subtract(DateTime.Now);
Unfortunately, this code forgets that there are threads running in your system. Our performance test application isn't running all alone and most important, it isn't running all the time.
Hence, a good performance test must take these issues into account.
My performance test uses the following approach:
- There are two threads. One thread is sleeping for a certain period of time. I like to call it the
TimerThread
. While it's sleeping, the other thread is invoking the method to test, over and over again, until the first thread wakes up. - The number of times that the method was invoked is registered in a variable.
- The
TimeThread
executes the following code: (timeThatWasSleeping*1000000)/counter
. The counter variable holds the number of times that the method to be tested was called. - The time in nanoseconds that the
TimerThread
calculates is the time that the method takes to finish.
For minimizing the scheduler interference in the tests, I've give ThreadPriority.Highest
to the thread that is making the test. However if you run the tests multiple times, you can get different values because of this.
Some Test Code
Let's look at some code, shall we?
The application is configured using some static
variables.
static int counter = 0;
static bool run = false;
static int timeMilisec = 5000;
static XmlDocument xmlDoc = new XmlDocument();
static object mon = new object();
static Thread thTimer;
The Main()
method is responsible for setting up the "test thread" and the "timer thread" based on the test that the user chose.
static void Main(string[] args)
{
string cmd = String.Empty;
do
{
lock(mon)
{
if(run)
Monitor.Wait(mon);
}
Console.WriteLine("XmlDocument.AppendChild: a");
Console.WriteLine("XmlDocument.InsertAfter: b");
Console.WriteLine("XmlDocument.InsertBefore: c");
Console.WriteLine("XmlDocument.PrependChild: d");
Console.WriteLine("Quit: q");
Console.Write("Enter your choice:");
cmd = Console.ReadLine();
using (StreamReader sr = new StreamReader("news.xml"))
xmlDoc.LoadXml( sr.ReadToEnd() );
run = true;
Thread thw = null;
switch(cmd)
{
case "a":
thw = new Thread(new ThreadStart(TestXmlDocAppendChild));
break;
case "b":
thw = new Thread(new ThreadStart(TestXmlDocInsertAfter));
break;
case "c":
thw = new Thread(new ThreadStart(TestXmlDocInsertBefore));
break;
case "d":
thw = new Thread(new ThreadStart(TestXmlDocPrependChild));
break;
case "q":
return;
}
Console.WriteLine("\nTest started...");
thTimer = new Thread(new ThreadStart(ThreadTimer));
thw.Priority = System.Threading.ThreadPriority.Highest;
thw.Start();
thTimer.Start();
}
while(!cmd.Equals("q"));
}
For example, the thread that is testing the AppendChild
method tries to call that method the maximum number of times.
private static void TestXmlDocAppendChild()
{
while(run)
{
xmlDoc.AppendChild(xmlDoc.FirstChild);
counter++;
}
}
The timer thread executes the following code:
private static void ThreadTimer()
{
Thread.Sleep(timeMilisec);
run = false;
Console.WriteLine("Result: "+(timeMilisec*1000000)/counter + "ns\n\n");
counter = 0;
lock(mon)
Monitor.PulseAll(mon);
}
The Results
AppendChild
- 38ns InsertAfter
- 12ns InsertBefore
- 13ns PrependChild
- 14ns
Conclusion
The InsertAfter
, InsertBefore
and PrependChild
methods have basically the same performance results. The AppendChild
is a little slower.
One interesting conclusion is the difference between the AppendChild
and the PrependChild
. The first "adds the specified node to the end of the list of child nodes, of this node" and the second "Adds the specified node to the beginning of the list of child nodes for this node." Hence, it's faster to insert nodes to the beginning of the list of child nodes than to the end.
The Challenge
This tests were made with an Intel Pentium M processor 1500Mhz and 512 MB of RAM running Windows XP Pro. I would like to challenge everyone who would like to run these tests to post the results here.
Hope that you can improve your code with this performance test.
History
- 1st September, 2006: Initial post