In Sorting Algorithms we took advantage of a few ideas to show how to do basic benchmarking to compare the various approaches.
Stopwatch
gives
us measurements in milliseconds).In this lab, you get your chance to learn a bit more about performance by comparing searches. The art of benchmarking is something that is easy to learn but takes a lifetime to master (to borrow a phrase from the famous Othello board game).
Most of the algorithms we cover in introductory courses tend to be polynomial in nature. That is, the execution time can be bounded by a polynomial function of the data size \(n\). A more accurate measure may also include a logarithm. Examples include but are not limited to:
And there are way more than these shown here. As you progress in computing, you’ll come to know and appreciate these in greater detail.
In this lab, we’re going to look at a few different data structures and methods that perform searches on them and do empirical analysis to get an idea of how well each combination works. Contrasted with other labs where you had to write a lot of code, we’re going to give you some code to do all of the needed work but ask you to write the code to do the actual analysis and produce a basic table.
We’re going to measure the performance of data structures we have been learning about (and will learn about, for lists and sets). For this lab, we’ll focus on:
In the interest of fairness, we are only going to look at the time it takes to perform the various search operations. We’re not going to count the time to randomly-generate the data and actually build the data structure. The reasoning is straightforward. We’re interested in the search time, which is completely independent of other aspects that may be at play. We’re not at all saying that the other aspects are unimportant but want to keep the assignment focus on search.
The experimental apparatus that we are constructing will do the following for each of the cases:
seed
, initialize a random generator that will generate
n
values.n
values.rep
. We will spread out the values looked for
by checking data elements that have indices at a regular interval throughout the array.
The separation is m = n/rep
when rep < n
. The separation is 1, and
we wrap around at the end of the array if rep > n
.To make your life easier, we have put together a project
that refers to all the code for all of the experiments you need
to run. (That’s right, we’re giving you the code for the experiments,
but you’re going to write
some code to run the various experiments and then run for varying sizes of n
.)
The stub file is performance_lab_stub/performance_lab.cs.
Recreate example project performance_lab_stub in your solution as performance_lab, so you have your own copy to modify. You can either
Main
method from binary_searching.cs
to something else (since Xamarin Studio allows only one Main
method in a
project).Here is the code for the first experiment, to test the performance of linear searching on integer arrays:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public static long ExperimentIntArrayLinearSearch (int n, int rep, int seed)
{
Stopwatch watch = new Stopwatch ();
int[] data = new int[n];
Sorting.IntArrayGenerate (data, seed);
int m = Math.Max(1, n/rep);
watch.Reset ();
watch.Start ();
// perform the rep lookups
for (int k=0, i=0; k < rep; k++, i=(i+m)%n) {
Searching.IntArrayLinearSearch (data, data [i]);
}
watch.Stop ();
return watch.ElapsedMilliseconds;
}
|
Let’s take a quick look at how this experiment is constructed. We’ll also take a look at the other experiments but these will likely be presented in a bit less detail, except to highlight the differences:
Stopwatch
instance. We’ll be using this to do the timing.sorting.cs
, as long as you made the lab project
able to reference it. We use the Sorting
class name
to access the method IntArrayGenerate()
within this class. We also take advantage
of this in the other experiments.Reset()
ensures it is zero.rep
times for an item already known to
be in the array.Start()
and Stop()
method calls,
which reflects the actual time of the experiment.Each of the other experiments is constructed similarly.
For linear search and binary search
we use the methods created earlier.
For the lists and the set we use the built-in Contains
method to search.
The list and set are directly initialized in their constructors from the
array data. (More on that in later chapters.)
You need to fill in the Main
method.
The stub already has code to generate a random value for the seed
for
any run of the program. Read through to the end of the lab before
starting to code. A step-by-step sequence is suggested at the end.
Your code must parse command line args
for the parameters rep
and any number
of values for n
. For instance:
mono PerformanceLab.exe 50000 1000 10000 100000
would generate the table shown below for 50000 repetitions
for each of the values of n
: 1000,
10000, and 100000.
In the end you will want to run each experiment
for rep
repetitions and iterate through each different value of n
.
Present the result data in a nice printed right-justified table for all values of n, with a title including the number of repetitions. Print something like the following, with the number of seconds calculated.
Times in seconds with 50000 repetitions
n linear list binary set
1000 ????.??? ????.??? ??.??? ??.???
10000 ????.??? ????.??? ??.??? ??.???
100000 ????.??? ????.??? ??.??? ??.???
The table would be longer if more values of n were entered on the command line. Note that the experiments return times in milliseconds, (1/1000 of a second) while the table should print times in seconds.
Your final aim is to show your TA or instructor the results of a run with a table with at least three lines of data and with n being successive powers of 10, and non-zero entries everywhere. Read on for the major catch!
You will need to
experiment and adjust the repetitions and n
choices.
In order to get all perceptible values (nonzero), you will need a
very large number of repetitions to work for the fastest searches.
Our choice of 50000 in the example is not appropriate with these n
values.
The catch is that without further tweaking, you will only get nonzero
values for all the fastest searches if the slower ones take
ridiculously long.
Because the range of speeds is so enormous, make an accommodation with the
slow linear versions: If rep >= 100
and (long)n*rep >= 100000000
,
then, for the linear and list columns only, time with rep2 = rep/100
instead of rep
,
and then compensate by multiplying the resulting time by (double)rep/rep2
to produce the final table value.
(This multiplier is not necessarily just 100, since the integer division creating
rep2
may not be exact.)
Before making the modification for large numbers, be sure to test with small enough values (though some results will be 0). Once again, you are encouraged to develop this is steps, for example:
rep
,
and separate code to print out all n
values, for any number
of n
values.rep
and one value of n
.rep
and one value of n
.
Keep rep*n
small enough so the linear searches do not take too much time.n
.rep*n
.