profiler.tar.gz attachment, which should contain a directory with all
of the code and any other files required to build your profiler.
For this lab, you will implement a basic sampling profiler for parallel programs. Your profiler won’t use any of the tricks we’ve seen in research papers on parallel profiling; instead, it will use sampling to estimate the time that each thread spends executing each function. This is a conventional approach to profiling that originated with single-threaded programs, but a few widely-used profilers implement this technique for parallel programs as well. If you are interested in implementing a more sophisticated technique (either from a paper or something of your own creation) then you should consider that as a final project extension to this lab.
To get started, download the starter code archive profiler.tar.gz and extract it.
The starter code includes a Makefile and a single source file that implements a very basic profiling tool.
All this tool does is use the Linux perf_event_open system call to set up a hardware performance counter, run a program in a child process, and report the count of hardware events.
The starter code should serve as a good starting point for your implementation, which will require that you use perf_event_open’s sampling features.
We’ll spend some time discussing this implementation in class.
The starter code already implements this command line interface;
just make sure to preserve it as you add to the profiler.
To profile a program, run the profiler executable, followed by the command you would like to profile and any of its command line arguments.
Here is an example execution of the ls command with our basic profiler:
$ ./profiler ls
Makefile profiler profiler.c
Profiler Output:
ref cycles: 1719250
As you can see, the ls program produces its output, followed by a count of hardware events from the profiler.
We’ll talk about what your completed profiler should produce as output in the next section.
Instead of reporting a basic count, your profiler should give statistics on each thread. For example, here is the output for a hypothetical two-thread program.
$ ./profiler test1
Profiler Output:
thread 0
75% do_work
20% main
3% pthread_create
thread 1
85% do_work
15% thread_fn
As you can see, the profiler reports a percentage for each function run in each thread. Once you have your profiler set up to collect samples, you can compute this percentage by counting what share of samples in a given thread fall within each function.
We’ll spend some time in class discussing a technique for converting sample addresses to function names.