返回首页

{A}{S0}简介
在这篇文章中,我将分析的方法,您可以在其中添加节点到XmlDocument和讨论哪种方式是最快的,使用。NET 1.1框架。我们如何将节点添加到一个XmlDocument?
有四种方式,你将节点添加到一个XmlDocument:XmlDocument.InsertAfterXmlDocument.InsertBeforeXmlDocument.AppendXmlDocument.PrependChild
那么,哪种方法是最快的呢?
为了回答这个问题,我创建了一个测试程序,它使用一个XML文件的内容(11KB)在内存中创建一个XmlDocument。然后,用户可以选择他想要测试哪些方法。测量结果以纳秒。你是如何进行性能测试?
人们可以轻松地说,性能测试的最简单的方法是这样的:

DateTime before = DateTime.Now;

// do the test...

DateTime res = before.Subtract(DateTime.Now);

不幸的是,这段代码忘记了,在您的系统中运行的线程。我们的性能测试应用程序是不运行的所有单独的和最重要的的,它不运行所有的时间。
因此,良好的性能测试必须考虑这些问题。
我的性能测试,使用下面的方法:有两个线程。一个线程处于睡眠状态的一段时间。我喜欢它TimerThread。虽然它睡觉,其他线程调用该方法来测试,一遍又一遍,直到第一个线程被唤醒。该方法被调用的次数,注册一个变量。TimeThread执行下面的代码:(timeThatWasSleeping * 1000000)/计数器。计数器变量保存要测试的方法被称为次。在纳秒时间TimerThread计算方法需要的时间完成。
在测试中,以减低调度干扰,我已经给ThreadPriority.Highest是使测试的线程。但是,如果你多次运行测试,你可以得到不同的值,因为这个。一些测试代码
让我们来看看一些代码,我们应?
应用程序被配置使用一些静态变量。 {C}
Main()方法是设立quot;测试threadquot;和",定时器threadquot;基于用户选择的测试。
static void Main(string[] args)

{

    string cmd = String.Empty;

    do

    {

        lock(mon)

        {

            if(run)

            Monitor.Wait(mon);// wait for the test to end.



        }

        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"));

}

例如,测试appendChild方法的线程试图调用该方法的最大次数。
private static void TestXmlDocAppendChild()

{

    while(run)

    {

        xmlDoc.AppendChild(xmlDoc.FirstChild);

        counter++;

    }

}

计时器线程执行下面的代码:
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);

}
结果AppendChild - 38nsInsertAfter - 12nsInsertBefore - 13nsPrependChild - 14ns结论
InsertAfter,InsertBefore和PrependChild方法基本上是相同的性能结果。 AppendChild是慢一点。
一个有趣的结论是AppendChild和PrependChild之间的区别。第一quot;添加到指定的子节点,这nodequot结束节点;和第二quot;这node.quot的子节点列表的开头将指定的节点,因此,它的速度更快插入节点的子节点列表的开头,而不是结束。挑战
此测试1500Mhz的英特尔奔腾M处理器和内存运行Windows XP专业版为512 MB。我想挑战大家想运行这些测试后的结果。
希望你能提高你的代码与此性能测试。历史2006年9月1日:战后初期

回答

评论会员:Dilatazu 时间:2011/12/07

31 NS
6 NS
5 NS
6 NS
评论会员:阿洛伊斯克劳斯 时间:2011/12/07
您好,

这是一个有趣的方法来衡量业绩。当你声称的东西是另一个API调用,你需要得到相对稳定的性能数字的两倍快(如索赔:String.Format 1.33倍StringBuilder.AppendFormat快速)。有没有需要切换到不同的线程,并发挥与OS调度肮脏的把戏,以得到这些结果。 DateTime.Now是好的,如果测试时间足够长的时间(2 - 3)。秒表是更好地在这里你可以测量高达纳秒级水平。较短的测试是不准确的,它会成为,因为随机文物。缓解是很容易的运行在一个循环的考验,让噪音取消使用平均值

你在你的文章,数字从运行的变化,您的PC上运行的状态。这是一个迹象,表明你的时间测量值并不稳定,不应该发表反正。 Someting不可复制的,是没有价值的,因为你是怎么想证明你的号码是正确的?有多大的偏差,如果你让测试运行100倍?

