并行任务调度算法与分析

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

多核程序任务调度算法与分析
于俊夫
辽宁师范大学计算机与信息技术学院
摘要
实验目的:
完成在多核多线程的硬件平台下的多任务调度,达到线程并行化,任务可调度化,尽量充分利用CPU的高频、多核、多线程的特点、加快程序的运行、在操作系统中的灵活调度、使得多核CPU发挥更大的效率。

实验方法:
在visual stdio 2005 下使用openmp 指导语句编写C语言代码,通过对C 代码的执行结果的分析,了解多核、多线程的CPU在操作系统下的任务调度。

实验中采用三种任务调度算法、主要通过4*4的两个矩阵相乘来完成。

实验结果:
算法1最终矩阵的的16个元的计算在8线程和4线程下分布到各线程中计算达到任务调度的实验目的;
算法2最终矩阵的的16个元的计算分块划分,8线程和4线程下分布到各线程中计算达到任务调度的实验目的;
算法3最终矩阵的的16个元的关联64的计算,8线程和4线程下分布到各线程中计算达到任务调度的实验目的;
实验结论:
在多核、多线程的操作系统中任务可调度、任务执行准确度高,算执行性强。

关键字
4*4的矩阵、分块指导语句、分段指导语句、循环任务分配指导语句
1 引言
实验中选择的是“任务调度”的优化算法,在多核、多线程处理器的任务调度策略问题。

对于独立性任务与关联性任务的不同处理方式。

最终实现了分块与分段的独立性任务调度算法,和循环任务调度的关联性任务调度算法。

2、实验关键字解析
分块指导语句是解决独立性任务的调度策略所用的方法,是将独立的任务划分成一个或者多个块,在块间进行调度分配。

分段指导语句是解决独立性任务调度的调度策略所用的方法,是将多里的块内的独立的任务再细划分成多个段,即在块内产生段间进行调度分配。

循环任务调度指导语句是解决相关联的任务调度策略所用的方法,是将在循环中的任务进行划分,在循环内进行调度分配。

3、实验的思想
多核程序的任务调度算法中并行化的思想是多核程序任务调度的主思想,在操作系统中根据需要同程序的关联性问题德需要自由的选择调度策略。

这样的操作系统执行效率高、灵活性强、可操控性高对程序的执行更有效果。

4、实验方法
4.1、理论分析
Openmp指导语句中parallel命令的sections与sections命令同schedule命令子句分别提供不同的任务调度的指导方法。

parallel命令的sections与sections命令可将程序分割成多块在每块下产生分成多段分段分配线程执行。

上述的命令是解决独立性任务的调度策略所用的方法。

schedule命令子句for循环并行化的任务调度语句将for循环内的计算分割并给予多线程分别并行执行。

上述的命令是解决相关联的任务调度策略所用的方法。

