百度实习生招聘的一道大数据处理题目(下)

       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: =~这个符号表示约等于。

 

 

 

你可能感兴趣的:(多线程,排序,大数据处理,百度笔试)