Sunteți pe pagina 1din 9

Write Up

Comments
Code
---------------------------------------

Operation Systems Homework 2


Samuel Boakye, skb3bb@virginia.edu
Though this assignment was somewhat challenging, after reading the chapters and going

over the lecture notes, I was able to complete the assignment.

1. Problem:

The goal of the assignment was to implement a parallel, binary reduction on a list

of N numbers using individual threads and a synchronization mechanism called a

barrier.

2. Approach:

In order to complete this assignment, three C++ files were written: Barrier.h,

Barrier.cpp, and max.cpp. The idea was to implement the Barrier.cpp (whose

constructors and variables where defined in Barrier.h) into max.cpp. Within the

Barrier class, there was one function called wait():

Wait- This function is used to synchronize threads at the point the barrier

is specified. The calling thread blocks until the required number of threads

have called it again. If the signal is delivered to a thread block on a barrier,

then upon return from the signal handler, the thread resumes waiting at the

barrier if the required number of threads have not arrived at the barrier.
There are two functions (main and max) and two typedef (sem and argstruct)

inside the max.cpp file:

Typedef sem- This structure is essentially a semaphore whose attributes

are a mutex and an id to keep track.

Typedef argstruct- This argument structure holds nine attributes a a

single thread: two ints for the two numbers compared in each thread; an

int to hold the maximum of each round; a barrier object for each thread; a

Boolean value to determine if the current thread is active or not; an id for

the current thread; the number of rounds expected to happen (log2(n)); a

semaphore pointer struct (sem from above); and an array of the maxes of

all the threads.

max- This function finds the maximum value inside an int array. More

specifically, it calculates the maximum out of all the threads. It does so by

doing the following: first, the two numbers within a given thread were

compared to see which the bigger number is. Then, that max number was

put into an array of maximum numbers alongside the other max numbers

of the other threads. Assuming the threads id was odd, sem_wait was

implemented to halt the thread from passing through. The odd id

semaphores are producers. If the threads id was even, sem_post was

called. This is the consumer side of the relationship; the evens consume

the max and use it for the next comparison. To insure that all the odd

threads had finished first, a wait by a single reusable barrier is


implemented. Once the operation was finished, the calling thread was

terminated by pthread.exit.

main- In the main, the input from the user or file is taken and put into an

int array. In addition, all of the attributes of the argstruct is created and

used to set up the max operations using threads. Each thread is created in a

for-loop respectable to the index of the pair of comparable integers.

Finally, pthread_join() is called to make the value passed to pthread_exit()

in the max function available, thus allowing the program to access the max

value.

3. Problems Encounted:

The first problem that was run into was just implementing the single barrier into

the max function. The first version of the code did not include threads or barriers;

it was just a simple for loop. The beta version included threads and run smoothly.

But even so, there did not seem a necessity to add a barrier, let alone how to

include. It was only at a realization that one could use the threads as odds and

evens to be producers and consumers that gave the correct places to put the barrier

(between the handling of odd and even threads).

The second problem arose when I run the code on my own machine. For some

reason, the program was caught in an unending loop, were all but the last the

threads would go through. This looked like a typical case of Dr. D (deadlock), but

when running the same code with the same inputs and same test file on the VM

image provided in class, it run fine. This just lets me know that in the future, I
shouldnt rely on the outputs/ results given to me on my computer for future

assignments.

4. Testing:

Testing the output of the code was relatively simple. First, an input of one integer

was tested; output was just that number. Then was couple of 2^N list numbers

were tested and successfully yielded the max value in each list. To test a huge list

of 2^12 numbers, a testfile.txt holding that number of integers was created and

tested by putting into the command line: ./a.out < testfile.txt . It worked.

For testing the barrier, I tested the code without the line which implements the

barrier (a->b->wait). With the inputs: 1 2 3 4 5 6 7 8, the output was 37580864,

which clearly indicated the threads were doing their own thing and accessing

random things. The second time the same input was run, the output was

24363072, now indicating a total randomness and no real way the threads were

being regulated. When the barrier was put back in place, the output was 8.

5. Conclusion:

Conclusively, barriers can be used to regulate threads for perform operations such

as finding the largest number in an array or the sum numbers in an array. I learnt

the relationship between producer and consumer threads and how to use counting

semaphores as binary ones. But most importantly, checking my code on the VM

provided rather than relying on my laptop.


6. Sources:
Max.cpp
/*
*Machine Problem #2
*
*CS 4414 Operating Systems
*Fall 2017
*
*Samuel Boakye (skb3bb)
*
*max.cpp
*
*The following code calculates the maximum value
*in a given integer array. I refer the reader to
*the report included in the tar file.
*
* COMPILE: make
* HEADERS: Barrier.h
*
* MODIFICATIONS:
* Sept 25 - Made the Barrier.h, Barrier.cpp and max.cpp.
* max.cpp only contains threads.
* Sept 26 - max.cpp now includes Barrier.cpp.
* Sept 28 - added barrier to max() function
*
*/

#include <stdio.h>
#include <semaphore.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include <string>
#include <iostream>
#include <pthread.h>
#include <math.h>
#include "Barrier.h"

using namespace std;

/* A semaphore struct called sem */


typedef struct sem{

sem_t mutex; //Semaphore mutex


int semID; //Semaphore ID
} sem;

