Please complete the required studio exercises listed below, along with any of the (optional) enrichment exercises that interest you.
As you work through the exercises, please record your answers in a file, and upon completion please upload a file with your answers to the assignment page for this studio in Canvas.
Please make sure that the name of each person who worked on these exercises is listed in the first answer, and that you number your answers so they are easy for us to match up with the appropriate exercise.
concurrency_design
).
In that directory, edit a new C++ source code file (which should be named
something like concurrency_design.cpp
) and in it
write a main function whose signature looks like the standard (i.e.,
portable between Linux and Windows) shell (on Windows, terminal window)
program main function
entry point for C++: int main (int, char * [])
.
Please also use the #define
precompiler
directive to define a manifest constant whose value is 0, and have the main
function simply return that constant to indicate successful completion of the
program.
In your main function, declare a two dimensional (2D) C style array of integers with 4 rows and 16
columns, another one with 16 rows and 4 columns, and another one with 8 rows and 8 columns.
Initialize their elements with different values, and for each of the arrays print out the memory address (i.e., the numeric value contained in a void *
pointer to that location, and the row number and column number in that array, of each of its elements.
Build and run your program, and as the answer to this exercise (1) show your program's output, (2) based on that output explain how to define rectangular regions of each array whose elements are contiguous in memory, and (3) explain whether or not there is a single common formula for defining contiguous rectangular regions that would apply to all three arrays (and if so what it is, or if not why not).
In your main function, call std::thread::hardware_concurrency()
to obtain the
number of threads to use for this exercise (if the function is not implemented or returns a value
less than 2, please use 4 as a default number of threads for this exercise).
Spawn that many threads in your main function, and use the search function for each of two variations: in one variation partition the array into disjoint regions and have all of the threads search for the same value; in the other variation have each thread search the entire array for its own unique value. After spawning all the threads, the main function should join with each of them.
As the answer to this exercise, please (1) say how many threads you used in each variation and whether
that number was returned by std::thread::hardware_concurrency()
or was used by default,
(2) show your code for the thread function and its use in the main function, and (3) show the output
your program produced, for both variations.
As the answer to this exercise, please show your code and the output of your program, and describe any interesting aspects of your design or of the program's behavior that you observed.
The main function should spawn the same number of threads as in the previous exercise, to multiply two 2D arrays and store the result in a third one, with each thread handling a disjoint portion of the work in parallel. After spawning all the threads, the main function should again join with each of them, and then print out the first, second, and third arrays.
As the answer to this exercise, please show your code and the output of your program, and describe any interesting aspects of your design or of the program's behavior that you observed.
Using the std::chrono::steady_clock
class or another suitable time source, measure the time
from just before the first thread is spawned until the last join is completed. Run each experiment multiple
times, and see if you can detect meaningful (and stable) variations in the program's performance with more
or fewer threads and smaller or larger arrays.
As the answer to this exercise please describe how you designed this experiment, what you saw, and what conclusions (if any) can be drawn from it.
A + B + C + D + E + F + G + H + I + J
where A
through J
are all square arrays of the same dimension and +
is the matrix addition operation.
Declare a struct with three pointers (and any other parameters you would like) defining the regions that
should be added and the region where the sums should be stored, and pass appropriately valued instances
of that struct to the active objects. For example, you might declare some temporary arrays that are
initialized to all zeroes (say T1
through Tn
where n is the number of threads),
pass the first active object pointers to A
, B
, and T1
, pass the second
active object pointers to C
, D
, and T2
, etc.
After all of the pairs of input arrays have been added, the program then should add the temporary arrays to
each other, with the result after all the additions ending up in the array Z
. How you assign
the remaining additions and how you synchronize them with the previous ones (in light of data dependences)
is up to you.
Build and run your program, and as the answer to this exercise please show your code, describe your design, and describe how the program performed.