# 多處理機平行程式設計HW5 [quest](https://hackmd.io/@joey3639570/H1xxTgUFK) ## Problem 1 ### Question #### 1 `temp` shoudle be shared,because each thread has to insert the result . `count` should be private , because count should not be modified by others `i` can be shared or private because it remain constant during sorting `j` should be privated , because it iteration through the list for comparing #### 2 No ,there isn't . Each loop calculate the count from 0 . Since the `temp` table won't encounter collision #### 3 Yes , but we have to calculate the range first .If the array isn't so large , it is inefficiency . #### 4 On the server machine Execution : ``` ./hw51 [sizeof array] [worker_n] ``` #### 5 * **serial** ![](https://i.imgur.com/3tL9H7K.png) * **with 4 threads** ![](https://i.imgur.com/uXEIbId.png) * **qsort** ![](https://i.imgur.com/6Zj0XAE.png) It is obvious that qsort is faster than Count-sort , becasue the time complexity is O(n) for Count-sort , but O(log(n)) for qsort .Although we runnning with mutithread , the search cost dones't decreased . When parallelized with 4 threads , our speedup is 1.19 , which is 20% faster than serial one . ## Problem 2 To parallelized the word-count program , we use an MPMC model .Each producer parse the token `word` out ,and put it enqueue .Each cosumer dequeue a `word` ,and insert into the hash table database . ### Usage * **compile** `gcc word_count.c -o word_count -std=c99 -g -Wall -fopenmp` * **execution** `./word_count [number of file] []file name 1] [file name 2] [worker number] [producer number]` * **example** ![](https://i.imgur.com/H8HeqZm.png) ### Flow Chart ```graphviz Digraph G{ ".txt file"->"producer1"[label = "fetch"] "producer1"->"job queue" ".txt file"->"producer2"[label = "fetch"] "producer2"->"job queue" ".txt file"->"producer3"[label = "fetch"] "producer3"->"job queue" ".txt file"->"producer4"[label = "fetch"] "producer4"->"job queue" "job queue"->consumer1 consumer1->"Hash Table"[label = "insert"] "job queue"->consumer2 consumer2->"Hash Table"[label = "insert"] } ``` ### Jobqueue The structure of the jobqueue is circular ring buffer . It is a amazing struture .First , the operation is simple ,which reduce the time in critical section .Second , we only has to take care of `in` and `out` index for producer and consumer . In this implementation , we reserve the position for producer and consumer to do the operation .We use busy waiting to check if the target position is readable or writable .Also we use a very large queue to reduce the cost of handling the `full` and `empty` situation . #### Flow Chart ```graphviz Digraph G{ "queue" ->"Take a position"->"check if valid" "check if valid"->"do the oparation"[label = "True"] "check if valid"->"check if valid"[ label="False" ] } ``` #### Main Code * **Dequeue** ```clike dic * dequeue(){ int pos ; omp_set_lock(&(q->w)) ; pos = q->out ++ ; omp_unset_lock(&(q->w)) ; if(stop < pn){ while(q->table[pos].words == NULL){ if(stop == pn) break ; }; } dic * tmp = &(q->table[pos]) ; return tmp ; } ``` * **Enqueue** ```clike int enqueue(char ** target , int size){ int pos ; omp_set_lock(&(q->r)) ; pos = q->in ++ ; omp_unset_lock(&(q->r)) ; q->table[pos].size = size ; q->table[pos].words = target ; return 1 ; } ``` #### Thread Safety Since the only critical section is operations on `in` nad `out` , the queue is thread safety .To prevent from over lapping , we specific the queue size be `worker_n + producer_n` which ensure the accurracay when all worker has reserved the position . Since the producer job will finished first , `stop` is used for breaking out the busy waiting loop in consumer . ### File Input When writing parallelized program , an array is always useful since the interval is easy to determined . So we use `mmap` , `open` and `lseek` these system call to map the file into an array . #### System Call * open Open a file descripter refer to the file * lseek Move the fd to the specific position , `SEEK_END` means seeks to the end of file .`lseek` returns the bytes go through , * mmap Maps the file into array ,the file has to be specified with fd , file_size ,and beginning offset . #### Main Code ```clike int get_file(char * file_name){ fd = open(file_name , O_RDONLY) ; if(fd == -1) return 0; fsize = lseek(fd , 0 ,SEEK_END) ; if(fsize < 0) return 0; file = mmap(NULL ,fsize,PROT_READ ,MAP_SHARED,fd ,0) ; if(!file) return 0 ; slice = fsize / pn ; return 1 ; } ``` ### Performance Source file : lyrics of a [song](https://www.youtube.com/watch?v=dQw4w9WgXcQ) #### Serial , 1 file ![](https://i.imgur.com/dFtkYgL.png) #### Producer = 1 ,Cosumer = 1 ,1 file ![](https://i.imgur.com/F4a7wOv.png) #### Producer = 1 ,Cosumer = 2 ,1 file ![](https://i.imgur.com/MtIfUMH.png) #### Producer = 2 ,Cosumer = 2 ,1 file ![](https://i.imgur.com/jjU7FvD.png) Among all test , the performance is really bad ! Only the 1-1 model has a 10% speed-up . I guess the reason is the fread time cost was saved by mmap , which cost the most heavy part be very light .And the insertion has to do `lock` and `busy waiting` operation , even when poping out the queue has to `lock` , too .The total time cost will be `serial`/ `threadn` + `busy` + `lock` . I think if I implement the program by fread or without single safety queue , it will be much faster !