你,
阿洛伊斯克劳斯

评论会员:carlopagliei 时间:2011/12/07
我真的无法弄清楚为什么比启动和停止时间更准确的计算基于两个线程的执行时间的方法是:毕竟CPU额外的工作,是在两种情况下相同。只有这样,我看到有一个良好的价值的运行一遍又一遍的测试和计算平均...这使您可以比较哪种方法执行速度更快,但没有得到真正的执行时间,因为它显然是由CPU的额外活动的影响。

PACO
评论会员:Bcoelho 时间:2011/12/07
"开始和停止时间"的方法不关心线程切换需要的时间。
的时间,你可以使用该方法,是不是真的,测试所消耗的时间,因为与此同时,其他线程运行。
使用两个线程,其中一个可以运行在最高优先级,最小化线程切换,其他线程,就是睡在测试过程中,停止"工人"到时的线程。通过这种方法,重点是测试CPU的。难道你不同意吗?

不过,我同意你的你的最后一点。我应该有多个测试结果的平均值。我会更新。
评论会员:carlopagliei 时间:2011/12/07
我不同意... ...这里的一些考虑:
- 我的电脑上,我已经约200个活动线程,所以很多的上下文切换是外部的应用程序
- GC线程始终是积极的,并同时GC发生的所有托管线程停止
- 你给托管线程的优先是动态的,它不是固定不变的。你不能在一个给定的的时间确定的优先事项。
- 在一个现实世界的应用程序,我将永远不会使用这种方式... ...所以我更愿意得到的时间与现在()方法
对于那些我认为不能准确的时间测量,但您的测试仍然是有效的,因为什么是真正有趣的主要原因是比较amond方法执行速度。

PACO
评论会员:Bcoelho 时间:2011/12/07
carlopagliei写道: - 我的电脑上,我已经约200个活动线程,所以很多的上下文切换是外部的应用程序

如果您运行任务管理器,然后运行我的测试应用程序,你会看到,该处理器的使用将火99%。 remaning的1%是无法控制。有系统的用户和系统线程。

carlopagliei写道: - GC线程始终处于激活状态,而发生GC所有托管线程停止

如果您签出SSCLI(微软的共享源实现的CLI),文件fjit.cpp包含了一个有趣的方法称为FJit::jitCompile (...).附近的方法的评论说,其jits的方法。如果成功,返回的字节数实时编译,否则返回0。在该行3828他们说:"squirel了返回值,这是安全的,因为GC是不可能发生的,直到我们完成收尾"

此外,GC线程可以始终处于活动状态,但并不总是运行。如你所知,只发生在第0代垃圾收集是完整的。

你可以看到在文件gcee.cpp行445:


/ /检查,如果我们已经准备好去暂停。 如果(pCurThread pCurThread> CatchAtSafePoint())
  ; {
 0; _ASSERTE(pCurThread - > PreemptiveGCDisabled ());{ BR}   ; pCurThread - > PulseGCMode(); / /进入暂停自己
。 }
ELSE
& #160; {
__SwitchToThread(0); / /重试前等待一小会儿,
}

carlopagliei写道: - 你给托管线程的优先级是动态的,它是不固定的。你不能在一个给定的的时间确定的优先事项。
我同意,但我没有说我可以在一个给定的的时间确定的优先。
我说的是,"使用两个线程,其中一个可以运行在最高优先级,最小化线程切换"。

评论会员:carlopagliei 时间:2011/12/07
所以我们同意,你可以做你想要的每一个优化或技巧,你想,但确切的数字,你会得到从测试将永远是准确的,因为有太多的随机变量
。至于说比较仍然是确定"的原因,我们可以考虑的随机始终是相同的。


PACO
评论会员:Bcoelho 时间:2011/12/07
说是另一个独立运行完整的CPU,显然被测试的方法,将执行较少的线程/进程。但定时器线程会报告的同时,不管是什么。那么,如何你的方法解决这个吗?

塞巴斯蒂安

情报共享情报的平方。
网址:{A3}