Assignment 6: Branching Here and There

Overview

The sixth assignment is all about branch predictors and their effectiveness. We’ll give you a number of branch traces from real benchmark programs. You’ll implement a program to simulate how a variety of branch predictors perform on these traces. You’ll then use your program and the given traces to determine the best overall branch predictor.

Programming Languages

You can use either C or C++ for this assignment. You’re allowed to use the standard library of your chosen language as much as you would like to, but you are not allowed to use any additional (non-standard) libraries.

We highly recommend that you opt for C++ because the C++ standard library contains many useful data structures that you would have to write from scratch in C! Don’t waste your time on data structures, spend your time working on the actual problem! However, if you pick C++ you must write real C++ code, not just “C with some classes added” as it were!

We must be able to build your programs (note the plural!) on the Lubuntu 16.04 LTS reference system using make with no additional arguments, so obviously you need to include a working Makefile with your source code. Make sure you use the C/C++ compiler flags posted on Piazza and follow the style guide posted there for your code.

Problem 1: Branch Traces (20%)

You’ll be working with branch traces of real programs for this assignment. Luckily the branch traces come in a simple line-oriented file format:

63de4b 63e120 N
63de53 63de86 N
63de6b 63de2d T
63de47 63de86 N

The first column is the (hexadecimal) program counter for the branch instruction in the respective program. (Note that since the branch traces come from x86 programs, the addresses are not divisible by 4 as they would be for MIPS programs.) The second column is the branch target, the address the program would continue at if the branch was taken. (Note, however, that information about instructions other than branches is not part of the branch trace. Therefore, in the third line above, the branch to 63de2d continues at 63de47 instead.) The third column indicates whether the branch was Taken (T) or Not taken (N).

Here are the four branch traces you’ll use throughout the assignment, all in one archive. Please note that the branch traces are xz-compressed for good reason, so you should probably leave them compressed. (You can use the zxcat tool to pipe an uncompressed trace into your programs.)

In order to “get a feel” for the branch traces, write a simple program that accepts a branch trace on standard input and writes the following information to standard output:

The format for your output must be exactly as follows:

Total 1482017
Forward 1026002
Backward 456015
Forward% 69.23
Backward% 30.77
Forward-distance 636.49
Backward-distance 613.59

(Those are the results for gcc-10M.branch.xz by the way.) Your program for this problem should be called param. Of course you’re required to hand in the source code, not the executable, otherwise we cannot grade your submission.

In your README file, try to characterize each of the branch traces in terms of (at least) these five properties. There might also be other measures you could look at to get a better idea about the data in each trace. In fact, that may very well be essential to find other useful measures…

Problem 2: Branch Prediction (70%)

You are now going to write a simulator for various branch predictors. Your simulator will read a branch trace in the format specified above from standard input and write the results of the simulation to standard output. A number of command line arguments will determine which exact branch predictor your program is going to simulate.

Your program for this problem should be called branchsim. Of course you’re required to hand in the source code, not the executable, otherwise we cannot grade your submission. Here’s how the command line options should work:

Note that slots and historypatterns are both positive integer power-of-twos between 4 and 65536. Also steps is a positive integer power-of-two between 2 and 16. You should do basic error checking of the command line arguments to avoid simulating branch predictors that are not allowed.

After the simulation is complete, your program is expected to print the following summary information to standard output in exactly the format given below:

Total 1482017
Good 856506
Bad 625511
Good% 57.79
Bad% 42.21
Size 0

So you’re printing

The results above are for gcc-10M.branch.xz if we run the trace through the static BTFN predictor. (Static predictors have no state, therefore their size is 0.)

Problem 3: Best Predictor? (10%)

For the last problem, you’ll use the branch traces as well as your simulator to determine which branch predictor has the best overall effectiveness. You should take a variety of properties into account: its mispredict rate, its size, the number of operations it has to perform, etc. In your README describe in detail what experiments you ran (and why), what results you got (and how), and what, in your opinion, is the best branch predictor of them all.

Credits

The branch traces above are derived from more general traces published by Milo Martin back when he was a professor at the University of Pennsylvania. Thank you for leaving the data around, Milo!

Deliverables

Please follow the submission instructions as detailed on Piazza. Make sure that your tarball contains no derived files whatsoever (i.e. no executable files), but allows building all required derived files. Also, be sure to include a Makefile that sets the appropriate compiler flags and builds all programs by default. Include a plain text README file that briefly explains what your programs do and contains any other notes you want us to check out before grading; your answers to written problems should be in this file as well. Finally, make sure to include your name and email address in every file you turn in (well, in every file for which it makes sense to do so anyway)!

Grading

For reference, here is a short explanation of the grading criteria; some of the criteria don’t apply to all problems, and not all of the criteria are used on all assignments.

Packaging refers to the proper organization of the stuff you hand in, following both the guidelines for Deliverables above as well as the general submission instructions for assignments on Piazza.

Style refers to programming style, including things like consistent indentation, appropriate identifier names, useful comments, suitable documentation, etc. Simple, clean, readable code is what you should be aiming for. For C programs, make sure you follow the style guide posted on Piazza!

Design refers to proper modularization (functions, modules, etc.) and an appropriate choice of algorithms and data structures.

Performance refers to how fast/with how little memory your programs can produce the required results compared to other submissions.

Functionality refers to your programs being able to do what they should according to the specification given above; if the specification is ambiguous, ask for clarification! (It also refers to you simply doing the required work, which may not be programming alone.)

If your programs cannot be built you will get no points whatsoever. If your programs cannot be built without warnings using the required compiler options given on Piazza we will take off 10% (except if you document a very good reason). If your programs cannot be built using make we will take off 10%. If valgrind detects memory errors in your programs, we will take off 10%. If your programs fail miserably even once, i.e. terminate with an exception of any kind or dump core, we will take off 10% (for each such case).