用c#测量其他进程的执行时间,结果很奇怪

本文关键字:结果 执行时间 测量 其他 进程 | 更新日期: 2023-09-27 17:49:16

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

这是用来测量执行时间的代码:

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;
}

查看代码是否正常工作,我决定实现"埃拉托色尼筛"算法。我实现了两次,一次是内置秒表,一次没有。

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"的执行并减去空项目的时间时,得到的数字应该与"timmedsieve"测量的数字大致相同。所以我开始测量…

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

显然这些结果看起来很可疑:

  1. timesieve比空项目和Sieve
  2. 都快得多。
  3. 空项目比筛慢!

出于好奇,我还使用powershell"Measure-Command"计时筛和空项目

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();
}

这摆脱了"空比筛慢"的影响,这就是为什么我决定在这个过程之前再次启动秒表。

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

用c#测量其他进程的执行时间,结果很奇怪

在非真实系统操作系统中测量另一个进程的执行时间可能会导致不同的不一致的结果。这就是没有(大部分)时间保证的系统的本质。

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

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

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

  1. 用代码JIT应用程序所花费的时间,而不仅仅是空Main,
  2. 初始化静态类所花费的时间(如果类从未被使用,则不会发生这种情况,例如在Empty中)
  3. 时间用于其他次要和重要的事情。

它们可能是小的时间跨度,但它们仍然以不同于空应用程序的方式增加了完整应用程序的执行时间。


另外,为了使结果更接近PowerShell给出的结果,您可以尝试使用Process。起始时间和进程。在测量完整的应用程序运行时,替换StopWatch的ExitTime:

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

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