I'm trying to build a small benchmarking application, which allows the user to measure the execution time and memory usage of a program.
This is the code to measure the execution time:
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;
}
To see if the code is working properly, I decided to implement the "Sieve of Eratosthenes" algorithm. I implemented it twice, one time with a built in stopwatch, and one time without.
Sieve
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);
Moreover, I have a project with an empty main method. My logic was, that when I measure the execution of the "Sieve" and subtract the time of the empty project, the resulting number should be roughly the same as the number measured by the "TimedSieve". So I started measuring...
Empty: 79 milliseconds
Sieve: 53 milliseconds
TimedSieve: 4 milliseconds
Obviously these results seem very fishy:
- The TimedSieve is a lot faster than both the empty project and the Sieve
- The empty project is slower than the Sieve!
Just out of curiosity I also timed the Sieve and the empty project using Powershells "Measure-Command"
Sieve: 25 milliseconds
Empty: 17 milliseconds
Something I noticed was that the fact that the order in which the processes were measured influenced the results, the process which was measured first always lost. I also noticed that moving the start of the stopwatch after the start of the process like this
process.Start();
_stopwatch.Start();
got rid of the aforementioned effect (Empty is now always faster than Sieve) and produced numbers which are a lot closer to the results of the other measurement methods
Empty: 34
Sieve: 42
While trying to solve the problem, I also read that benchmarks should include a "warmup" round, and I decided to benchmark both programs multiple times and take the average to get better results.
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();
}
This got rid of the "empty slower than sieve" effect, which is why I decided to start the stopwatch before the process again.
How can I improve this code to get reliable results? While the numbers have gotten a lot more reasonable, they are still slower than both the Powershell and the TimedSieve measurements.