You are on page 1of 7

Ho Chi Minh University of Technology

Faculty of Computer Science and Engineering

Operating Systems
Laboratory Report 3

April 11, 2021

Student’s name: Vu Hoang Hai


Student ID: 1952669
Class: CC01 - CO2017
Date Performed: March 27, 2021
Instructor: Le Thanh Van

1 Monte Carlo’s π estimation

– All single-threaded and multi-threaded codes contains these following structures


and functions:
struct Points {
double x, y;
};

double randomize(double min, double max) {

double scale = (double)rand() / (double)RAND_MAX;


return min + scale * (max - min);
}

void generatePoints(struct Points graphPoint[], int size) {

for (int i = 0; i < size; i++) {


(graphPoint[i]).x = randomize(-1.0, 1.0);
(graphPoint[i]).y = randomize(-1.0, 1.0);
}
}

– randomize(int, int): generates a random double number from −1.0 to 1.0.


– generatePoints(struct, int): intermediate function to generate random points
on the Cartesian graph.

1
void isInsideCircle(struct Points graphPoint[], int size) {

double temp;

for (int i = 0; i < size; i++) {


temp = sqrt(pow(graphPoint[i].x, 2.0) +
pow(graphPoint[i].y, 2.0));

if (temp <= 1.0) {


inside_count++;
}
}

– isInsideCircle(struct, int): function to check if the point falls inside the


circle area with radius of 1.

– Code for single threaded workload:


int main(int argc, char* argv[]) {

// SINGLETHREAD VERSION

if (argc != 2) {
fprintf(stderr, "Overflow of command argument!");
return -1;
}
if (atoi(argv[1]) <= 0) {
fprintf(stderr, "Size cannot be smaller than 1! Size
inputed: %d", atoi(argv[1]));
return -1;
}

struct Points graphPoint[atoi(argv[1])];

generatePoints(graphPoint, atoi(argv[1]));
isInsideCircle(graphPoint, atoi(argv[1]));

printf("\nInside: %d", inside_count);


printf("\nPi = %f", (4.0 * inside_count) / atof(argv[1]));

2
– This main(int, char*) function takes in a command line of the total number of
generated points and purely call individual functions to perform the estimation.
– The following result is obtained:

– It can be seen that this program has a somewhat close estimation of the π =
3.148480 number when generating 1,000,000 entries.
However, at larger values, it cannot access (or increase) the array in general so it
throws Segmentation fault error.
Generally the single-threaded program has far close than accurate estimation of π
and does not work with larger sizes.

– Code for multi threaded workload:


#define NUM_THREADS 10
int main(int argc, char* argv[]) {

// MULTITHREAD VERSION

if (argc != 2) {
fprintf(stderr, "Overflow of command argument!");
return -1;
}
if (atoi(argv[1]) <= 0) {
fprintf(stderr, "Size cannot be smaller than 1! Size
inputed: %d", atoi(argv[1]));
return -1;
}

3
struct arguments* args;
args = malloc(sizeof(struct arguments) + (atoi(argv[1]) /
NUM_THREADS) * sizeof(struct Points));
args->size = atoi(argv[1]) / NUM_THREADS;

pthread_t threads[NUM_THREADS];

for (long t = 0; t < NUM_THREADS; t++) {


pthread_create(&threads[t], NULL, multithread, args);
pthread_join(threads[t], NULL);
}

printf("\nInside: %d", inside_count);


printf("\nPi = %f", (4.0 * inside_count) / atof(argv[1]));

pthread_exit(0);
}

– This main\left( int, char* \right) function takes in a command line of the
total number of generated points and creates 10 different threads to each generate
a size/10 points and individually count up the number of inscribed points.
Afterwards, all threads wait for each other to finish execution via pthread_join(threads[t],
NULL) and print out the results.
– The following result is obtained:

4
– It can be seen that this program has a much closer estimation of the π number
when generating more than 1,000,000 entries. The production time is also faster
overall in comparison to the single-threaded version.

2 Concurrency

Given the initial code:


#include <stdio.h>
#include <pthread.h>

void* hello(void* tid) {


printf("Hello from thread %d\n", (int)tid);

5
}

int main() {
pthread_t tid[10];

int i;

for (i = 0; i < 10; i++) {


pthread_create(&tid[i], NULL, hello, (void*)i);
}

pthread_exit(NULL);
}

The above code causes race conditions when multiple threads are created as we don’t
know what process will the OS execute.

To fix this issue, we can either use pthread_join(threads[t], NULL) to wait for
thread execution one after another or implement a hard token, in which it can be passed
over, and the process which has the token in particular will be allowed to execute while
the other must wait for its turn.

The following improvisation is shown:


#include <stdio.h>
#include <pthread.h>

pthread_mutext_t lock;

void* hello(void* tid) {


pthread_mutex_lock(&lock);
printf("Hello from thread %d\n", (int)tid);
pthread_mutex_unlock(&lock);
}

int main() {
pthread_t tid[10];

pthread_mutex_init(&lock, NULL);

int i;

for (i = 0; i < 10; i++) {


pthread_create(&tid[i], NULL, hello, (void*)i);
}

6
pthread_exit(NULL);
}

This code implements a global pthread_mutex_t object, of which the variable lock
acts as lock to prevent execution from other threads beside the running one.

The lock object is initialized via pthread_mutex_init(&lock, NULL)

In the void* hello(void* tid) function, we can see that the function from the mutex
class pthread_mutex_lock(&lock) is called to “lock” the token to this thread so as
other threads cannot run concurrently. After it finishes execution, the lock is “unlocked”
via pthread_mutex_unlock(&lock) and the token will be passed to the next thread in
order.

The following result is obtained in order of execution every time:

You might also like