【发布时间】: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
显然这些结果看起来很可疑:
- TimedSieve 比空项目和 Sieve 快很多
- 空项目比筛子慢!
出于好奇,我还使用 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