GRID: Introduction
For this assignment you will be experimenting with different granularities of locking on a data structure that is shared among many threads.
The data structure you will be experimenting with is a simple n x n array of numbers. The invariant is that the sum of all entrees in the grid remain the same.
n will range from 1 to 10.
You will have many threads executing in your program.
The life cycle of each thread is:
for i = 1 to NO_SWAPS
cell1 = random cell
cell2 = random cell
swap(cell1, cell2)
end for
In order to allow more than 1 thread to execute this code concurrently on the data structure, there must be some locking to prevent corruption of data.
Since this is a gridded data structure, it is natural to think of four different granularities of locking:
- GRID level - obtain a lock for the entire grid before doing the swap. Only one thread can access the grid at a time.
- ROW level - obtain a row level lock for both of the cells the thread is going to swap. Only one thread can access any single row at a time, but this allows more threads to access the grid simultaneously.
- CELL level - obtain a cell level lock for both of the cells the thread is going to swap. Only one thread can access any single cell at a time. This is the finest level of granularity.
- NONE - for comparison. Expect corruption.
Program usage:
gridapp gridsize numthreads locking_granularity
Examples:
gridapp 8 3 -cell
- executes gridapp
with an 8 x 8 array, 3 threads running, with cell level locking
gridapp 10 100 -grid
- 10 x 10 array, 100 threads running, grid level locking
gridapp 10 100 -row
10 x 10 array, 100 threads running, row level locking
gridapp 10 100 -none
10 x 10 array, 100 threads running, *** NO
LOCKING *** this is for testing, and data
should be corrupted at the end.
Program output:
- Print initial grid
- Print sum of all cells in initial grid
- Execute life cycle of all the threads
- Print final grid
- Print sum of final grid
- Print elapsed time in seconds
The initial and final sums should match if there is no data corruption
(i.e. the synchronization code is correct).
GRID: Objectives
- Practice thread programming in Linux
- Comprehensive understanding of the synchronizatin issues in thread programming
- Properly impose the locking mechanism to parallel computing
- Learn the scenarios of race condition and deadlock
- Find the solutions to resolve the deadlock
GRID: Steps for Your Assignment
- Examine the skeleton code we have provided:
gridapp.c in
gridapp.tar.gz
Note that grid initialization, printing, and summing functions are provided.
We have also provided the thread starting code and the thread's function body.
Note thesleep(1)
hidden in the middle of the swap operation. You will investigate this later in the assignment. - Compile (note the -lpthread)and execute this code, examine results:
gcc -o gridapp gridapp.c -lpthread
./gridapp 10 1 -none
- We have provided everything you need except for the locking data
structures and locking code. (Don't forget threads_left!)
You should be able to fill in the necessary code using pthread synchronization primitives like pthread_mutex_lock/pthread_mutex_unlock or pthread_cond_wait/pthread_cond_signal. You will need to decide how many locks to use and what each one protects.
NOTE THAT YOU WILL NEED A DEADLOCK PREVENTION SCHEME. You can add code elsewhere if necessary like global variable declarations, etc.
- Verify that the concurrency is working correctly:
Run
All of these should result in the same initial and final sum../gridapp 10 10 -cell ./gridapp 10 10 -row ./gridapp 10 10 -grid
There should also be significant differences in time elapsed. Note these results. - Now, try running without locking again
Run./gridapp 10 10 -none
The initial and final sum should be different.
Also note how the elapsed time compares to the other cases. - Experiment with different grid sizes, thread counts, and
granularities of locking.
- At grid size 10, graph the time elapsed on the
y axis and thread count from 1 to 10 on
the x axis. Make this graph for all four types of
granularity (-none, -cell, -row, -grid).
The total tests are 40(10*4).
Each test should be run 3 times and recorded.
And calculate their means and variance,
then use the means as the final results to put into the graph.
(Experiment: What is the maximum number of threads you can create with pthread_create failing?)
- Now, leave the thread count at 10, graph the time elapsed on the y axis and the grid size from 2 to 10 on the x axis. Make this graph for all four types of granularity. The total tests are 36(9*4). Each test should be run three times and recorded, calculating its means and variance, then use the means as the final results to put into the graph.
- At grid size 10, graph the time elapsed on the
y axis and thread count from 1 to 10 on
the x axis. Make this graph for all four types of
granularity (-none, -cell, -row, -grid).
The total tests are 40(10*4).
Each test should be run 3 times and recorded.
And calculate their means and variance,
then use the means as the final results to put into the graph.
- Answer the questions about:
What can you conclude about how grid size, number of threads, and locking granularity affect performance?
What general results can you conclude from this exercise? When does fine granularity locking have the biggest effect? What costs does it impose? - Experiment with no locking. Without any modifcations, you shouldn't get any integrity violation with 1 thread. Why? With a big enough grid and few enough threads, you might avoid data integrity violations. Why?
- Note the
sleep(1)
command in the swapping code.- Why do you think it is there? Try commenting it out or moving it to a different location in the block of swapping code. Try moving it to just before the first line of the swapping code but after you have obtained your lock.
- What effects does this have on performance and correctness of final sum? Try experimenting with all levels of granularity on a 10x10 grid with 10 threads.
- Anything surprising?
- What can you say about why this
sleep(1)
is there and where it is placed? - If we didn't have this
sleep(1)
there, how would this change the assignment? - What does this say about when multithreading and synchronization make sense?
GRID: Available Materials
-
Source Code: Here is the source code where you can start for this assignment. But it doesn't include synchronization mechanism yet.
-
Experiment Scripts: Here are the scripts that will help you to collect those experiment data in this assignment. You need to analyze the imact of how the locking granularity impacts the thread numbers and grid size.
- You can come up with your scripts or run manually at your convenience.
- Notice that running the tests may take more time than coding.
- Make sure your code works correctly before starting the tests and analysis.
GRID: Submission
What to submit:
Submit the following to Moodle, a single tar.gz file containing:
- README
README file should contain your names and CUid and a guide to the rest of the files. Describe known problems with your implementation and describe any optional features implemented. - gridapp.c
- Writeup and Data
You may submit your Writeup and data as a PDF document with analysis and graphs and then files with your data (Excel perhaps). -
Discuss your locking and deadlock prevention strategies
I recommend breaking one of the 4 necessary conditions for deadlock not implementing the banker's algorithm!
-
8 graphs or maybe 2 graphs with 4 lines each. Graphs should clearly
label axes and quantities being graphed!
I highly recommend drawing what you expect the graphs to look like before you ever generate them. This gives you a chance to sanity check your expectation with reality rather than just jumping right into "explaining" what you see.
-
Analysis(Address the questions highlighted in green above)
- Effect of grid size
- Effect of # threads
- Effect of granularity of locking
- Effect of
sleep(1)