首先给出一个数组求和的例子,通过下面的图示可以很清楚的看明白,我也就不说过多的废话了,当然当你看完下面的这幅图的时候,就会面对三组程序,这些都是在并行下的程序代码,分别是OpenMP以及Windows Threads还有POSIX Pthreads下的改编代码,均实现了并发的作用,至于串行下的代码,这里没有给出,我想这么简单的求和代码你一定闭着眼睛都能写出来的。我们的体验就从这里出发,后面会在接下来的文章中逐一体验这种将串行改编成并行代码的玩乐之旅。
对于上面简要的给予说明,就是对于数组进行求和运算的一个过程,下面是三部分的代码,串行的程序代码你很清楚怎么写,很简单。
首先是OpenMP的并行代码示例:
OpenMP
///
#include "stdafx.h"
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int sum=0;
int X[10]={1,2,3,4,5,6,7,8,9,10};
#pragma omp parallel for reduction(+:sum)
for(int i=0;i<10;i++)
{
sum = sum+X[i];
}
cout<<sum<<endl;
system("pause");
return 0;
}
//
这部分是Windows Threads代码示例:
#include "stdafx.h"
#include <windows.h>
#define NUM_THREADS 2
int X[10]={1,2,3,4,5,6,7,8,9,10};
int gSum[NUM_THREADS];
DWORD WINAPI Summation(LPVOID pArg)
{
int tNum=(int)pArg;
int lSum=0;
int start, end;
start=(10/NUM_THREADS)*tNum;
end =(10/NUM_THREADS)*(tNum+1);
if (tNum == (NUM_THREADS-1))
{
end=10;
}
for(int i=start; i<end; i++)
{
lSum+=X[i];
}
gSum[tNum]=lSum;
return 0;
}
int main(int argc, char *argv[])
{
int j, sum=0;
HANDLE tHandles[NUM_THREADS];
for (j=0;j<NUM_THREADS;j++)
{
DWORD threadID;
tHandles[j]=CreateThread(NULL, 0 ,Summation, (LPVOID)j, 0, &threadID);
}
for (j=0; j<NUM_THREADS; j++)
{
WaitForSingleObject(tHandles[j],INFINITE);
sum += gSum[j];
}
printf("sum is %d\n", sum);
system("pause");
return 0;
}
下面给出POSIX Pthreads下的并行代码:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 2
int X[10]={1,2,3,4,5,6,7,8,9,10};
int gSum[NUM_THREADS];
void* Summation(void *pArg)
{
int tNum=*((int*)pArg);
int lSum=0;
int start, end;
start=(10/NUM_THREADS)*tNum;
end =(10/NUM_THREADS)*(tNum+1);
if (tNum == (NUM_THREADS-1))
{
end=10;
}
for(int i=start; i<end; i++)
{
lSum+=X[i];
}
gSum[tNum]=lSum;
free(pArg);
}
int main(int argc, char *argv[])
{
int j, sum=0;
pthread_t tHandles[NUM_THREADS];
for (j=0;j<NUM_THREADS;j++)
{
int *threadNum=new(int);
*threadNum=j;
pthread_create(&tHandles[j], NULL, Summation, (void*)threadNum);
}
for (j=0; j<NUM_THREADS; j++)
{
pthread_join(tHandles[j], NULL);
sum += gSum[j];
}
printf("sum is %d\n", sum);
system("pause");
return 0;
}
///
通过上面的程序,你或许会说这不就是多线程啊,没什么东西,对了,你说对了,这是个引例,没有什么不过是不可或缺的开始,通过将串行代码修改成并行代码有很多中方式,你可以使用OPenMP,TBB,也可以使用不同环境下的线程来做。下面你将看到这篇文章的真正的开始,如何将串行修改成并行,达到在多核情况下的效率提升:
从上图你看明白我们要实现什么吗,我们需要实现每项是之前项的和,下面是实现的串行代码:
#include <iostream.h>
int main(int argc, char *argv[])
{
int A[8]={3,5,2,5,7,9,4,6};
for (int i=1;i<8;i++)
{
A[i]=A[i-1]+A[i];
}
for (int j=0;j<8;j++)
{
cout<<A[j]<<" ";
}
cout<<endl;
return 0;
}
下面我来看看并发的修改方式,图示如下
下面是实现的代码,上面的过程分为三部分来实现,在程序里面体现出来,希望这些会给你帮助
#include <iostream.h>
#include <windows.h>
#include <process.h>
#define NUM_THREADS 2
int A[8]={3,5,2,5,7,9,4,6};
int inTotals[NUM_THREADS],outTotals[NUM_THREADS];
HANDLE doneStep1[NUM_THREADS];
HANDLE doneStep2;
unsigned _stdcall prefixScan(LPVOID pArg)
{
int tNum=*((int *)pArg);
int start,end,i;
int lPrefixTotal;
free(pArg);
start=(8/NUM_THREADS)*tNum;
end=(8/NUM_THREADS)*(tNum+1);
if (tNum == (NUM_THREADS-1))
{
end=8;
}
for (i=start+1;i<end;i++)
{
A[i]=A[i-1]+A[i];
}
inTotals[tNum]=A[end-1];
SetEvent(doneStep1[tNum]);
WaitForSingleObject(doneStep2, INFINITE);
lPrefixTotal=outTotals[tNum];
for (i=start; i<end;i++)
{
A[i]=lPrefixTotal+A[i];
}
return 0;
}
int main(int argc, char *argv[])
{
int i,j;
HANDLE tHandles[NUM_THREADS];
for (i=0;i<NUM_THREADS;i++)
{
doneStep1[i]=CreateEvent(NULL,TRUE,FALSE,NULL);
}
doneStep2=CreateEvent(NULL,TRUE,FALSE,NULL);
for (i=0;i<NUM_THREADS;i++)
{
int *tnum=new int;
*tnum=i;
tHandles[i]=(HANDLE)_beginthreadex(NULL, 0, prefixScan, (LPVOID)tnum, 0, NULL);
}
WaitForMultipleObjects(NUM_THREADS, doneStep1, TRUE, INFINITE);
outTotals[0]=0;
for (j=1;j<NUM_THREADS;j++)
{
outTotals[j]=outTotals[j-1]+inTotals[j-1];
}
SetEvent(doneStep2);
WaitForMultipleObjects(NUM_THREADS, tHandles, TRUE, INFINITE);
for (int ti=0;ti<8;ti++)
{
cout<<A[ti]<<" ";
}
cout<<endl;
return 0;
}
//
当数据量很大时,再加之内核数量的提升,并发将会带来极大的效率上面的提高。希望对你有用。
由于时间不足,这篇文章后面还有修正添加。