CSC 447 Midterm, Spring 2016

Lebanese American University · Parallel Programming · 11 pages · Dr. Haidar Harmanani

All CSC 447 files
CSC 447 Midterm, Spring 2016, page 1

Stuck on a question? A tutor can walk you through it.

CSC 447 Midterm, Spring 2016, page 2 CSC 447 Midterm, Spring 2016, page 3 CSC 447 Midterm, Spring 2016, page 4 CSC 447 Midterm, Spring 2016, page 5 CSC 447 Midterm, Spring 2016, page 6 CSC 447 Midterm, Spring 2016, page 7 CSC 447 Midterm, Spring 2016, page 8 CSC 447 Midterm, Spring 2016, page 9 CSC 447 Midterm, Spring 2016, page 10

Showing the first 10 of 11 pages. Download the PDF to see all of it.

More CSC 447 files

Show the text of this exam
School of Arts & Sciences Department of Computer Science & Mathematics CSC 447—Parallel Programming for Multicore and Cluster Systems Spring 2016 Midterm Examination Name: Student ID: Signature: Duration: 75 minutes General Instructions • There are 6 questions and 11 pages. Make sure that you have all of them. • Exam questions are NOT sorted by order of difficulty. Scan the exam before you start and budget your time over the exam questions so you can maximize your grade. • Your answers should be brief and right to the point. There is no need for essay answers! Use the back of the previous sheet if you need additional space. • Your handwriting should be readable so it can be graded. You are liable to have points deducted from your grade if your handwriting is excessively difficult to decipher! • The exam is a closed book, closed notes, and closed neighbor exam. Any attempts at cheating or communicating with a neighbor will lead to expulsion from the exam! Question Points Score 1 21 2 12 3 20 4 15 5 17 6 15 Total: 100 CSC 447: Midterm Exam, v1.0 2016-03-10 Multiple-Choice Questions 1. (21) Answer the following questions with a True or False. Justify the answer if it is false. (a) According to Amdahl’s law, the maximum speed-up of a parallel computation given that 80% of the computation can be executed in parallel is 10. (a) (b) If we have an algorithm whose sequential complexity is O(N) and we parallelize it with P processors, the maximum speedup we would expect is N log N (b) (c) Flynn’s taxonomy classifies computer systems into four categories (SISD, SPMD, MISD, MIMD) according to the number of instruction and data streams used. (c) (d) Hypercube is the topology with the smallest diameter for a machine whose number of nodes is a power of 2. (d) (e) Whenever producers and consumers use locks, the producer thread must overwrite the shared buffer when the previous task has not been picked up by a consumer thread. The consumer threads must continue on picking up tasks until done. Individual consumer threads should pick up tasks one at at time. (e) (f) If a program is running concurrently it is also running in parallel. (f) (g) The main advantages for the shared memory parallel architecture are simplified programming and potential memory capacity. Some of the disadvantages are high cost and limited scalability (g) Spring 2016 Page 2 of 11 CSC 447: Midterm Exam, v1.0 2016-03-10 2. (12) Select the appropriate answer. (a) Indicate which of the following phenomena contributed to the rise of multicore computing: A. Limitations on cooling technologies B. Limitations on software complexity C. Limitations on on-chip bandwidth D. Limitations on scalability of single core designs (b) Which of the following is not an alternative for Shared Memory Multiprocessors. A. Using sequential library routines with an existing sequential programming language B. Using a sequential programming language with a parallelizing compiler C. UNIX processes D. Threads (c) In programming shared address space, the following is true: A. Threads assume all memory is global. B. Threads assume all memory is local. C. Memory manipulation is slower. D. Process based models assume that memory is public and available to other processors. (d) Which of the following is a valid reason for using threads. A. Software Portability B. Latency Hiding C. Scheduling and Load Balancing D. Ease of Programming E. All of the above (e) How many threads could be used for the computation below, each thread executing one or more of the instructions: x++ ; a = x + 2; b = a + 3; c++; A. Two concurrent threads. B. Three concurrent threads. C. Four concurrent threads. D. None of the above. The code is not parallelizable. (f) Which of the following specifies that the variable is to be initialized in an OpenMP program. A. private B. firstprivate C. shared D. reduction (g) If two threads execute the instruction x++ where x is a shared variable initialized to 0, what are the possible values that x could have after the execution of the threads: A. x = 2 if at different times and x = 1 if interleaved B. x = 1 since threads will execute at different times C. x = 2 since threads will be interleaved. D. None of the above (h) The only way to significantly increase the performance of microprocessors is to: A. Improve power efficiency at about the same rate as the performance increase. B. Use good compilers C. Use faster chips D. Use efficient algorithms Spring 2016 Page 3 of 11 CSC 447: Midterm Exam, v1.0 2016-03-10 Parallel Performance Name: 1. Examine the task graph shown below. Each task, which you can think of as an async, is labelled with its runtime. Answer the following four questions about the program’s runtime. In all cases you may ignore any work scheduling or task spawning overheads. S1 = 50ms P1 = 100ms P2 = 100ms P3 = 100ms P4 = 2000ms S2 = 50ms P5 = 100ms P6 = 500ms P7 = 100ms P8 = 100ms S2 = 50ms (a) Assuming a single worker thread (X10 NTHREADS=1) what is the runtime of this program? The single worker thread must execute all of the tasks serially, so T1 = 50 + (2000 + 100 + 100 + 100) + 50 + (100 + 500 + 100 + 100) + 50 = 3250ms (b) What is the speedup when X10 NTHREADS=8? The stages are now executed in parallel, with the duration of the stage determined by the slowest task: T8 = 50+ (2000) + 50 + (500) + 50 = 2650ms, so the speedup is T1 T8 or 1.226. (c) If each parallel task (PN = Xms) were parallelized further to become two parallel tasks (QN = X 2 ms,RN = X 2 ms), and again run with X10 NTHREADS=8, what would the runtime be? What is the speedup be relative to the previous run? The slowest task in the parallel stage is now twice as fast meaning: T ′ 8 = 50 + ( 2000 2 ) + 50 + ( 500 2 ) + 50 = 1400ms, so the speedup is T8 T ′ 8 or 1.89. (d) Why is the speedup not 2×? This is due to Amdahl’s Law. To achieve a 2x overall speedup, the entire program would need to be sped up by a factor of two, but only a portion of the program was. The serial tasks were unchanged. COMS 4995 Principles and Practice of Parallel Programming - Midterm Exam Page 2 of 8 3. (a) (5) Assuming a single worker thread (X10 NTHREADS=1) what is the runtime of this program? (b) (5) What is the speedup when X10 NTHREADS=8? (c) (5) If each parallel task (PN = Xms) were parallelized further to become two parallel tasks (QN = X 2 ms, RN = X 2 ms), and again run with X10 NTHREADS=8, what would the runtime be? What is the speedup be relative to the previous run? (d) (5) Why is the speedup not 2×? Spring 2016 Page 4 of 11 CSC 447: Midterm Exam, v1.0 2016-03-10 4. (15) Parallelize Floyd’s algorithm shortest all pairs shortest path using OpenMP. You can assume that matrix A is given as input and the number of nodes is n. Recall that the sequential Floyd’s algorithm maybe implemented using the following code segment: for (k=0; k<n; k++) for (i=0; i<n; i++) for (j=0; j<n; j++) if ( (d[i][k]+d[k][j]) < d[i][j] ) d[i][j] = d[i][k]+d[k][j]; Spring 2016 Page 5 of 11 CSC 447: Midterm Exam, v1.0 2016-03-10 Multi-Core Programming using Pthreads 5. (17) One of the OpenMP constructs that is the reduction operator. For instance, the dot product of two vectors can be computed as follows: /* Compute and return the dot produce of vectors ’a’ and ’b’. */ double vector_dot_product(double *a, double *b, int n) { double dotproduct = 0; int i; #pragma omp parallel for reduction(+:dotproduct) for (i = 0; i < n; i++) dotproduct += a[i] * b[i]; return dotproduct; } Show the above code using pThreads. Be sure to include all necessary synchronization code. To simplify the problem, you may assume that the number of threads nT is a constant, and that n % nT == 0. You may also assume that each parallel region spawns a new team of threads (though real-world OpenMP implementations reuse a pool of threads). Your code must be thread-safe, but must avoid unnecessary or excessive synchronization. Spring 2016 Page 6 of 11 CSC 447: Midterm Exam, v1.0 2016-03-10 Multi-Core Programming using OpenMP 6. (15) Write an OpenMP producer-consumer algorithm. The producer generates a set of random numbers and then adds them to the tail of the queue. The producer uses a work stealing algorithm where it would go through the head of the queue and retrieves one element at a time. The producer will then invoke a method process with the dequeued element as an argument. The algorithm repeats until the queue is empty and no random numbers are generated. There is no need to worry about the implementation of the process method. Furthermore, there is no need to worry about any methods declarations. A sequential version could look like: void Producer(Queue *p) { int myRand; while (flag > 0) { myRand = rand(); enqueue(p, myRand); flag = (myRand > 1,0000,000) } // CONSUMER: Sum the data in A double Consumer() { int myRand; Queue *p; while (p != NULL) { myRand = dequeue(p); process(p); } Spring 2016 Page 7 of 11 CSC 447: Midterm Exam, v1.0 2016-03-10 This page was left blank intentionally Spring 2016 Page 8 of 11 CSC 447: Midterm Exam, v1.0 2016-03-10 Pthreads API Cheat Sheet Pthread creation pthread_t threads[N] pthread_create(&threads[i], NULL, start_routine, void *args) pthread_join(threads[i]) Mutex pthread_mutex_t mutex; pthread_mutex_init(&mutex); pthread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex); pthread_mutex_destroy(&mutex); Semaphore sem_t sem; sem_init(&sem, 0, initial) -> initial = 0: lock, initial > 0: unlocked sem_wait(&sem) -> sem = 0: wait, sem > 0 decrement and go sem_post(&sem) -> increment value sem_destroy(&sem) Condition Variable pthread_cond_t cond pthread_cond_init(&cond) pthread_cond_wait(&cond, &mutex) -> unlock mutex and wait on cond pthread_cond_signal(&cond) -> wake up threads waiting on cond pthread_cond_destroy(&cond) Common Condition Variable Usage pthread_mutex_lock(&mutex); while(isnotready()) pthread_cond_wait(&cond, &mutex); critical section pthread_mutex_unlock(&mutex); pthread_cond_signal(&cond2); Spring 2016 Page 9 of 11 OpenMP Reference Sheet for C/C++ Constructs <parallelize a for loop by breaking apart iterations into chunks> #pragma omp parallel for [shared(vars), private(vars), firstprivate(vars), lastprivate(vars), default(shared|none), reduction(op:vars), copyin(vars), if(expr), ordered, schedule(type[,chunkSize])] <A,B,C such that total iterations known at start of loop> for(A=C;A<B;A++) { <your code here> <force ordered execution of part of the code. A=C will be guaranteed to execute before A=C+1> #pragma omp ordered { <your code here> } } <parallelized sections of code with each section operating in one thread> #pragma omp parallel sections [shared(vars), private(vars), firstprivate(vars), lastprivate(vars), default(shared|none), reduction(op:vars), copyin(vars), if(expr)] { #pragma omp section { <your code here> } #pragma omp section { <your code here> } .... } <grand parallelization region with optional work-sharing constructs defining more specific splitting of work and variables amongst threads. You may use work-sharing constructs without a grand parallelization region, but it will have no effect (sometimes useful if you are making OpenMP'able functions but want to leave the creation of threads to the user of those functions)> #pragma omp parallel [shared(vars), private(vars), firstprivate(vars), lastprivate(vars), default(private|shared|none), reduction(op:vars), copyin(vars), if(expr)] { <the work-sharing constructs below can appear in any order, are optional, and can be used multiple times. Note that no new threads will be created by the constructs. They reuse the ones created by the above parallel construct.> <your code here (will be executed by all threads)> <parallelize a for loop by breaking apart iterations into chunks> #pragma omp for [private(vars), firstprivate(vars), lastprivate(vars), reduction(op:vars), ordered, schedule(type[,chunkSize]), nowait] <A,B,C such that total iterations known at start of loop> for(A=C;A<B;A++) { <your code here> <force ordered execution of part of the code. A=C will be guaranteed to execute before A=C+1> #pragma omp ordered { <your code here> } } <parallelized sections of code with each section operating in one thread> #pragma omp sections [private(vars), firstprivate(vars), lastprivate(vars), reduction(op:vars), nowait] { #pragma omp section { <your code here> } #pragma omp section { <your code here> } .... } <only one thread will execute the following. NOT always by the master thread> #pragma omp single { <your code here (only executed once)> } } Directives shared(vars) <share the same variables between all the threads> private(vars) <each thread gets a private copy of variables. Note that other than the master thread, which uses the original, these variables are not initialized to anything.> firstprivate(vars) <like private, but the variables do get copies of their master thread values> lastprivate(vars) <copy back the last iteration (in a for loop) or the last section (in a sections) variables to the master thread copy (so it will persist even after the parallelization ends)> default(private|shared|none) <set the default behavior of variables in the parallelization construct. shared is the default setting, so only the private and none setting have effects. none forces the user to specify the behavior of variables. Note that even with shared, the iterator variable in for loops still is private by necessity > reduction(op:vars) <vars are treated as private and the specified operation(op, which can be +,*,-,&,|,&,&&,||) is performed using the private copies in each thread. The master thread copy (which will persist) is updated with the final value.> copyin(vars) <used to perform the copying of threadprivate vars to the other threads. Similar to firstprivate for private vars.> if(expr) <parallelization will only occur if expr evaluates to true.> schedule(type [,chunkSize]) <thread scheduling model> t ype chunkSize static number of iterations per thread pre-assigned at beginning of loop (typical default is number of processors) dynamic number of iterations to allocate to a thread when available (typical default is 1) guided highly dependent on specific implementation of OpenMP nowait <remove the implicit barrier which forces all threads to finish before continuation in the construct> Synchronization/Locking Constructs <May be used almost anywhere, but will only have effects within parallelization constructs.> <only the master thread will execute the following. Sometimes useful for special handling of variables which will persist after the parallelization.> #pragma omp master { <your code here (only executed once and by the master thread). } <mutex lock the region. name allows the creation of unique mutex locks.> #pragma omp critical [(name)] { <your code here (only one thread allowed in at a time)> } <force all threads to complete their operations before continuing> #pragma omp barrier <like critical, but only works for simple operations and structures contained in one line of code> #pragma omp atomic <simple code operation, ex. a += 3; Typical supported operations are ++,--,+,*,- ,/,&,^,<<,>>,| on primitive data types> <force a register flush of the variables so all threads see the same memory> #pragma omp flush[(vars)] <applies the private clause to the vars of any future parallelize constructs encountered (a convenience routine)> #pragma omp threadprivate(vars) Function Based Locking < nest versions allow recursive locking> void omp_init_[nest_]lock(omp_lock_t*) <make a generic mutex lock> void omp_destroy_[nest_]lock(omp_lock_t*) <destroy a generic mutex lock> void omp_set_[nest_]lock(omp_lock_t*) <block until mutex lock obtained> void omp_unset_[nest_]lock(omp_lock_t*) <unlock the mutex lock> int omp_test_[nest_]lock(omp_lock_t*) <is lock currently locked by somebody> Settings and Control int omp_get_num_threads() <returns the number of threads used for the parallel region in which the function was called> int omp_get_thread_num() <get the unique thread number used to handle this iteration/section of a parallel construct. You may break up algorithms into parts based on this number.> int omp_in_parallel() <are you in a parallel construct> int omp_get_max_threads() <get number of threads OpenMP can make> int omp_get_num_procs() <get number of processors on this system> int omp_get_dynamic() <is dynamic scheduling allowed> int omp_get_nested() <is nested parallelism allowed> double omp_get_wtime() <returns time (in seconds) of the system clock> double omp_get_wtick() <number of seconds between ticks on the system clock> void omp_set_num_threads(int) <set number of threads OpenMP can make> void omp_set_dynamic(int) <allow dynamic scheduling (note this does not make dynamic scheduling the default)> void omp_set_nested(int) <allow nested parallelism; Parallel constructs within other parallel constructs can make new threads (note this tends to be unimplemented in many OpenMP implementations)> <env vars- implementation dependent, but here are some common ones> OMP_NUM_THREADS "number" <maximum number of threads to use> OMP_SCHEDULE "type,chunkSize" <default #pragma omp schedule settings> Legend vars is a comma separated list of variables [optional parameters and directives] <descriptions, comments, suggestions> .... above directive can be used multiple times For mistakes, suggestions, and comments please email e_berta@plutospin.com