diff --git a/Lab3/examples/1-no_join-fixed.c b/Lab3/examples/1-no_join-fixed.c new file mode 100644 index 0000000..d0e3efa --- /dev/null +++ b/Lab3/examples/1-no_join-fixed.c @@ -0,0 +1,17 @@ +/**To compile, don't forget to add -lpthread. Might not work without that */ +#include +#include + +void* run (void* arg){ + printf("Hello from run\n"); + return NULL; +} + +int main(){ + pthread_t thread; // variable to store the reference to the thread + pthread_create(&thread, NULL, &run, NULL); + printf("In main"); // This section will be executed in parallel + pthread_join(thread,NULL); // necessary for waiting for the thread to finish + printf("In main 2"); + return 0; +} \ No newline at end of file diff --git a/Lab3/examples/2-sleep no_join-fixed.c b/Lab3/examples/2-sleep no_join-fixed.c new file mode 100644 index 0000000..4faecd4 --- /dev/null +++ b/Lab3/examples/2-sleep no_join-fixed.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include +#include + +void* run (void* arg){ + printf("Hello from run\n"); + return NULL; +} + +int main(){ + pthread_t thread; + pthread_create(&thread, NULL, &run, NULL); + // While the sleep() function will appear to provide synchronization, + // it is incorrect, and if used for this purposes instead of join + // or synchronization mechanism - the grade for the assignment will be + // reduced + sleep(2); + printf("Back in main"); + return 0; +} diff --git a/Lab3/examples/3-losing_track_of_threads-fixed.c b/Lab3/examples/3-losing_track_of_threads-fixed.c new file mode 100644 index 0000000..4703f9d --- /dev/null +++ b/Lab3/examples/3-losing_track_of_threads-fixed.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include +#include + +void* run (void* arg){ + sleep(10-(int)arg); + printf("Hello from run\n"); +} + +int main(){ + pthread_t thread[10]; + int i = 0; + for (i = 0 ; i < 10; ++i) + pthread_create(&thread[i], NULL, &run,(void*)i); + + // If you plan to use the results of all the threads, consider + // using join for all of the threads + for (i = 0 ; i < 10; ++i) + pthread_join(thread[i],NULL); + printf("In main"); +} \ No newline at end of file diff --git a/Lab3/examples/4-passing_data_wrongly.c b/Lab3/examples/4-passing_data_wrongly.c new file mode 100644 index 0000000..8f7cfc1 --- /dev/null +++ b/Lab3/examples/4-passing_data_wrongly.c @@ -0,0 +1,21 @@ +#include +#include + +void* run (void* arg){ + int i = (int) arg; + printf("Hello from run, arg is %i\n",i); +} + +int main(){ + pthread_t thread[10]; + int i = 0; + for (i = 0 ; i < 10; ++i) + pthread_create(&thread, NULL, &run,(void*)i); + // Notice how warnings are generated. This can be resolved by properly + // allocating space for the thread parameters. + // Note, that if you are using stack variable, the values might be corrupted + for (i = 0 ; i < 10; i++){ + pthread_join(thread[i],NULL); + } + printf("In main"); +} \ No newline at end of file diff --git a/Lab3/examples/4-passing_data_wrongly_fixed.c b/Lab3/examples/4-passing_data_wrongly_fixed.c new file mode 100644 index 0000000..1e6b470 --- /dev/null +++ b/Lab3/examples/4-passing_data_wrongly_fixed.c @@ -0,0 +1,31 @@ +#include +#include +#include + +typedef struct thread_args{ + char letter; + int id; +}Args; +void* run (void* arg){ + Args* argg = (Args*) arg; + int i = argg->id; + printf("Hello from run %d\n", i); + return NULL; +} + +int main(int argc, char** argv){ + pthread_t thread[10]; + int i = 0; + for (i = 0 ; i < 10; i++){ + Args* argg = (Args*) (malloc(sizeof(Args))); + argg->letter = 'q'; + argg->id = i; + pthread_create(&(thread[i]), NULL, &run,(void*)argg); + } + for (i = 0 ; i < 10; i++){ + pthread_join(thread[i],NULL); + } + // Free the resources somewhere, for long running program. Or let the OS handle that + printf("In main"); + return 0; +} \ No newline at end of file diff --git a/Lab3/examples/5-pthread_detach.c b/Lab3/examples/5-pthread_detach.c new file mode 100644 index 0000000..1af07af --- /dev/null +++ b/Lab3/examples/5-pthread_detach.c @@ -0,0 +1,19 @@ +#include +#include + +void* run (void* arg){ + printf("Hello from run\n"); + return NULL; +} + +int main(){ + pthread_t thread; + pthread_create(&thread, NULL, &run, NULL); + pthread_detach(thread); + // This command will make thread detached. This means the resources will be released + // upon the thread's completion - calling return + // However, detached threads cannot be joined, which means if you care about the result + // of the thread execution - you should not be using pthread_detach + printf("In main"); + return 0; +} \ No newline at end of file diff --git a/Lab3/main.c b/Lab3/main.c index da67e57..dcd8bd2 100644 --- a/Lab3/main.c +++ b/Lab3/main.c @@ -1,8 +1,42 @@ -#include -#include -#include -#include -#include +/** + * Question 1: Run the program with the problem size of 1000 and 10 threads, what is the approximate speedup you are achieving? + * + * Answer 1: On my home machine single threading took 0.640239 seconds and multithreading took 0.194644 seconds. + * On the lab machine single threading took 0.697472 seconds and multithreading took 0.114415 seconds. + * This means that the my home machine is 3.29 times faster and the lab machine is 6.10 times faster. + * + * Question 2: Is there a problem size / number of threads combination that slows down the computation process? Why do you think it is happening? + * + * Answer 2: There is a problem with having too many threads for the number of hardware threads available. Increasing past this only + * increases the overhead of creating and managing the threads. This is because the threads are not running in parallel and are instead + * being switched between by the OS. At lower matrix sizes the cost of creating threads and managing them is greater than the cost of + * just doing the computation in a single thread, so any combination where threads > the number of hardware threads will be slower, and + * as the matrix size approaches 1, than the greater effect thread creation and management will have on the speed of the program. + * + * Question 3: What is the minimum size of the problem that benefits from creating an extra thread? + * + * Answer 3: The lowest size on my home machine that consistently benefited from an extra thread was 150, but this can change depending + * on the specifications of the machine. + * + * Question 4: Does using the threads always improve execution duration? + * + * Answer 4: No, as the number of threads increases past the number of hardware threads available the execution duration increases + * due to managing the threads, as well as low size matrices where the cost of creating and managing threads is greater than the + * cost of just doing the computation in a single thread. + * + * Question 5: Guesstimate and comment on the nature of growth of the speedup with the number of threads – is it linear, exponential, are there any limits? + * + * Answer 5: The speedup is linear up to the number of hardware threads available, given a large enough matrix size. + * Given a matrix of size x, the speedup of using y threads is approximately x/y, up to the number of hardware threads available. + * After this the speedup will decrease as the number of threads increases, due to the overhead of creating and managing threads. + **/ + +#include +#include +#include +#include +#include +#include #define MAXN 5 @@ -82,6 +116,23 @@ void *multiply_matrices_threaded(void *threadParams) { * threading capacity and parallelize the computation in such a * way that a thread computes result per one or more rows */ + ThreadParams *t = (ThreadParams *) threadParams; + int N = t->size; + int row = t->row_index; + int column = 0; + int temp_result = 0; + while (row < N) { + column = 0; + while (column < N) { + temp_result = 0; + for (int i = 0; i < t->size; i++) { + temp_result = temp_result + t->first_array[row][i] * t->second_array[i][column]; + } + t->result[row][column] = temp_result; + column = column + 1; + } + row += t->max_threads; + } } int main(int argc, char **argv) { @@ -121,6 +172,22 @@ int main(int argc, char **argv) { * Write your code to create and use max_threads here, such that the threaded_result * is populated with the result of the computation. */ + thr->result = threaded_result; + pthread_t threads[max_threads]; + for (int i = 0; i < max_threads; i++) { + ThreadParams *params = (ThreadParams *) malloc(sizeof(ThreadParams)); + params->first_array = array1; + params->second_array = array2; + params->result = threaded_result; + params->row_index = i; + params->size = size; + params->max_threads = max_threads; + pthread_create(&threads[i], NULL, &multiply_matrices_threaded, (void *) params); + } + for (int i = 0; i < max_threads; i++) { + pthread_join(threads[i], NULL); + } + gettimeofday(&end, NULL); //The next line is inspired by https://linuxhint.com/gettimeofday_c_language/ microseconds = (end.tv_sec * 1000000 + end.tv_usec) - (begin.tv_sec * 1000000 + begin.tv_usec); @@ -129,7 +196,7 @@ int main(int argc, char **argv) { if (check_if_matrices_differ(result, threaded_result, size) != 0) { printf("Threaded result differ from single core computation, error\n"); - exit(0); + exit(1); } return 0; } \ No newline at end of file diff --git a/Lab3/try-for-error.sh b/Lab3/try-for-error.sh new file mode 100644 index 0000000..2bc135d --- /dev/null +++ b/Lab3/try-for-error.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cond=0 +while [ $cond -eq 0 ] +do + ./build/Lab3 10 10 &> output.txt + cond=$? + echo $cond +done \ No newline at end of file