PAMT1/Debug1

Programming Assignment Multi-Threading and Debugging 1
Final: Friday night, May 15 @ 11:59pm
Overview:
The purpose of this mini-assignment is to briefly introduce you to parallel programming and multi-threading;
covering aspects of implementation, benefits and the tradeoffs involved. Provided with skeleton code, you will
fill in the functionality where necessary to calculate the squared sum, min and max of a very large array of
numbers, both in parallel over many threads simultaneously, and sequentially in one. The program will also
record and display the execution time of the calculations using both methods, allowing you to see the relative
performance of each.
NOTE: Due to the number of classes that use ieng9 and the CPU-intensive nature of this assignment, we are
developing and running the multi-threaded programming assignment on the workstations or ieng6! See the
section on Getting the Code for more details on how to set up the repository on the workstation.
You will also be given a chance to practice your debugging skills. We've written a program that will read in
strings from the command line, reverse the string, and determine whether each string is a palindrome (the
same string forward and backward). We think it's close to working, but we didn't have time to debug it. It's your
job to help us track down the bugs and fix it up so we can get to work reversing strings. The debugging
exercise will be completed on ieng9.
In summary:
1) pamt1 will be developed and executed and turned in with your cs30x account on the workstations in the lab
(preferred) or ieng6.ucsd.edu remotely.
2) debug1 will be debugged and executed and turned in with your cs30x account on ieng9.ucsd.edu.
Grading:
PAMT 1
README: 10 points
See "PAMT 1 README File" section
Compiling: 10 points
Using our Makefile; no warnings, more details below.
Correctness: 40 points
Includes both abnormal and normal output, both to the correct destination (stderr vs stdout).
Debug 1
README: 30 points
See "Debug README File" section
Correctness: 10 points
NOTE: If what you turn in does not compile with given Makefile, you will receive 0 points for this assignment.
PAMT 1 Overview
All functions for pamt1 will be written in C++ (really most of it is C, but there are some multi-threading calls that
require C++). We will provide a git repository from which you will clone the starter code. Note that all
development and running of pamt1 must take place on the workstations in the labs or on ieng6. Do not
develop/run this programming assignment on ieng9.
One file (initData.cpp) is provided in its entirety for you, while another (main.cpp) has most of the code, with
small additions that you are required to make. The one file you are expected to create from scratch is
sequentialSquaredSumMinMax.cpp.
The pamt1.h header file, which is included in the git repository you will clone, contains the following:
- the function prototypes
- the definition of struct result
- const int numOfThreads, a constant to determine the number of threads the program will spawn
C routines
int main( int argc, char* argv[] );
void initData( char a[], int size );
struct result sequentialSquaredSumMinMax( char a[], int lo, int hi );
The definition for struct result should be as follows:
struct result {
long long sum;
int min;
int max;
};
Getting the Code for PAMT 1
Unlike previous assignments, there is a git repository already set up -- you just have to clone the repository into
your class account on a workstation in the basement or on ieng6.
Follow the following steps to clone the repository:
Log in to a workstation in the basement and open a terminal
Clone the repository with the following command:
$ git clone ieng9.ucsd.edu:../public/git/pamt1.git
You'll be prompted for a password -- just use the password you normally use to log in to ieng9.
You should now have a directory named pamt1. This will contain the starter code for the project, including
main.cpp, initData.cpp, and pamt1.h. You are responsible for filling in some code in main.cpp and creating the
file sequentialSquaredSumMinMax.cpp.
PAMT 1 Example Output
The program takes a single argument from the command line:
$ ./squaredSumMinMax array_size
This input, array_size, is the size of the array that will be initialized with random values and then have its
squared sum, min and max calculated, sequentially in one thread and then in parallel over multiple.
Below are a few examples (bold indicates user input):
[cs30xyz@ieng6]:pamt1$ ./squaredSumMinMax 8675309
Initializing array (size = 8675309) with random values
. Done
Sequential squared sum, min, max (be patient)
Squared Sum is: 47210683345
Min value is: -128
Max value is: 127
Completed in 0.020940 sec
Async-get parallel squared sum, min, max
Number of threads = 8
Squared Sum is: 47210683345
Min value is: -128
Max value is: 127
Completed in 0.007121 sec
Speed-up: 2.940598
[cs30xyz@ieng6]:pamt1$ ./squaredSumMinMax 987654321
Initializing array (size = 987654321) with random values
......................... Done
Sequential squared sum, min, max (be patient)
Squared Sum is: 5378110202388
Min value is: -128
Max value is: 127
Completed in 2.384418 sec
Async-get parallel squared sum, min, max
Number of threads = 8
Squared Sum is: 5378110202388
Min value is: -128
Max value is: 127
Completed in 0.371566 sec
Speed-up: 6.417213
The output should be fairly self-explanatory. Something important to note is that the first group of results is for
sequential mode (running all of the calculations on the array in a single thread all at once), while the second
group of results is for parallel mode (dividing up the array into subarrays, launching a bunch of threads and
having each one process one subarray, and then combining all of the results back together in the main thread).
You should also note the number of threads in the second group. This output was generated on a workstation
in B240 which supports 8 concurrent threads. This result was determined by calling the
std::thread::hardware_concurrency C++ library function (the numThreads constant from pamt1.h). The
program uses that number to decide how many subarrays to divide the array into for parallel computation.
The speedup is the factor by which parallel computation of the result is faster than sequential. A speedup of 1
would mean that both methods are about equally fast, while a speedup of less than 1 would indicate that the
sequential mode is better, and a speedup of greater than 1 would indicate that the parallel mode is better. As
you can see, for an array of this size on this machine, it was approximately 6.4 times faster to calculate the
solution in parallel. Your output might not be exactly the same as the sample output, but it should look
reasonable and justifiable.
C Modules
1. main.c
int main( int argc, char* argv[] );
The driver of the program. Will take the array_size argument from the command line and convert it to long
using strtol(). It will then malloc() an array of that size and pass a reference to it and the size to
initData() which will populate the array with psuedo-random numbers.
When initData() returns, main() calls gettimeofday() to get the start time, and then calls
sequentialSquaredMinMax() to run the calculations sequentially. Upon return, gettimeofday() is
called again to get the end time. The time difference is then calculated and the results of the sequential run are
printed to stdout. Everything up to this point is given to you in the starter code.
Next, we will partition the array into N separate pieces, where N = # of threads supported by the machine (you
can use the numOfThreads constant defined in pamt1.h) and create N - 1 new threads (the Nth thread is just
main). An array of result structs is then created to store the results of the subarray calculations and a thread is
launched to work on each part of the array, each calling sequentialSquaredSumMinMax() with low
(inclusive) and high (exclusive) array index arguments that define the boundaries that each thread is operating
on, and each returning its results into the array of result structs. You will have to calculate the partition size and
fill in the calls to sequentialSquaredSumMinMax() with the appropriate arguments.
Now the results need to be combined. The program should iterate through the array of struct result, sum
up all of the squared sums and determine the min and max values of the entire array. The results should be
saved in a struct result. It is your responsibility to implement the functionality just described in this
paragraph.
gettimeofday() is called before the parallel computations begin and again after they are completed. The
time difference and the speedup are then calculated, and the results of the parallel run are printed to stdout.
Note: There are several comments marked TODO in main.cpp that show you where you should make
additions/changes. You do not need to (and in fact should not) make changes to main.cpp anywhere except at
these points.
Return Value:
Note, this is already done for you and should not be changed.
2. initData.c
void initData( char a[], int size );
Populates the array passed in from main with random numbers. This entire function is provided for you.
Return Value:
Note, this is already done for you and should not be changed.
3. sequentialSquaredSumMinMax.cpp
struct result sequentialSquaredSumMinMax( char a[], int lo, int hi );
sequentialSquaredSumMinMax() takes a reference to the array defined in main(), and low and high
indexes. It should then iterate through each element, from lo to hi (inclusive of lo, but exclusive of hi) and
calculate the squared sum (the sum of the square of each element), and determine the minimum and
maximum values in sequence. When calculating the square of each element, do not use pow(), since result of
the function call will return a double value instead of an integer, which may later cause unnecessary error due
to rounding off errors.
Return Value:
The results will be saved in a struct result and returned to the caller.
PAMT 1 README File
Along with your source code, you will be turning in a README (use all caps and no file extension for example,
the file is README and not README.txt) file with every assignment. Use vi/vim to edit this file!
Your README file for this and all assignments should contain:
- Header with your name, cs30x login
- High level description of what your program does
- How to compile it (usually just typing "make")
- How to run it (give an example)
- An example of normal output and where that normal output goes (stdout or a file or ???)
- An example of abnormal/error output and where that error output goes (stderr usually)
- How you tested your program
- Anything else that you would want/need to communicate with someone who has not read the writeup
- Answers to questions (if there are any)
Questions to Answer for the README
1. Try running your program with an array size of 500000000 (that's 500 million). If you are using 4 threads,
you should notice a fairly substantial speedup of somewhere between 3.5 and 4. If you are on the workstation
using 8 threads, you should notice a speedup around 6. Now try running it with an array size of 500 -- the
speedup is tiny, less than .01, meaning the sequential computation was much faster than parallel. Why is the
sequential computation so much faster for small array sizes?
2. So parallel processing is better for very large arrays, and sequential is better for small arrays. Try out some
more values for the array size. At approximately what array size do the parallel and sequential calculations
take the same time -- that is, at what size is the speedup approximately 1? (This doesn't have to be exact, just
a ballpark number).
Debugging Exercise Overview
The purpose of this program is to read in all strings from the command line and find the reverse of that string.
The main() function is written in C, and it will find the reverse of the string with help from several assembly
functions.
We provide all of the code to you, but the code doesn't quite compile or work as it should. It is up to you to
track down the bugs and fix them. There are a total of 8 bugs in the source code. You are required to record
ALL of the bugs we placed and the solution for each bug. See the section on Debug README File for more
details.
C routines
int main( int argc, char* argv[] );
Assembly routines
int reverse( char* str );
int findEnd( char* str, char** endPtr );
int swapChars( char* c1, char* c2 );
Getting the Code for Debugging Exercise
For debug1 (the debugging exercise), you will develop on ieng9 as you do for most programming assignments.
However, we will provide you with the buggy code. Simply go to your home directory and copy the whole folder
to your home directory:
$ cd ~
$ cp -r ~/../public/debug1 .
This will provide you with all of the buggy source code. All you have to do is fix the code and detail in a
README file exactly what fixes you had to make to get this code to work properly - line numbers, what the line
looked before and after your fix, etc. Be sure to include a short explanation of how you debugged each
problem.
Debugging Exercise Example Output
The program takes one or more string arguments from the command line:
$ ./reverseString str1 [str2 str3 ...]
Each string will be printed to the screen, then reversed and printed to the screen again. If the string is a
palindrome, the program will print a message saying so. At the end, the program prints the total number of
palindromes found.
Below are a few examples (bold indicates user input):
[cs30xyz@ieng9]:pamt1$ ./reverseString potatoes
Before: potatoes
After: seotatop
You found 0 palindrome(s)
As you can see, the string entered on the command line is printed out, then reversed and printed out again.
Let's see what happens if it's a palindrome...
[cs30xyz@ieng9]:pamt1$ ./reverseString amanaplanacanalpanama
Before: amanaplanacanalpanama
PALINDROME!
After: amanaplanacanalpanama
You found 1 palindrome(s)
This string is the same forward and backward, so we let the user know (with a triumphant PALINDROME!). We
also print the number of palindromes found. (Note: we're not quite fancy enough to deal with spaces and
punctuation, so the well-known palindrome "a man, a plan, a canal: Panama" won't work).
Now let's try entering several strings:
[cs30xyz@ieng9]:pamt1$ ./reverseString abba was a band with some serious wow
factor
Before: abba
PALINDROME!
After: abba
Before: was
After: saw
Before: a
PALINDROME!
After: a
Before: band
After: dnab
Before: with
After: htiw
Before: some
After: emos
Before: serious
After: suoires
Before: wow
PALINDROME!
After: wow
Before: factor
After: rotcaf
You found 3 palindrome(s)
Aside from declaring my love for ABBA, this example shows what happens when several strings are entered
on the command line, including some palindromes.
We can also enclose strings in quotes if we want to include spaces:
[cs30xyz@ieng9]:pamt1$ ./reverseString "I've always wanted to know how to spell
my name in reverse"
Before: I've always wanted to know how to spell my name in reverse
After: esrever ni eman ym lleps ot woh wonk ot detnaw syawla ev'I
You found 0 palindrome(s)
[cs30xyz@ieng9]:pamt1$ ./reverseString "I was a saw I" "semolina is no meal" "--uuu-^U^-uuu---" "four score and seven years ago"
Before: I was a saw I
PALINDROME!
After: I was a saw I
Before: semolina is no meal
After: laem on si anilomes
Before: ---uuu-^U^-uuu--PALINDROME!
After: ---uuu-^U^-uuu--Before: four score and seven years ago
After: oga sraey neves dna erocs ruof
You found 2 palindrome(s)
Debugging C Modules
1. main.c
int main( int argc, char *argv[] );
The only C module of this program. Loops through all command line arguments in argv[]. It first prints the
original string, then calls reverse() on it and prints the reversed string. If the string was a palindrome, a
counter is incremented. When all strings have been read and reversed, a message is printed showing the total
number of palindromes.
Return Value:
Debugging Assembly Modules
1. reverse.s
int reverse( char* str );
The primary purpose of this function is to reverse the character array pointed to by str. It does this by finding
the length of the string and a pointer to the last character of the string (using findEnd()) and then looping
through all characters in the string, simultaneously incrementing the pointer at the front and decrementing the
pointer at the back and swapping the characters. If the characters were the same (as returned by
swapChars()), this function will keep track of that.
Return Value:
If all characters that were swapped were the same, this function will print a message ("PALINDROME!") and
will return 1. Otherwise, it will not print any message and will return 0.
2. findEnd.s
int findEnd( char* str , char** endPtr );
This function has two purposes: to find the length of the string str and to set endPtr to point to the last
character of the string. It does this simply by iterating through the string and checking whether the character is
the null character, keeping a count of how many characters were seen. Once it finds the end of the string, it
stores the pointer to the last character in endPtr.
Return Value:
Returns the length of the str.
3. swapChars.s
int swapChars( char* c1 , char* c2 );
Swaps the values of the two characters pointed to by c1 and c2. Determines if the characters were the same
and, if so, if they were in fact the same character in the string (i.e. the addresses were the same).
Return Value:
If the characters were different, returns 0. If they were the same but the addresses were also the same, returns
1. If they were the same and the addresses were different, returns 2.
Debugging 1 README File
For the debugging assignment only, you do not have to include the usual high level description, how tested,
etc. You will, however, have to list each of the compilation errors you encountered and the fix you made to
correct the error (include the compilation error, the file name, the line number, and the new code).
You will also have to solve several runtime errors. Some of them will be obvious (for example, Bus Error), but
some will involve a little more testing and debugging. Again, for each problem, describe the error and describe
your solution for fixing it, including the file name, line number, and code for your fix.
As a guideline, there should be 2 compilation errors and 6 runtime/functionality problems. Make sure you
locate all of them!! (Note: When we say there are 2 compilation errors, we mean that there are two fixes you'll
have to make, not that there are two errors that are printed to the screen).
Turnin Instructions
Complete Turnin - due Friday night, May 15 @ 11:59 pm
Once you have checked your output, compiled, executed your code, and finished your README file (see
below), you are ready to turn it in. Before you turn in your assignment, you should do make clean in order to
remove all the object files, lint files, core dumps, and executables.
How to Turn in an Assignment
First, you need to have all the relevant files in a subdirectory of your home directory.
PAMT 1
cse30turnin pamt1
In your cs30x account on the lab workstations of ieng6. You will not be able to turn-in pamt1 on ieng9.
DEBUG 1
cse30turnin debug1
In your cs30x acoount on ieng9. You will not be able to turn-in debug1 on ieng6.
Style Requirements
You will be graded for the style of programming on all the assignments. A few suggestions/requirements for
style are given below. Read carefully, and if any of them need clarification do not hesitate to ask.
- Use reasonable comments to make your code clear and readable.
- Use file headers and function header blocks to describe the purpose of your programs and functions. Sample
file/function headers are provided with PA0.
- Explicitly comment all the various registers that you use in your assembly code.
- In the assembly routines, you will have to give high level comments for the synthetic instructions, specifying
what the instruction does.
- You should test your program to take care of invalid inputs like nonintegers, strings, no inputs, etc. This is
very important. Points will be taken off if your code doesn't handle exceptional cases of inputs.
- Use reasonable variable names.
- Error output goes to stderr. Normal output goes to stdout.
- Use #defines and assembly constants to make your code as general as possible.
- Use a local header file to hold common #defines, function prototypes, type definitions, etc., but not variable
definitions.
- Judicious use of blank spaces around logical chunks of code makes your code much easier to read and
debug.
- Keep all lines less than 80 characters, split long lines if necessary.
- Use 2-4 spaces for each level of indenting in your C source code (do not use tab). Be consistent. Make sure
all levels of indenting line up with the other lines at that level of indenting.
- Do use tabs in your Assembly source code.
- Always recompile and execute your program right before turning it in just in case you commented out some
code by mistake.
- Before running turnin please do a make clean in your project directory.
- Do #include only the header files that you need and nothing more.
- Always macro guard your header files (#ifndef N #endif).
- Never have hardcoded magic numbers. This means we shouldn't see magic constants sitting in your code.
Use a #define if you must instead.