Traces of Memory
This assignment is to write a program which reads traces of
memory references produced by running programs and computes statistics
on their behavior. Here's mine, with all the the reporting turned on:
[tom@localhost meas]$ ./stats ccrdr.txt dist=100 tau=10000
=== ccrdr.txt ===
8190844 rfs to 526 pages, 15571.9 ref/pg, 1 to 1788422. 3.80174refs/byte,
By type: IF 72.7744% DF 18.8588% ST 8.36685%
Within 100 bytes: 68.665% instruction fetches, 11.8536% data refs
Working set, tau = 10000. Av. 48.9975 pages, from 4 to 136.
0.13204% entered working set.
The meaning of the runes is as follows:
./stats ccrdr.txt dist=100 tau=10000
The command line gives the name of the file of traces to analyze,
ccrdr.txt. It's a small C++ compile. The other parameters turn on
certain additional measurements, as described below. The trace file
is plain text, this one containing 8190844 memory references (more on format
later).
8190844 rfs to 526 pages,
As represented in the trace, the program produced 8190844 memory references.
These references were to 526 distinct 4k pages. The page size can be set
by adding
the command line parameter pagebits=N, but this run uses the default
of 12.
15571.9 ref/pg, 1 to 1788422. 3.80174 refs/byte
Each of the 526 pages was referenced and average number of 15571.9 times,
and the numbers of references to pages ranged from a minimum of 1 to
a maximum of 1788422. The references work out to 3.80174 references per byte
of the total page space. That's just
8190844÷(4k×526) in this case. It gives a measure of how much use
the system gets out of the data it brings into memory. It tends to increase
when a smaller page size is used.
By type: IF 72.7744% DF 18.8588% ST 8.36685%
The references are classified by type, instruction fetch, data fetch, and
data store. This gives the percentages of each type within the trace.
Within 100 bytes: 68.665% instruction fetches, 11.8536% data refs
This is a measure spacial locality: How close one reference is to another.
Within the instruction fetches, 68.665% are within 100 bytes of the previous
instruction fetch, and 11.8536 data operations are within 100 bytes of the
previous one. The distance is specified by the dist=100 flag, which
also enables the measurement.
Working set, tau = 10000. Av. 82.1262 pages, from 7 to 217.
When the parameter tau is given, it enables working set analysis.
The program maintains the working set of the previous 10000 references,
and computes statistics on the size of the working set at each reference.
The working set averages 82.1262 pages over each reference, ranging in
size from 7 to 217. (The program does not commence counting until after
the first τ references, so here the working set size is first observed
on the 100001st reference, then all following.)
0.294419% entered working set.
The working set calculation also computes the percentage of references
(after the first τ) which must be added to the set. This is a measure
of the stability of the working set.
What To Do
First, download the starting code as
start.tgz or
start.zip.
This contains a class to read trace files, and a test program
rprint.cpp which simply reads and prints a specified file.
It will also serve quite will as a starting place for your program.
It already opens the trace file, and loops through the references therein.
You will also need some trace files.
The linked page contains several for download, and more information
about where they come from and how to make your own.
Unpack the start and get the reader to compile. On a Unix-like
environment, just put the files in a directory, go there, and say
make. For most IDEs, including CodeBlocks, unpack the files to
some convenient place, then create a project under your IDE and add
the files to it. You should then be able to build the rprint executable.
Once you have that
working, make a copy of
rprint.cpp under another
name (I called mine uncreatively
stats.cpp) to work on. You should
be able to compile it also. If you're using my make file, say
“
make stats”. (If you called your file something else, you'll
need to edit the Makefile. An IDE will probably just follow whatever
name you give it.)
Then modify it to build the statistics shown above:
- The number of references
- The minimum, maximum and average numbers of references to a page.
- The number of references per the total number of bytes in all the
referenced pages.
- The percentages of the references in each of the classes instruction
fetch, data fetch, data store.
If a value for dist is given, print the percentage of references
(after the first one) which are within that many bytes of the previous
reference of the same type. Here, types are only instruction or data,
where data stores and fetches are treated as the same type, data.
The working
set statistics are optional, but come with a 10pt
bonus, if you make them work. To do so, if the tau parameter
is given, it is a window for the working set, as discussed in the
book. The program should maintain the current working set over that
window, updating for each reference. Starting with the
τ+1th
reference, collect and report the following statistics:
- The minimum, maximum, and mean size of the working set, in
pages. (No, the size
is not the same as τ. Check the definition again.)
- The percentage of references which add a new page to the working set.
More details follow
Command Line Parameters To Support
The program accepts a number of parameters on the command line.
The first one is always the name of the trace file to read, and the
ones which follow set parameters for the computation. The presence of
some parameters also enable certain counts. In a command environment, you
simply type these on the command line when running the program. An IDE
will generally have some place buried in the menus where you can enter
the command line parameters. In CodeBlocks, look under Project/Set Programs's Arguments, once you have created a project.
The existing rprint.cpp contains code to read the command line
arguments, and process a few parameters itself, mostly as examples:
If you specify limit=N, the program stops after that many references.
If you say count, the program will report the number of references
printed.
Do have a look at it to see how this is done.
Implement the following parameters:
pagebits=b
Specify the number of bits on the right of each address which constitute
the page offest (the rest are the page number). The default is 12, which
specifies the usual 4K page, since 212=4k.
rpt=n
In addition to reporting the statistics at the end of the trace, report
the current values after each n references. This does not
imply a reset; simply print the current accumulation and continue to
accumulate.
dist=n
This activates the temporal locality measurement, and specifies the
distance. The distance is in bytes.
tau=t
This activates the working set measurements, and specifies the window
for computing the working set. Measurement is in references.
Reading Traces
The trace reader read method returns each reference as a
Reference. This small class is defined in rdr.h, and
is really more of a struct, having all public fields.
Each reference has a type, InstrFetch
DataFetch or Stor. The class also defines a type
None, which is only used for invalid objects, not references
from the program trace. It also has a (virtual) address, and size
in bytes. These traces are from a Pentium or x64 architecture, so
the sizes vary widely. The addresses are typed as unit64_t,
defined in the header cstdint. This is an alias for a an
unsigned integer type having at least 64 bits. Be careful doing
arithmetic with the unsigned type in C. In particular, subtraction
will never give you a negative number. If you subtract a larger
unsigned from a smaller one, you just silently get the wrong answer.
Ahhh, C.
Consult rprint.cpp to see how the object can be handled.
Since all fields are public, there are no getters.
Refer directly to the data fields.
Meaning of Close
If a distance is specified, you need to check whether each reference
(after the first) is “close” to the previous one of the same type,
and count those so you can compute the percentage. More specifically, that
means that any byte covered by the first one is the address of the other,
in either direction. In the picture to the right, the reference B
is close to reference A, since it falls in the area pictured. Reference B
is close above A if it is greater than or equal Address A and less
than Address A + Size A + distance.
To test for closeness, check this in each direction.
About The Working Set
The working set computation involves maintaining the set of page
numbers referenced in the last τ references. Sets are easy; C++ has
one in the standard libraries, so you can always add the latest reference.
The hard part is knowing if you must remove a references when it passes
beyond the τ range.
There may, or may not, be other reference to the same page
remaining within range. And you have to do this efficiently, because you have
to update your structure for each reference, and the program already takes
a lot of time.
I finally used a rotating buffer of size τ to retain the
references in range, and a map to keep the counts of how often each
appears there, so I don't have to scan the array. Increment the count
for each page entering the buffer, and decrement when each leaves, to
avoid scanning the buffer.
Where Do Traces Come From, Anyway?
They come from using the valgrind
tool with a plugin called
(for some reason) lackey. Lackey seems to be pretty simple, intended mainly
as an example of writing plugins for Valgrind.
Valgrind is available for Linux and Mac;
there's no Windows port.
If you have a supported platform, and have acquired valgrind, you can
create a trace with this command:
valgrind --tool=lackey --trace-mem=yes command 2> filename
This will run any command under the tracer, and direct the standard error
output to filename. The trace is written to the error stream. The command
will generally run quite slowly, and the trace file will be quite large.
I've also had trouble getting any large GUI program to work; I suspect
there is some issue that comes from slowing things down so much, but I
don't know for certain if that's the problem.
Submission
When your program works, and is properly commented and indented,
submit it over the web using
this form.