/* An argument stucture to hold attributes to do operations to threads */


typedef struct arg_struct {
int num1; //1st compared number
int num2; //2nd compared number
int maximum; //Max between num1 and num2
Barrier* b; //Barrier between each thread
bool isAct; //Boolean to determine if thread is active
int id; //The ID of each thread
int numRounds; //The number of Rounds (log(2)n)
sem* s; //Semaphore structure for each thread
int* maxes; //Array of the maxes of each comparison in each
round
} argstruct;

/* The max method to find the maximum value in an array */


void* max(void* p) {

argstruct* a = (argstruct*)p;

int rounds = 0;

/*While process of parallel binary reduction is not finished*/

while (rounds < a->numRounds) {

if (a->isAct){

if (a->num1 > a->num2){


a->maximum = a->num1;
} else{
a->maximum = a->num2;
}
if (a->id % 2 == 1){ //The thread is odd
sem_wait(&(a->s[(a->id-1)/2].mutex));

/*Makes the semaphor wait. a derefernce s, which is an array f semaphors*/

a->maxes[(a->id-1)/2] = a->maximum;

/*An array of n/2 maximums. The odd semaphors are producers. They take
their max and store it in this maxes array. The evens comsume the max
and use it for the next comparison.

a->isAct = false;

/*Essentially, this makes that odd thread inactive, meaning we don't use
it anymore. */
}
}

rounds++;

a->b->wait();

/*The barrier all threads have to wait until all threads are at that
barrier. */
if (a->isAct && rounds < a->numRounds){
/* The case for the even ones, the producers. It gives them their
attributes */
a->num1 = a->maximum;
a->num2 = a->maxes[(a->id)/2];
sem_post(&(a->s[(a->id)/2].mutex));

/*This is the consumer side of that relationship of sem_wait above */


a->id = (a->id)/2;
}
}
pthread_exit; //Terminate the calling thread
}

/* Main method */
int main() {

/* Getting the input from each line to put in an int array */


vector<int> vec_nums;
string input;
getline(cin, input);

while (input != "") {


vec_nums.push_back(atoi(input.c_str()));
getline(cin, input);
}

int n = vec_nums.size(); //The the total number of inputs


int* nums = &vec_nums[0]; //int array pointer

/* Threads and implement barrier to calculate the max value*/


argstruct arg[n/2]; //Make argument array
pthread_t th[n/2]; //Create handle for array of threads

Barrier* b = new Barrier(n/2); //Create a barrier


int* maxes = new int[n/2]; //Array to hold max of reach round
sem* s = new sem[n/2]; //Make an array of semaphores

for (int i = 0; i < n/2; i++){


sem_init(&(s[i].mutex), 1, 1); //Unlock all semaphores
}

int j =0;

/* Set up all the attributes of the argstruct */


for (int i = 0; i < n; i++){
arg[j].isAct = true; //Current thread to active
arg[j].maxes = maxes; //Set the maxes array
arg[j].id = j; //Set the id to index
arg[j].numRounds = log2(n); //Set the number of rounds
arg[j].b = b; //Set barrier
arg[j].s = s; //Set the semaphor
arg[j].num1 = nums[i]; //Set the first compared num
arg[j].num2 = nums[i+1]; // Set the second compared num
/*Creates the thread used for calulating the max */
pthread_create(&th[j], NULL, max, (void*)&arg[j]);
j++;
i++;

for (int k = 0; k < j; k++){


pthread_join(th[k], NULL);
/*Wait for the thread specified to terminate */
}
if (n == 1){
cout<<nums[0]<<endl; //Check to see if just input size is 1
}else{

/*Print out arg[0], which is the max number of the array. */


cout<<arg[0].maximum <<endl;
}

Barrier.h:
#include <semaphore.h>

class Barrier {

public:
Barrier(int k); //Barrier constructor with k parameter
void wait(); //Wait function for Barrier
private:
sem_t waitq; //Semaphore type wait to halt threads
sem_t mutex; //Semaphore type mutex to allow threads
sem_t throttle;//Allow hand shakes between threads
int k; //Number of threads
int count; //Keep up with the current no. of threads
} ;
Barrier.cpp:
#include "Barrier.h"

/* Barrier contructor with attributes: waitq, mutex, and throttle */


Barrier::Barrier(int value) {
k = value;
count = 0;
sem_init(&waitq, 1, 0);
sem_init(&mutex, 1, 1);
sem_init(&throttle, 1, 0);
}

/* The Barrier's function wait that halts threads until all threads are
done doing a specific job. */
void Barrier::wait() {

sem_wait(&mutex);
count++;
if (count < k) {
sem_post(&mutex); //Allows threads to modify variables
sem_wait(&waitq); //Halts thread (locks current thread)
sem_post(&throttle); //Handshake betw/ releaser and waiter
}
else {

/*For each thread, unlock and allow handshake for next in queue */

for (int i = 0; i < k-1; i++){


sem_post(&waitq);
sem_post(&throttle);
}
count = 0; //Reset count to 0
sem_post(&mutex); //Unlock current thread
}
}

HONOR PLEDGE:
On my honor as a student, I have neither given nor received aid on this assignment.
-Samuel Boakye

S-ar putea să vă placă și