4.2、实验方法
4.2.1:实验方法一:
一个分块内分多个段的调度策略
void Agra1(int a[4][4],int b[4][4],int c[4][4])
{
printf("THIS IS THE FIGURE 1\n");
#pragma omp parallel sections
{
#pragma omp section
{
printf("section 1
THREADID=%d\na[0][0]*b[0][0]+a[0][1]*b[1][0]+a[0][2]*b[2][0]+a[0][3]*b[3][0]=%d\n",o mp_get_thread_num(),c[0][0]=a[0][0]*b[0][0]+a[0][1]*b[1][0]+a[0][2]*b[2][0]+a[0][3]*b[3][ 0]);
}
#pragma omp section
{
printf("section 2 THREADID=%d\n
a[0][0]*b[0][1]+a[0][1]*b[1][1]+a[0][2]*b[2][1]+a[0][3]*b[3][1]=%d\n",omp_get_thread_nu m(),c[0][1]=a[0][0]*b[0][1]+a[0][1]*b[1][1]+a[0][2]*b[2][1]+a[0][3]*b[3][1]);
}
#pragma omp section
{
printf("section 3 THREADID=%d\n
a[0][0]*b[0][2]+a[0][1]*b[1][2]+a[0][2]*b[2][2]+a[0][3]*b[3][2]=%d\n",omp_get_thread_nu m(),c[0][2]=a[0][0]*b[0][2]+a[0][1]*b[1][2]+a[0][2]*b[2][2]+a[0][3]*b[3][2]);
}
#pragma omp section
{
printf("section 4 THREADID=%d\n
a[0][0]*b[0][3]+a[0][1]*b[1][3]+a[0][2]*b[2][3]+a[0][3]*b[3][3]=%d\n",omp_get_thread_nu m(),c[0][3]=a[0][0]*b[0][3]+a[0][1]*b[1][3]+a[0][2]*b[2][3]+a[0][3]*b[3][3]);
}
#pragma omp section
{
printf("section 5 THREADID=%d\n
a[1][0]*b[0][0]+a[1][1]*b[1][0]+a[1][2]*b[2][0]+a[1][3]*b[3][0]=%d\n",omp_get_thread_nu m(),c[1][0]=a[1][0]*b[0][0]+a[1][1]*b[1][0]+a[1][2]*b[2][0]+a[1][3]*b[3][0]);
}
#pragma omp section
{
printf("section 6 THREADID=%d\n
a[1][0]*b[0][1]+a[1][1]*b[1][1]+a[1][2]*b[2][1]+a[1][3]*b[3][1]=%d\n",omp_get_thread_nu m(),c[1][1]=a[1][0]*b[0][1]+a[1][1]*b[1][1]+a[1][2]*b[2][1]+a[1][3]*b[3][1]);
}
#pragma omp section
{
printf("section 7 THREADID=%d\n
a[1][0]*b[0][2]+a[1][1]*b[1][2]+a[1][2]*b[2][2]+a[1][3]*b[3][2]=%d\n",omp_get_thread_nu m(),c[1][2]=a[1][0]*b[0][2]+a[1][1]*b[1][2]+a[1][2]*b[2][2]+a[1][3]*b[3][2]);
}
#pragma omp section
{
printf("section 8 THREADID=%d\n
a[1][0]*b[0][3]+a[1][1]*b[1][3]+a[1][2]*b[2][3]+a[1][3]*b[3][3]=%d\n",omp_get_thread_nu m(),c[1][3]=a[1][0]*b[0][3]+a[1][1]*b[1][3]+a[1][2]*b[2][3]+a[1][3]*b[3][3]);
}
#pragma omp section
{
printf("section 9 THREADID=%d\n
a[2][0]*b[0][0]+a[2][1]*b[1][0]+a[2][2]*b[2][0]+a[2][3]*b[3][0]=%d\n",omp_get_thread_nu m(),c[2][0]=a[2][0]*b[0][0]+a[2][1]*b[1][0]+a[2][2]*b[2][0]+a[2][3]*b[3][0]);
}
#pragma omp section
{
printf("section 10 THREADID=%d\n
a[2][0]*b[0][1]+a[2][1]*b[1][1]+a[2][2]*b[2][1]+a[2][3]*b[3][1]=%d\n",omp_get_thread_nu
m(),c[2][1]=a[2][0]*b[0][1]+a[2][1]*b[1][1]+a[2][2]*b[2][1]+a[2][3]*b[3][1]);
}
#pragma omp section
{
printf("section 11 THREADID=%d\n
a[2][0]*b[0][2]+a[2][1]*b[1][2]+a[2][2]*b[2][2]+a[2][3]*b[3][2]=%d\n",omp_get_thread_nu m(),c[2][2]=a[2][0]*b[0][2]+a[2][1]*b[1][2]+a[2][2]*b[2][2]+a[2][3]*b[3][2]);
}
#pragma omp section
{
printf("section 12 THREADID=%d\n
a[2][0]*b[0][3]+a[2][1]*b[1][3]+a[2][2]*b[2][3]+a[2][3]*b[3][3]=%d\n",omp_get_thread_nu m(),c[2][3]=a[2][0]*b[0][3]+a[2][1]*b[1][3]+a[2][2]*b[2][3]+a[2][3]*b[3][3]);
}
#pragma omp section
{
printf("section 13 THREADID=%d\n
a[3][0]*b[0][0]+a[3][1]*b[1][0]+a[3][2]*b[2][0]+a[3][3]*b[3][0]=%d\n",omp_get_thread_nu m(),c[3][0]=a[3][0]*b[0][0]+a[3][1]*b[1][0]+a[3][2]*b[2][0]+a[3][3]*b[3][0]);
}
#pragma omp section
{
printf("section 14 THREADID=%d\n
a[3][0]*b[0][1]+a[3][1]*b[1][1]+a[3][2]*b[2][1]+a[3][3]*b[3][1]=%d\n",omp_get_thread_nu m(),c[3][1]=a[3][0]*b[0][1]+a[3][1]*b[1][1]+a[3][2]*b[2][1]+a[3][3]*b[3][1]);
}
#pragma omp section
{
printf("section 15 THREADID=%d\n
a[3][0]*b[0][0]+a[3][1]*b[1][2]+a[3][2]*b[2][2]+a[3][3]*b[3][2]=%d\n",omp_get_thread_nu m(),c[3][2]=a[3][0]*b[0][0]+a[3][1]*b[1][2]+a[3][2]*b[2][2]+a[3][3]*b[3][2]);
}
#pragma omp section
{
printf("section 16 THREADID=%d\n
a[3][0]*b[0][3]+a[3][1]*b[1][3]+a[3][2]*b[2][3]+a[3][3]*b[3][3]=%d\n",omp_get_thread_nu m(),c[3][3]=a[3][0]*b[0][3]+a[3][1]*b[1][3]+a[3][2]*b[2][3]+a[3][3]*b[3][3]);
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
omp_set_num_threads(8);
int a[4][4]={{1,1,1,1},{2,2,2,2},{3,3,3,3},{4,4,4,4}};
int b[4][4]={{5,5,5,5},{6,6,6,6},{7,7,7,7},{8,8,8,8}};
int c[4][4];
Agra1(a,b,c);
return 0;
}
特点方法一是将4*4的矩阵相乘的计算分在一个大块内,在块内分为16个段,段与段之间并行调度。

图1是一次实验结果的截图:
(图1)
4.2.2、实验方法二:
分多个块每个块内分多个段的调度策略
void Agra2(int a[4][4],int b[4][4],int c[4][4])
{
printf("THIS IS THE FIGURE 2\n");
#pragma omp parallel
{
#pragma omp sections
{
#pragma omp section
{
c[0][0]=a[0][0]*b[0][0]+a[0][1]*b[1][0]+a[0][2]*b[2][0]+a[0][3]*b[3][0];
printf("c[0][0]=%d\n",c[0][0]);
printf("section 1 THREADID=%d\n",omp_get_thread_num());
}
#pragma omp section
{
c[0][1]=a[0][0]*b[0][1]+a[0][1]*b[1][1]+a[0][2]*b[2][1]+a[0][3]*b[3][1];
printf("ac[0][1]=%d\n",c[0][1]);
printf("section 2 THREADID=%d\n",omp_get_thread_num());
}
#pragma omp section
{
c[0][2]=a[0][0]*b[0][2]+a[0][1]*b[1][2]+a[0][2]*b[2][2]+a[0][3]*b[3][2];
printf("cc[0][2]=%d\n",c[0][2]);
printf("section 3 THREADID=%d\n",omp_get_thread_num());
}
#pragma omp section
{
c[0][3]=a[0][0]*b[0][3]+a[0][1]*b[1][3]+a[0][2]*b[2][3]+a[0][3]*b[3][3];
printf("c[0][3]=%d\n",c[0][3]);
printf("section 4 THREADID=%d\n",omp_get_thread_num());
}
}
#pragma omp sections
{
#pragma omp section
{
c[1][0]=a[1][0]*b[0][0]+a[1][1]*b[1][0]+a[1][2]*b[2][0]+a[1][3]*b[3][0];
printf("c[1][0]=%d\n",c[1][0]);
printf("section 5 THREADID=%d\n",omp_get_thread_num());
}
#pragma omp section
{
c[1][1]=a[1][0]*b[0][1]+a[1][1]*b[1][1]+a[1][2]*b[2][1]+a[1][3]*b[3][1];
printf("c[1][1]=%d\n",c[1][1]);
printf("section 6 THREADID=%d\n",omp_get_thread_num());
}
#pragma omp section
{
c[1][2]=a[1][0]*b[0][2]+a[1][1]*b[1][2]+a[1][2]*b[2][2]+a[1][3]*b[3][2];
printf("c[1][2]=%d\n",c[1][2]);
printf("section 7 THREADID=%d\n",omp_get_thread_num());
}
#pragma omp section
{
c[1][3]=a[1][0]*b[0][3]+a[1][1]*b[1][3]+a[1][2]*b[2][3]+a[1][3]*b[3][3];
printf("c[1][3]=%d\n",c[1][3]);
printf("section 8 THREADID=%d\n",omp_get_thread_num());
}
}
#pragma omp sections
{
#pragma omp section
{
c[2][0]=a[2][0]*b[0][0]+a[2][1]*b[1][0]+a[2][2]*b[2][0]+a[2][3]*b[3][0];
printf("c[2][0]=%d\n",c[2][0]);
printf("section 9 THREADID=%d\n",omp_get_thread_num());
}
#pragma omp section
{
c[2][1]=a[2][0]*b[0][1]+a[2][1]*b[1][1]+a[2][2]*b[2][1]+a[2][3]*b[3][1];
printf("c[2][1]=%d\n",c[2][1]);
printf("section 10 THREADID=%d\n",omp_get_thread_num());
}
#pragma omp section
{
c[2][2]=a[2][0]*b[0][2]+a[2][1]*b[1][2]+a[2][2]*b[2][2]+a[2][3]*b[3][2];
printf("c[2][2]=%d\n",c[2][2]);
printf("section 11 THREADID=%d\n",omp_get_thread_num());
}
#pragma omp section
{
c[2][3]=a[2][0]*b[0][3]+a[2][1]*b[1][3]+a[2][2]*b[2][3]+a[2][3]*b[3][3];
printf("c[2][3]=%d\n",c[2][3]);
printf("section 12 THREADID=%d\n",omp_get_thread_num());
}
}
#pragma omp sections
{
#pragma omp section
{
c[3][0]=a[3][0]*b[0][0]+a[3][1]*b[1][0]+a[3][2]*b[2][0]+a[3][3]*b[3][0];
printf("c[3][0]=%d\n",c[3][0]);
printf("section 13 THREADID=%d\n",omp_get_thread_num());
}
#pragma omp section
{
c[3][1]=a[3][0]*b[0][1]+a[3][1]*b[1][1]+a[3][2]*b[2][1]+a[3][3]*b[3][1];
printf("c[3][1]=%d\n",c[3][1]);
printf("section 14 THREADID=%d\n",omp_get_thread_num());
}
#pragma omp section
{
c[3][2]=a[3][0]*b[0][0]+a[3][1]*b[1][2]+a[3][2]*b[2][2]+a[3][3]*b[3][2];
printf("c[3][2]=%d\n",c[3][2]);
printf("section 15 THREADID=%d\n",omp_get_thread_num());
}
#pragma omp section
{
c[3][3]=a[3][0]*b[0][3]+a[3][1]*b[1][3]+a[3][2]*b[2][3]+a[3][3]*b[3][3];
printf("c[3][3]=%d\n",c[3][3]);
printf("section 16 THREADID=%d\n",omp_get_thread_num());
}
}
}
}
特点方法二是将4*4的矩阵相乘的计算分在4个大块之中,每个大块分成4个段,块内段并行调度,块与块之间并行调度。

下图是一次实验结果的截图:
(图2)
4.2.3、实验方法三:
循环内的任务调度策略
void Agra3(int a[4][4],int b[4][4],int c[4][4])
{
int i,j,k,sum=0;
printf("THIS IS THE FIGURE 3\n");
#pragma omp parallel for private(i,k,j)schedule(static) reduction(+:sum)
for(i=0;i<4;i++)
{
for(k=0;k<4;k++)
{
sum=0;
// #pragma omp parallel for schedule(/*static*//*guided*/dynamic,/*4*/2) reduction(+:sum)
for(j=0;j<4;j++)
{
sum=sum+a[i][j]*b[j][k];
c[i][j]=sum;
//printf("THREADID=%d\t ",omp_get_thread_num());
}
printf("\nTHREADID=%d c[%d][%d]=%d\n",omp_get_thread_num(),i,k,c[i][k]);
}
}
}
特点:方法三是将4*4的矩阵相乘的计算用for循环来计算运用schedule 命令子句将其并行调度,schedule命令子句提供static(静态)、dynamic(动态)、guided(指导)三种模式的调度。

图是dynamic的一次执行的截图:
(图3)
4.3、实验结果分析:
4.3.1、实验方法一的实验结果分析:
经过多次程序的执行对每次的程序执行情况作统计分析得到如下结论,经过分块后块内段间并行调度可执行,但thread0主线程
的没有出现在段间的调度是因为thread0执行于分块程序中,用于
分块的调度。

块内段间调度并行化程度高,对线程的利用率高无论
是4线程还是8线程都能充分利用。

4.3.2、实验方法二的实验结果分析:
经过多次的程序执行得到多组数据,统计和特殊线程出现概率的分析,同方法一的结论下主线程thread0主要在用于块分配,用于
第一块的分配,之后的段间与块间的任务调度都是所有线程都参加
执行,分配更加均衡。

每一块的负载率相同执行时间相差不大。


以是多线程的调度对CPU的负载平衡。

4.3.3、实验方法三的实验结果分析;
由于实验方法三的调度情况较多,分由static、dynamic、guided、外层指导四种情况分析:
4.3.3.1、static:
静态的调度方式使得线程间的冲突与不均较少,虽然每次计
算的线程不同(并行的特点)但是其后的所有类似型位置的计算
所用线程完全相同。

主线程在外层参加计算,不会进入最内层的
计算,因而内层计算不会出现主线程thread0,而外层的计算全
部都是thread0,而且保持在外层循环那里。

4.3.3.2、dynamic:
动态调度的方式,上面的结论中的主线程thread0的结论依然成立。

而在内层的计算调度中动态性显现,相邻的两个线程不
相同,但是每次内层循环的计算位置相同的地方线程没有出现静
态方法中的全部相同,线程分布更加离散化,即调度的更加灵活。

4.3.3.3、guided:
指导性的调度需要加参数,而参数就是每个线程执行的连续执行计算的次数,即当size参数是2,则每条线程连续执行两
次,当size参数是4则每一线程的连续执行计算次数是4.。


线程的结论相同。

4.3.3.4、外层指导:
线程分配原则同最内层的实验结论相同,线程执行的顺行由并行产生,无序性强。

调度更加灵活。

同样可以分为static,dynamic,guided三种调度方法。

效果与内层调度的线程分配是相同的。

5、结论:
本实验的结论是在操作系统下的任务调度使得多核、多线程的处理器经过优化的任务调度算法,根据独立性与关联性的区别产生两种不同的调度选择,在独立性的任务间用#pragma omp parallel sections #pragma omp section分块与分段的独立分配方法产生的调度效果更好,更加灵活,并且可以根据任务的数量分析分块与分段的方法。

在数据关联的任务调度时主要针对线程在关联数据间的分配使得线程得到充分的利用,调度灵活计算快速。

这样的调度算法可以更加准确的灵活的利用硬件系统。

6、参考文献:
[1]周伟明、多核计算与程序设计、武汉、华中科技大学出版社。

相关文档
最新文档