要统计一个整型数组中3出现的次数,写一个并行程序:
#include<time.h>
#include<assert.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<pthread.h>
#define ARRLEN 200000000 //int型数组的长度,我们的程序就是要统计这个数组中元素3出现了多少次
struct padded_int{
int value; //4个字节
char padding[60]; //60个字节
}*private_counter;
int counter=0;
int arr[ARRLEN]={0};
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;
int max_threads=0; //令最多开辟的线程数等于核数
/*用随机数初始化数组*/
void initArr(){
int i;
for(i=0;i<ARRLEN;i++)
arr[i]=rand()%1000;
}
/*从/proc/cpuinfo文件中获取CPU的核数*/
void getCoreNum(int *num){
FILE *fp;
if((fp=fopen("/proc/cpuinfo","r"))==NULL){
perror("fopen");
exit(1);
}
int cn=0;
char buf[256];
memset(buf,0x00,sizeof(buf));
while((fgets(buf,sizeof(buf),fp))!=NULL){
if(strstr(buf,"cpu cores")!=NULL){
char *delim=":";
char *tag=strtok(buf,delim);
char *cn_str=strtok(NULL,delim);
cn=atoi(cn_str);
break;
}
memset(buf,0x00,sizeof(buf));
}
fclose(fp);
*num=cn;
printf("您的计算机是%d核的\n",cn);
}
/*每个线程负责统计数组的一段区域内3出现的次数*/
void *count_thread(void* id){
int index=*(int *)id;
assert(max_threads!=0);
int section_len=ARRLEN/max_threads;
int start=index*section_len;
int i;
for(i=start;i<start+section_len;i++){
if(arr[i]==3){
private_counter[index].value++;
}
}
printf("private_counter[%d].value=%d\n",index,private_counter[index].value);
pthread_mutex_lock(&lock);
counter+=private_counter[index].value;
pthread_mutex_unlock(&lock);
}
/*串行方法统计3出现的次数*/
void count(){
counter=0;
int i;
for(i=0;i<ARRLEN;i++)
if(arr[i]==3)
counter++;
}
/*主函数*/
main(){
initArr();
getCoreNum(&max_threads);
counter=0;
clock_t t1=clock();
pthread_t *pths;
private_counter=(struct padded_int*)calloc(2,sizeof(struct padded_int));
pths=(pthread_t*)calloc(max_threads,sizeof(pthread_t));
int i;
for(i=0;i<max_threads;i++) //开辟多线程分头工作
pthread_create(pths+i,NULL,count_thread,(void*)&i);
for(i=0;i<max_threads;i++) //等待所有子线程收工
pthread_join(*(pths+i),NULL);
free(private_counter);
free(pths);
printf("并行计算的结果:%d\n",counter);
clock_t t2=clock();
count();
printf("串行计算结果:%d\n",counter);
clock_t t3=clock();
printf("并行计算用时:%.2f秒\n",((double)(t2-t1)/CLOCKS_PER_SEC));
printf("串行计算用时:%.2f秒\n",((double)(t3-t2)/CLOCKS_PER_SEC));
}
改进历史:
- 由于自加运行并不是原子操作,所以在每个线程中counter++前要获得互斥锁。
- 频繁地加锁和解锁会使运行效率降低。下面我们给每个线程一个私有的计数器private_counter,计算结束后一次性更新全局变量counter。
- 私有计数器private_counter从int演变为struct padded_int的原因是:int型的private_counter并不是真正私有的。cache一致性协议两个处理器看到的存储映象是相同的。cache一致性的单位是cache行,本机有cahche行大小是64字节(我猜是这样的,因为/proc/cpuinfo文件中写着:cache_alignment: 64 )。private_counter[0]和private_counter[1]被放在了同一个cache行中,对cache行中任一部分的修改等同于对整个cache行的修改。逻辑上不同的数据共享cache行的现象叫做假共享。所以当处理器1修改private_counter[0]之前,处理器2必须把自己手中相应的cache行废弃掉;当处理器1修改完private_counter[0]之后,处理器2再能重新获取private_counter[1]所在的cache行的操作权。为了不让private_counter[0]和private_counter[1]在同了个cache行内,所以把它们都填充成了64字节。
现在的体系结构已经可以把多线程映射到不同的处理器上,可为什么并行计算的速度还是没有串行的快呢?
本文来自博客园,作者:高性能golang,转载请注明原文链接:https://www.cnblogs.com/zhangchaoyang/articles/2311102.html