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.
© Copyright 2024