【问题标题】:Measuring execution time of other processes with C#, odd results用 C# 测量其他进程的执行时间,奇怪的结果
【发布时间】:2015-07-26 05:05:28
【问题描述】:

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

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

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

定时筛:

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. 空项目比筛子慢!

出于好奇,我还使用 Powershells“Measure-Command”对 Sieve 和空项目进行了计时

Sieve: 25 milliseconds
Empty: 17 milliseconds

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

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

摆脱了上述影响(空的现在总是比筛子快)并且产生的数字更接近其他测量方法的结果

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 和 TimedSieve 测量要慢。

【问题讨论】:

  • 您很可能在那里测量磁盘 IO 时间,这就是结果可疑的原因。第一个进程较慢,因为磁盘必须寻找,而第二个进程可能已经被寻找,并且直接进入。不幸的是,没有简单的方法可以消除这种影响。您的第二个解决方案(启动一次,然后运行 ​​100 次)的原因是因为 Windows 将程序缓存到内存中。如果缓存不受干扰(就像在那种情况下那样),那么它可以从缓存中加载程序。
  • 谢谢,这似乎是对第一个结果的合理解释,我怀疑它会进入我的脑海
  • 这是一个经常被忽视的解释,有时我们会忘记(甚至我们所有的程序员)在程序开始时还有其他事情发生。磁盘文件 IO、内存分配、堆分配等。

标签: c# performance benchmarking


【解决方案1】:

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

您已经或多或少地处理了预热(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. 是时候处理发生的其他小事和大事了。

它们可能是很小的时间跨度,但它们仍然会以不同于 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;
}

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

【讨论】:

  • 感谢您提供如此详细的回答!我实际上想使用秒表,因为它的精度很高,但考虑到我的大部分测量值都在毫秒范围以上,我认为您的 DateTime 解决方案非常出色
  • @Aifu 是的,它会降低精度,但对于相对较大的执行时间和多次运行来说,它就足够了。此外,正如我已经说过的,它会提供真正的执行时间,而无需预启动和通知时间,这与 StopWatch 不同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多