CSc 422 Assignment 4

Traces of Memory

Assigned
Due

Nov 15
65 pts
Dec 12
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.