百度實習生招聘的一道大數據處理題目(下)

       4爲排序階段CPU的使用率,可以看到只有一個核達到了100%的利用率。下面爲一個多線程(線程的數量爲核的數量)的排序版本,每個線程只對1G數據中的一部分進行快速排序,排序完成後再由另外一個線程進行歸併,將結果寫入文件。

多線程排序代碼如下:

/*multi_thread_sort.c*/

  1. /* 
  2.  
  3. * Author: Chaos Lee 
  4.  
  5. Date: 2012-06-30 
  6.  
  7. * Description: load, merge , store data with single core, but sorting data with all the cores provided by the SMP 
  8.  
  9. */ 
  10.  
  11. #include<stdio.h> 
  12.  
  13. #include<pthread.h> 
  14.  
  15. #include<sys/sysinfo.h> 
  16.  
  17. #include<sys/stat.h> 
  18.  
  19. #include<sys/types.h> 
  20.  
  21. #include<stdint.h> 
  22.  
  23. #include<stdlib.h> 
  24.  
  25. #include<assert.h> 
  26.  
  27.   
  28.  
  29. #include "../error.h" 
  30.  
  31. #include "timer.h" 
  32.  
  33.   
  34.  
  35. uint64_t * buffer = NULL
  36.  
  37. pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER; 
  38.  
  39. pthread_cond_t merge_start = PTHREAD_COND_INITIALIZER; 
  40.  
  41. int cores_number; 
  42.  
  43. int counter; 
  44.  
  45.   
  46.  
  47. int uint64_compare(const void * ptr1,const void * ptr2) 
  48.  
  49.  
  50.         return  *((uint64_t *)ptr1) > *((uint64_t *)ptr2) ? 1 : *((uint64_t *)ptr1) < *((uint64_t *)ptr2) ? -1 : 0; 
  51.  
  52.  
  53.   
  54.  
  55. typedef struct segment_tag 
  56.  
  57.  
  58.         uint64_t start; 
  59.  
  60.         uint64_t end
  61.  
  62. }segment_t,*segment_p; 
  63.  
  64.   
  65.  
  66. void barrier() 
  67.  
  68.  
  69.         int status; 
  70.  
  71.         status = pthread_mutex_lock(&counter_mutex); 
  72.  
  73.         if(0 != status) 
  74.  
  75.                 err_abort("locking error.",status); 
  76.  
  77.         counter++; 
  78.  
  79.         if(cores_number == counter) 
  80.  
  81.         { 
  82.  
  83.                 pthread_cond_signal(&merge_start); 
  84.  
  85.         } 
  86.  
  87.         status = pthread_mutex_unlock(&counter_mutex); 
  88.  
  89.         if(0 != status) 
  90.  
  91.                 err_abort("unlocking error.",status); 
  92.  
  93.  
  94. void * sort_thread_routin(void * args) 
  95.  
  96.  
  97.         DPRINTF(("%s","sorting thread start...\n")); 
  98.  
  99.         segment_p seg = (segment_p) args; 
  100.  
  101.         assert(buffer != NULL); 
  102.  
  103.         DPRINTF(("%s","begin to sort...\n")); 
  104.  
  105.         qsort(buffer+seg->start,seg->end-seg->start,sizeof(uint64_t),uint64_compare); 
  106.  
  107.         DPRINTF(("%s","Entering barrier...\n")); 
  108.  
  109.         barrier(); 
  110.  
  111.         pthread_exit((void *)0); 
  112.  
  113.  
  114.   
  115.  
  116. void * merge_thread_routin(void * args) 
  117.  
  118.  
  119.         int status,i,finish_count,elapsed_seconds; 
  120.  
  121.         FILE * fp_result; 
  122.  
  123.         uint64_t tmp; 
  124.  
  125.         restart_timer(); 
  126.  
  127.         DPRINTF(("%s","merging thread start...\n")); 
  128.  
  129.         fp_result = fopen("multi-result.dat","wb"); 
  130.  
  131.         while(cores_number != counter) 
  132.  
  133.         { 
  134.  
  135.                 status = pthread_cond_wait(&merge_start,&counter_mutex); 
  136.  
  137.                 if(0 != status) 
  138.  
  139.                         err_abort("waiting condition error.",status); 
  140.  
  141.         } 
  142.  
  143.         elapsed_seconds = get_elapsed_time(); 
  144.  
  145.         fprintf(stdout,"sorting cost %d seconds.\n",elapsed_seconds); 
  146.  
  147.         status = pthread_mutex_unlock(&counter_mutex); 
  148.  
  149.         if(0 != status) 
  150.  
  151.                 err_abort("unlocking error.",status); 
  152.  
  153.         DPRINTF(("begin to merge...\n")); 
  154.  
  155.         finish_count = 0; 
  156.  
  157.         segment_p segs = (segment_p) args; 
  158.  
  159.         restart_timer(); 
  160.  
  161.         while(finish_count<cores_number) 
  162.  
  163.         { 
  164.  
  165.                 int i,first=0,j; 
  166.  
  167.                 for(i=0;i<cores_number;i++) 
  168.  
  169.                 { 
  170.  
  171.                         if( 0 == first
  172.  
  173.                         { 
  174.  
  175.                                 if(segs[i].start<segs[i].end
  176.  
  177.                                 { 
  178.  
  179.                                         tmp = buffer[segs[i].start]; 
  180.  
  181.                                         j = i; 
  182.  
  183.                                         first = 1; 
  184.  
  185.                                 } 
  186.  
  187.                         } 
  188.  
  189.                         else 
  190.  
  191.                         { 
  192.  
  193.                                 if(segs[i].start<segs[i].end && buffer[segs[i].start]<tmp) 
  194.  
  195.                                 { 
  196.  
  197.                                         tmp = buffer[segs[i].start]; 
  198.  
  199.                                         j = i; 
  200.  
  201.                                 } 
  202.  
  203.                         } 
  204.  
  205.                 } 
  206.  
  207.                 segs[j].start++; 
  208.  
  209.                 if(segs[j].start >= segs[j].end
  210.  
  211.                 { 
  212.  
  213.                         finish_count++; 
  214.  
  215.                 } 
  216.  
  217.                 fwrite(&tmp,sizeof(uint64_t),1,fp_result); 
  218.  
  219.         } 
  220.  
  221.         elapsed_seconds = get_elapsed_time(); 
  222.  
  223.         fprintf(stdout,"merging cost %d seconds.\n",elapsed_seconds); 
  224.  
  225.         DPRINTF(("merging is over\n")); 
  226.  
  227.         fclose(fp_result); 
  228.  
  229.         pthread_exit((void *)0); 
  230.  
  231.  
  232.   
  233.  
  234. int main(int argc,char *argv[]) 
  235.  
  236.  
  237.         int elapsed_seconds,status,i; 
  238.  
  239.         segment_p segments; 
  240.  
  241.         pthread_t * sort_threads; 
  242.  
  243.         pthread_t * merge_thread; 
  244.  
  245.         uint64_t size,length,seg_len; 
  246.  
  247.         FILE * fp; 
  248.  
  249.         struct stat data_stat; 
  250.  
  251.   
  252.  
  253.         cores_number = get_nprocs(); 
  254.  
  255.   
  256.  
  257.         status = stat("data.dat",&data_stat); 
  258.  
  259.         if(0 != status) 
  260.  
  261.                 error_abort("stat file error.\n"); 
  262.  
  263.         size = data_stat.st_size; 
  264.  
  265.         length = size / sizeof(uint64_t); 
  266.  
  267.         seg_len = length / cores_number; 
  268.  
  269.   
  270.  
  271.         buffer = (uint64_t *) malloc(size); 
  272.  
  273.         if(NULL == buffer) 
  274.  
  275.         { 
  276.  
  277.                 fprintf(stderr,"mallocing error.\n"); 
  278.  
  279.                 exit(1); 
  280.  
  281.         } 
  282.  
  283.         fp = fopen("data.dat","rb"); 
  284.  
  285.         if(NULL == fp) 
  286.  
  287.         { 
  288.  
  289.                 fprintf(stderr,"file open error.\n"); 
  290.  
  291.                 exit(1); 
  292.  
  293.         } 
  294.  
  295.         start_timer(); 
  296.  
  297.         fread(buffer,size,1,fp); 
  298.  
  299.         elapsed_seconds = get_elapsed_time(); 
  300.  
  301.         fprintf(stdout,"loading cost %d seconds\n",elapsed_seconds); 
  302.  
  303.   
  304.  
  305.         segments = (segment_p)malloc(sizeof(segment_t)*cores_number); 
  306.  
  307.         if(NULL == segments) 
  308.  
  309.         { 
  310.  
  311.                 fprintf(stderr,"at %s:%d : %s",__FILE__,__LINE__,"malloc error.\n"); 
  312.  
  313.                 exit(1); 
  314.  
  315.         } 
  316.  
  317.         for(i=0;i<cores_number;i++) 
  318.  
  319.         { 
  320.  
  321.                 segments[i].start = i * seg_len; 
  322.  
  323.                 if(i != cores_number-1) 
  324.  
  325.                         segments[i].end = (i + 1 ) * seg_len; 
  326.  
  327.                 else 
  328.  
  329.                         segments[i].end = length; 
  330.  
  331.         } 
  332.  
  333.         sort_threads = (pthread_t *)malloc(sizeof(pthread_t) * cores_number); 
  334.  
  335.         if(NULL == sort_threads) 
  336.  
  337.         { 
  338.  
  339.                 fprintf(stderr,"at %s:%d :%s",__FILE__,__LINE__,"malloc failuer.\n"); 
  340.  
  341.                 exit(1); 
  342.  
  343.         } 
  344.  
  345.         merge_thread = (pthread_t *)malloc(sizeof(pthread_t)); 
  346.  
  347.         if(NULL == merge_thread) 
  348.  
  349.         { 
  350.  
  351.                 fprintf(stderr,"at %s:%d :%s",__FILE__,__LINE__,"malloc failuer.\n"); 
  352.  
  353.                 exit(1); 
  354.  
  355.         } 
  356.  
  357.   
  358.  
  359.         for(i=0;i<cores_number;i++) 
  360.  
  361.         { 
  362.  
  363.                 status = pthread_create(&sort_threads[i],NULL,sort_thread_routin,(void *)&segments[i]); 
  364.  
  365.                 if(0 != status) 
  366.  
  367.                         err_abort("creating threads faulire.\n",status); 
  368.  
  369.         } 
  370.  
  371.         status = pthread_create(merge_thread,NULL,merge_thread_routin,(void *)segments); 
  372.  
  373.         if(0 != status) 
  374.  
  375.                 err_abort("creating thread faulier.\n",status); 
  376.  
  377.         for(i=0;i<cores_number;i++) 
  378.  
  379.         { 
  380.  
  381.                 status = pthread_join(sort_threads[i],NULL); 
  382.  
  383.                 if(0 != status) 
  384.  
  385.                         err_abort("joining threads error.\n",status); 
  386.  
  387.         } 
  388.  
  389.         status = pthread_join(*merge_thread,NULL); 
  390.  
  391.         if(0 != status) 
  392.  
  393.                 err_abort("joining thread error.\n",status); 
  394.  
  395.         free(buffer); 
  396.  
  397.         fclose(fp); 
  398.  
  399.         return 0; 
  400.  

 

 

再編譯運行下,以下爲測試結果:

 

  1. [lichao@sg01 thread_power]$ gcc multi_thread_sort.c -o multi_thread_sort timer.o -lpthread 
  2.  
  3. [lichao@sg01 thread_power]$ ./multi_thread_sort 
  4.  
  5. loading cost 14 seconds 
  6.  
  7. sorting cost 22 seconds. 
  8.  
  9. merging cost 44 seconds. 

下圖5爲多線程排序時CPU的利用率,可以看到CPU的四個核都已經達到100%的利用率,即:硬件沒有白投資:D。當然排序的時間效果也很好,幾乎達到了之前的4倍的加速比。另外可以看到文件的加載速度和回寫速度也有所提高,這點也是讓我比較疑惑的。下面再次運行單線程排序版本。

 

李超

 

排序階段CPU的利用率

 

 

  1. [lichao@sg01 thread_power]$ ./single_thread_sort 
  2.  
  3. loading cost 17 seconds 
  4.  
  5. sorting cost 81 seconds 
  6.  
  7. writing results cost 12 seconds 

可以看到加載速度和回寫速度有了顯著的提升,雖然排序時間還是沒有多大變化。

再次運行多線程排序版本試試:

 

  1. [lichao@sg01 thread_power]$ ./multi_thread_sort 
  2.  
  3. loading cost 31 seconds 
  4.  
  5. sorting cost 22 seconds. 
  6.  
  7. merging cost 23 seconds. 

加載速度又延長了,排序速度幾乎不變,回寫速度也提高了不少。我想這主要是因爲文件系統本身提供了緩衝的作用,即上次用過的文件可以放在交換區,便於迅速載入內存吧。這樣第二次使用的時候,由於這些文件還存放在交換區中,所以以很高的速度傳入內存中。回寫的原理應該也一樣。對於1G的文件回寫到內存,只用了23s,大致的回寫速度爲50MB/s

假設文件系統一直起作用,並能達到第二次實驗的效果,即分塊排序22s,歸併排序並回寫文件系統23s,那麼計算和歸併回寫是能夠重合的。對於200G的文件A來說,分塊排序的處理時間大致爲:200*22s =~1.2h,就擴大爲1小時15分鐘吧。這樣對文件B來說也差不多爲1小時15分鐘,一共需要2個半小時,接下來開始歸併比較了,假設文件的緩衝系統能夠啓作用,即速度能達到50MB/s,這樣,對於2200G的文件都需要在內存中過一遍,大致時間應該爲400*10^3/50 = 8000s,大致爲2小時15分鐘,所以加上前面的2個半小時,對於2200G的文件尋找相同值共需要的時間爲 5個小時左右,至少比300萬年好點。

PS: =~這個符號表示約等於。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章