使用C#测量其他进程的执行时间,奇数结果

时间:2015-05-14 14:19:42

标签: c# performance benchmarking

我正在尝试构建一个小型基准测试应用程序,它允许用户测量程序的执行时间和内存使用情况。

这是衡量执行时间的代码:

private static Stopwatch _stopwatch;

static void Main(string[] args]
{
    _stopwatch = new Stopwatch();

    //only for this test
    Console.WriteLine(TimeProcess("Empty.exe"));
    Console.WriteLine(TimeProcess("Sieve.exe"));
    Console.ReadKey();
}

private static long TimeProcess(String name)
{
    Process process = new Process();
    process.StartInfo.FileName = name;

    _stopwatch.Reset();
    _stopwatch.Start();
    process.Start();
    process.WaitForExit();
    _stopwatch.Stop();

    return _stopwatch.ElapsedMilliseconds;
}

为了查看代码是否正常工作,我决定实施" Eratosthenes" Sieve of Eratosthenes"算法。我实施了两次,一次使用内置秒表,一次没有。

int[] numbersToTest = Enumerable.Range(0, 1000).ToArray();
int posInArray = 2;

while (numbersToTest[posInArray] != numbersToTest[numbersToTest.Length - 1])
{
    numbersToTest = numbersToTest.Where(x => x % numbersToTest[posInArray] != 0 || x == numbersToTest[posInArray]).ToArray();
    posInArray++;
}

TimedSieve:

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
int[] numbersToTest = Enumerable.Range(0, 1000).ToArray();
int posInArray = 2;

while (numbersToTest[posInArray] != numbersToTest[numbersToTest.Length - 1])
{
    numbersToTest = numbersToTest.Where(x => x % numbersToTest[posInArray] != 0 || x == numbersToTest[posInArray]).ToArray();
    posInArray++;
}
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedTicks);

此外,我有一个空主方法的项目。我的逻辑是,当我衡量" Sieve"并减去空项目的时间,得到的数字应与" TimedSieve"测量的数字大致相同。 所以我开始测量...

Empty: 79 milliseconds
Sieve: 53 milliseconds
TimedSieve: 4 milliseconds

显然,这些结果似乎非常可疑:

  1. TimedSieve比空项目和Sieve
  2. 快得多
  3. 空项目比Sieve慢!
  4. 出于好奇,我还使用Powershells" Measure-Command"

    计划了Sieve和空项目。
    Sieve: 25 milliseconds
    Empty: 17 milliseconds
    

    我注意到的事实是,测量过程的顺序影响了结果,首先测量的过程总是丢失。我还注意到在这个过程开始之后移动秒表的开始就像这样

    process.Start();
    _stopwatch.Start();
    

    摆脱了前面提到的效果(Empty现在总是比Sieve快)并产生的数字更接近其他测量方法的结果

    Empty: 34
    Sieve: 42
    

    在尝试解决问题的同时,我还读到基准应该包括"热身"我决定多次对两个程序进行基准测试,并取平均值以获得更好的结果。

    static void Main(string[] args)
    {
        _stopwatch = new Stopwatch();
    
        //discard results of the first run
        TimeProcess("Sieve.exe");
    
        long sum = 0;
        for (int i = 0; i < 100; i++)
        {
            sum += TimeProcess("Sieve.exe");
        }
        Console.WriteLine(sum/100);
    
        TimeProcess("Empty.exe");
    
        sum = 0;
        for (int i = 0; i < 100; i++)
        {
            sum += TimeProcess("Empty.exe");
        }
        Console.WriteLine(sum/100);
        Console.ReadKey();
    }
    

    这摆脱了&#34;空的比筛子慢#34;效果,这就是为什么我决定再次在这个过程之前启动秒表。

    如何改进此代码以获得可靠的结果?虽然这些数字已经变得更加合理,但它们仍然比Powershell和TimedSieve测量速度慢。

1 个答案:

答案 0 :(得分:0)

在非实际系统OS中测量某个其他进程的执行时间可能会导致不一致的结果。这只是没有(大部分)时间保证的系统的本质。

您已经或多或少地处理了预热(IO和IO缓存相关...)问题,并且通过多次运行使结果在统计上更正确。

TimeOf(Algo) 并不等于 TimeOf(FullAppWithAlgo) - TimeOf(Empty)

已接近,但TimeOf(Algo)不包括:

  1. 使用代码JIT应用程序所花费的时间,而不仅仅是空的Main,
  2. 初始化静态类所花费的时间(that doesn't occur if class is never used, like in Empty
  3. 发生其他次要和重大事件的时间。
  4. 它们可能是小时间跨度,但它们仍然以不同于Empty的方式增加完整应用程序的执行时间。

    另外,为了使结果更接近PowerShell提供的结果,您可以在测量完整的应用程序运行时时尝试使用Process.StartTimeProcess.ExitTime代替StopWatch

    private static long TimeProcess(String name)
    {
        Process process = new Process();
        process.StartInfo.FileName = name;
    
        process.Start();
        process.WaitForExit();
    
        return (process.EndTime - process.StartTime).TotalMilliseconds;
    }
    

    它不会改变空应用程序和完整应用程序之间的差异,但会为每次运行提供更一致的计时,因为您不必等待操作系统通知,这显然发生在应用程序有一段时间后已经结束了。