有关OpenMP的一些事实
这篇文章是不能代替网站内包含的文件。这个网站提供了OpenMP API的当前版本的完整文档。在并行计算中,最强隐线程使用OpenMP的一个规范。同时考虑的一个隐含的线程库,OpenMP中,或开放式多处理,是一个应用程序接口(API),可用于明确直接多线程,共享内存并行。 OpenMP是不能保证最有效地利用共享内存,所以它不是为分布式内存并行系统本身的含义。通过特殊的编译指示,并插入到你的源代码,以指示段,同时要执行的指令OpenMP的实现并发。这些标记确认和编译器进行处理。这意味着从多核心处理器架构,OpenMP的资本。总之,这是至关重要的了解OpenMP的,为了有效地使用这些新的多核处理器架构。
隐线程库需要创建,管理的复杂性,多照顾,在一定程度上同步线程。 OpenMP是打算要适合在一个SMP架构的广泛实施,但它不是一个新的编程语言。相反,它是可以添加到FORTRAN,C或C的顺序程序来形容是如何工作的,将执行不同的处理器或内核和秩序需要共享数据的访问的线程之间共享的符号。 OpenMP的特点适当插入到一个连续的程序将允许很多,也许是大多数应用程序受益于共享内存并行体系结构,往往用最少的代码进行修改。在实践中,许多应用程序有相当的并行性,可以利用。
OpenMP的成功可以归因于多项因素。其中之一是其结构化的并行编程的高度重视。另一个原因是,OpenMP的使用比较简单,因为负担工作的并行程序的细节是编译器。它已被广泛采用的主要优势,使OpenMP应用程序将运行在许多不同的平台。但首先,OpenMP是及时的。在小型和大型SMP和其他硬件多线程,共享内存编程是简单易学,适用于在计算行业接受的标准,需要部署的强劲增长。这反过来,意味着所有主要的编译器 - 微软Visual C / C + + NET Windows和GNU GCC编译器为Linux和英特尔C / C + +编译器Windows和Linux,也支持OpenMP的。OpenMP的有关如何工作的一些基本
OpenMP指令划定可以在平行的(所谓的并行区域)和控制代码如何被分配给线程执行的代码。在OpenMP代码的线程的fork - join模式下运作。当主线程遇到并行区域执行应用程序时,一队的线程派生,这些线程开始执行并行区域内的代码。在并行区域结束时,团队内部的线程等待,直到所有的在团队中的其他线程完成之前"加入"。主线程继续执行以下语句的并行区域的序列。在所有并行区域(和大多数其他结构OpenMP的定义)结束的隐式屏障保留顺序的一致性。
{S0}
的OpenMP的指令,让用户告诉编译器指令并行执行和如何分配他们之间的线程将运行代码。在OpenMP指令是在一个特殊的格式,仅适用于OpenMP的编译器理解的指令。事实上,它看起来像一个普通的FORTRAN编译器或pragma来一个C / C + +编译器的一个评论,所以该程序可以运行,只是因为它没有事先如果没有OpenMP的编译器是知道的。 API不有许多不同的指令,但他们强大的有效利用。因此,粗略地说,OpenMP的实现共享内存(或共享的地址空间)的编程模型。该模型假设,正如其名称所暗示的,计划将执行一个或多个处理器共享部分或所有的可用内存。共享内存方案通常是由多个独立线程(能够处理指令流的执行状态,)执行的线程共享数据,但也可能有一些额外的,私人的数据。
共享内存并行编程的方法,必须提供,除了一个正常范围的指令,启动线程,分配给他们的工作,并协调其访问共享数据,包括确保执行某些操作的手段一个线程的时间。要重申,OpenMP的实现并发通过特殊的编译指示,并插入到你的源代码,以指示段,同时要执行的指令。这些标记确认和编译器进行处理。此pragma后,将通过一个单独的语句或用花括号的代码块。当应用程序在执行过程中遇到的这一说法,将叉子一组线程,每个线程的并行区域内执行所有的语句,并在该地区的最后陈述后加入线程。
在许多应用中,一个独立运营的大量发现在循环中。使用OpenMP构造循环工作共享,可以分裂这些循环迭代,并将它们分配给线程的并发执行。构造平行将启动一个新的并行区域周围的单一循环的pragma,团队线程之间的鸿沟循环迭代。分配迭代结束后,线程在并行区域结束的隐式屏障,坐在等待加入的其他线程。这是可能的分裂建设成两个pragma的并行:并行构造和建设,必须在并行区域中的词法。使用这种分离时,有并行工作循环迭代以外的线程组。您还可以附加调度子句循环工作共享构造控制,如何分配给线程的迭代。静态的时间表将划分成块迭代和循环迭代之前开始执行线程之间的分配块;轮循调度使用,如果有比线程多块。动态的时间表将分配给团队中的每个线程的迭代块;线程结束前一组的迭代,一个新的块分配,直到所有的块已分发。静态和动态调度,控制每块迭代的数量有一个可选的块参数。所以,让我们看看一个例子:#include <stdio.h>
#include <stdlib.h>
int main(argc,argv)
int argc; char *argv[];
{
double sum;
double a [256], b [256];
int status;
int n=256;
int i;
for ( i = 0; i < n; i++) {
a [i] = i * 0.5;
b [i] = i * 2.0;
}
sum = 0;
#pragma omp parallel for reduction(+:sum)
for (i = 1; i <= n; i++ ) {
sum = sum + a[i]*b[i];
}
printf ("sum = %f \n", sum);
}
编译:C:\ .. \ .. \ GT,CL dot.c输出{C}
在OpenMP的,所有的数据是由默认共享。在这种情况下,我们能够并行循环只需插入一个指令,它告诉编译器并行化,并确定为减少变量的总和。分配循环迭代的线程,有不同的线程的细节建设部分的资金,他们进入一个全球性的总结积累的编译器。为了默认情况下,重复,几乎所有的OpenMP线程程序中的变量是线程之间的共享。共享访问规则的例外是:循环工作共享构造(每个线程必须有它自己的拷贝,才能正确地遍历迭代分配),循环的索引变量声明的变量在并行区域内或在宣布并行区域内调用的函数,以及任何其他变量,线程的堆栈(例如,函数参数)。如果使用内循环在C / C,工作共享构造嵌套循环,循环的索引变量紧接构造会自动进行私人。如果其他变量必须是本地线程,如嵌套循环的循环索引变量,添加一个私有条款有关的构造。将会为每个线程分配一个本地副本列表中的变量。内私人条款中列出的变量的初始值将是不确定的,您必须指定值之前,将它们的使用区域内读。
OpenMP的同步结构,确保您的关键地区相互排斥。使用这些变量时必须保持所有线程共享,但对这些变量进行更新必须在并行区域。关键构造的行为就像一个围绕一个临界区的锁。只有一个线程可以执行一次在一个受保护的的关键地区。希望有机会的关键地区的其他线程必须等待,直到没有线程正在执行的关键地区。现在,因为我们说,默认情况下,几乎所有的OpenMP线程程序中的变量是线程之间共享(即,线程之间传递的数据),我们可以回忆到这个规则的例外,并把它使用此共享访问规则有一个例外:循环的索引变量与工作共享循环相关结构。
这段代码是一个循环工作共享的例子。在这个例子中,一个循环迭代动态预计整个团队线程。一次执行一个线程将被定为下一步的工作块块迭代:
输出#include <omp.h>
#include <sdio.h>
#include <stdlib.h>
#define CHUNKSIZE 10
#define N 100
int main (int argc, char *argv[])
{
int nthreads, tid, i, chunk;
float a[N], b[N], c[N];
/* Some initializations */
for (i=0; i < N; i++)
a[i] = b[i] = i * 1.0;
chunk = CHUNKSIZE;
#pragma omp parallel shared(a,b,c,nthreads,chunk) private(i,tid)
{
tid = omp_get_thread_num();
if (tid == 0)
{
nthreads = omp_get_num_threads();
printf("Number of threads = %d\n", nthreads);
}
printf("Thread %d starting...\n",tid);
#pragma omp for schedule(dynamic,chunk)
for (i=0; i<n; c[%d]="%f\n",tid,i,c[i]);")
c[i]="a[i]";
Number of threads = 1
Thread 0 starting...
Thread 0: c[0]= 0.000000
Thread 0: c[1]= 2.000000
Thread 0: c[2]= 4.000000
Thread 0: c[3]= 6.000000
Thread 0: c[4]= 8.000000
Thread 0: c[5]= 10.000000
Thread 0: c[6]= 12.000000
Thread 0: c[7]= 14.000000
Thread 0: c[8]= 16.000000
Thread 0: c[9]= 18.000000
Thread 0: c[10]= 20.000000
Thread 0: c[11]= 22.000000
Thread 0: c[12]= 24.000000
Thread 0: c[13]= 26.000000
Thread 0: c[14]= 28.000000
Thread 0: c[15]= 30.000000
Thread 0: c[16]= 32.000000
Thread 0: c[17]= 34.000000
Thread 0: c[18]= 36.000000
Thread 0: c[19]= 38.000000
Thread 0: c[20]= 40.000000
. . . stopped here for brevity's sake..
下一个例子涉及合并减少并行循环。它演示了联合并行循环内兴建的总和减少。请注意,默认的数据元素的范围是假设。有没有指定共享或私有变量的条款。 OpenMP的会自动使团队内的私人循环的索引变量:
输出#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
int i, n;
float a[100], b[100], sum;
/* Some initializations */
n = 100;
for (i=0; i < n; i++)
a[i] = b[i] = i * 1.0;
sum = 0.0;
#pragma omp parallel for reduction(+:sum)
for (i=0; i < n; i++)
sum = sum + (a[i] * b[i]);
printf(" Sum = %f\n",sum);
}
Sum = 328350.000000
一个共同的计算是总结或减少大量的数据到一个单一的值集合。例如,这可能包括的数据项的总和或最大或最小数据集。算法做这样的计算有一个用来收集的部分和最后的答案共享变量的依赖。 OpenMP提供了子句来处理并发减少的细节。减少条款的要求相结合的数据,以及减少变量列表的关联和交换操作。并行团队中的每个线程将收到的减少变量的私有拷贝,执行分配计算时使用。与私人条款中的变量,这些私有变量的初始化值,减少操作取决于。地区减少条款,所有的本地副本,使用中的条款指出操作相结合,结果存储在变量的共享副本。因此,在这一点上,让我们看看在数值计算程序,然后看看在线程并行的第二个版本。
在微积分,当我们要计算曲线下方的面积,我们绘制矩形的方法曲线的线。该曲线是连续的,这意味着它在每一点的切线。也就是说,在每一个点,包括曲线,有一个变化,改变方向。这也是所谓的差分系数。每个矩形的中点,和矩形的高度和宽度。窄的矩形,其中多有总结。这就是所谓接近该地区。其实,数值积分计算函数曲线下的面积近似的方法,尤其是当确切的积分不能得到解决。例如,可以定义常数pi的值由下面的积分。然而,而不是不可或缺的完全解决这个,我们可以近似数值积分使用的解决方案:
{S1}
在下面的例子中的代码是一个数值积分中点矩形的规则,以解决积分刚刚展示的实施。要计算曲线下的面积近似,我们必须通过寻找每个矩形的中点(中)和计算,矩形(高度),高度,这是简单的功能的一些矩形(num_rects)面积计算值,中点。我们加在一起所有的矩形(总和),经计算,我们高度的总和乘以矩形的宽度(宽)总面积(区),以确定所需的近似PI值的高度。这不是一个线程应用程序,但代码显示如何可以线程,当我们写的第二个例子:#include <stdio.h>
static long num_rects=100000;
void main()
{
int i;
double mid, height, width, sum = 0.0;
double area;
width = 1.0/(double) num_rects;
for (i = 0; i < num_rects; i++){
mid = (i + 0.5) * width;
height = 4.0/(1.0 + mid*mid);
sum += height;
}
area = width * sum;
printf("Computed pi = %f\n",area);
}
计算PI = 3.141593
第二个应用程序计算pi的近似数值积分和中点矩形规则,在第一个例子使用的价值。代码分为num_rect间隔一体化范围,并计算每个区间的中点(矩形)的功能价值4.0 /(1 × 2)。换句话说,在创建一个连续的OpenMP程序的第一步是要找出它包含的并行。基本上,这意味着寻找指示,指令序列,或者甚至大地区可能由不同的处理器同时执行的代码:#include <stdio.h>
#include <stdlib.h>
static long num_rects = 1000000;
int main(int argc, char* argv[])
{
double mid, height, width, sum=0.0;
int i;
double area;
width = 1./(double)num_rects;
#pragma omp parallel for private(mid, height) reduction(+:sum)
for (i=0; i < num_rects; i++) {
mid = (i + 0.5)*width;
height = 4.0/(1.+ mid*mid);
sum += height;
}
area = width * sum;
printf("The value of PI is %f\n",area);
}
pi的值是3.141593。
的OpenMP循环工作共享构造已被添加。对OpenMP标准的编译器将插入代码产生一个线程的团队,给私人复制的中期,高度,我给每个线程变量,瓜分了线程之间的循环迭代,终于,当线程分配计算完成后,结合在总结本地副本存储到共享的版本值。这共享的总和副本将被用于计算PI的近似时,区间的宽度乘以。
最后,让我们来看看一个矩阵乘法代码:
输出#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#define NRA 62 /* number of rows in matrix A */
#define NCA 15 /* number of columns in matrix A */
#define NCB 7 /* number of columns in matrix B */
int main (int argc, char *argv[])
{
int tid, nthreads, i, j, k, chunk;
double a[NRA][NCA], /* matrix A to be multiplied */
b[NCA][NCB], /* matrix B to be multiplied */
c[NRA][NCB]; /* result matrix C */
chunk = 10; /* set loop iteration chunk size */
/*** Spawn a parallel region explicitly scoping all variables ***/
#pragma omp parallel shared(a,b,c,nthreads,chunk) private(tid,i,j,k)
{
tid = omp_get_thread_num();
if (tid == 0)
{
nthreads = omp_get_num_threads();
printf("Starting matrix multiple example with %d threads\n",nthreads);
printf("Initializing matrices...\n");
}
/*** Initialize matrices ***/
#pragma omp for schedule (static, chunk)
for (i=0; i < NRA; i++)
for (j=0; j < NCA; j++)
a[i][j] = i + j;
#pragma omp for schedule (static, chunk)
for (i=0; i<NCA; i++)
for (j=0; j < NCB; j++)
c[i][j] = 0;
/*** Do matrix multiply sharing iterations on outer loop ***/
/*** Display who does which iterations for demonstration purposes ***/
printf("Thread %d starting matrix multiply...\n",tid);
#pragma omp for schedule (static, chunk)
for (i=0; i < NRA; i++) {
for (j=0; j < NCB; j++)
for (k = 0; k < NCA; k++)
c[i][j] += a[i][k] * b[k][j];
}
} // end of parallel region
//**Print Results**//
printf("******************************************************\n");
printf("Result Matrix:\n");
for (i=0; i < NRA; i++)
{
for (j = 0; j < NCB; j++)
printf("%6.2f ", c[i][j]);
printf("\n");
}
printf("******************************************************\n");
printf("Done\n");
}
Starting matrix multiple example with 2 threads
Initializing matrices...
Thread 0 starting matrix multiply...
Thread 1 starting matrix multiply...
Thread=0 did row=0
Thread=1 did row=10
Thread=0 did row=1
Thread=1 did row=11
Thread=0 did row=2
Thread=1 did row=12
Thread=0 did row=3
Thread=1 did row=13
Thread=0 did row=4
Thread=1 did row=14
Thread=0 did row=5
Thread=1 did row=15
Thread=0 did row=6
Thread=1 did row=16
Thread=0 did row=7
Thread=1 did row=17
Thread=0 did row=8
Thread=1 did row=18
Thread=0 did row=9
Thread=1 did row=19
Thread=0 did row=20
Thread=1 did row=30
Thread=0 did row=21
Thread=1 did row=31
Thread=0 did row=22
Thread=1 did row=32
Thread=0 did row=23
Thread=1 did row=33
Thread=0 did row=24
Thread=1 did row=34
Thread=0 did row=25
Thread=1 did row=35
Thread=0 did row=26
Thread=1 did row=36
Thread=0 did row=27
Thread=1 did row=37
Thread=0 did row=28
Thread=1 did row=38
Thread=0 did row=29
Thread=1 did row=39
Thread=0 did row=40
Thread=1 did row=50
Thread=0 did row=41
Thread=1 did row=51
Thread=0 did row=42
Thread=1 did row=52
Thread=0 did row=43
Thread=1 did row=53
Thread=0 did row=44
Thread=1 did row=54
Thread=0 did row=45
Thread=1 did row=55
Thread=0 did row=46
Thread=1 did row=56
Thread=0 did row=47
Thread=1 did row=57
Thread=0 did row=48
Thread=1 did row=58
Thread=0 did row=49
Thread=1 did row=59
Thread=0 did row=60
Thread=0 did row=61
******************************************************
Result Matrix:
0.00 1015.00 2030.00 3045.00 4060.00 5075.00 6090.00
0.00 1120.00 2240.00 3360.00 4480.00 5600.00 6720.00
0.00 1225.00 2450.00 3675.00 4900.00 6125.00 7350.00
0.00 1330.00 2660.00 3990.00 5320.00 6650.00 7980.00
0.00 1435.00 2870.00 4305.00 5740.00 7175.00 8610.00
0.00 1540.00 3080.00 4620.00 6160.00 7700.00 9240.00
0.00 1645.00 3290.00 4935.00 6580.00 8225.00 9870.00
0.00 1750.00 3500.00 5250.00 7000.00 8750.00 10500.00
0.00 1855.00 3710.00 5565.00 7420.00 9275.00 11130.00
0.00 1960.00 3920.00 5880.00 7840.00 9800.00 11760.00
0.00 2065.00 4130.00 6195.00 8260.00 10325.00 12390.00
0.00 2170.00 4340.00 6510.00 8680.00 10850.00 13020.00
0.00 2275.00 4550.00 6825.00 9100.00 11375.00 13650.00
0.00 2380.00 4760.00 7140.00 9520.00 11900.00 14280.00
0.00 2485.00 4970.00 7455.00 9940.00 12425.00 14910.00
0.00 2590.00 5180.00 7770.00 10360.00 12950.00 15540.00
0.00 2695.00 5390.00 8085.00 10780.00 13475.00 16170.00
0.00 2800.00 5600.00 8400.00 11200.00 14000.00 16800.00
0.00 2905.00 5810.00 8715.00 11620.00 14525.00 17430.00
0.00 3010.00 6020.00 9030.00 12040.00 15050.00 18060.00
0.00 3115.00 6230.00 9345.00 12460.00 15575.00 18690.00
0.00 3220.00 6440.00 9660.00 12880.00 16100.00 19320.00
0.00 3325.00 6650.00 9975.00 13300.00 16625.00 19950.00
0.00 3430.00 6860.00 10290.00 13720.00 17150.00 20580.00
0.00 3535.00 7070.00 10605.00 14140.00 17675.00 21210.00
0.00 3640.00 7280.00 10920.00 14560.00 18200.00 21840.00
0.00 3745.00 7490.00 11235.00 14980.00 18725.00 22470.00
0.00 3850.00 7700.00 11550.00 15400.00 19250.00 23100.00
0.00 3955.00 7910.00 11865.00 15820.00 19775.00 23730.00
0.00 4060.00 8120.00 12180.00 16240.00 20300.00 24360.00
0.00 4165.00 8330.00 12495.00 16660.00 20825.00 24990.00
0.00 4270.00 8540.00 12810.00 17080.00 21350.00 25620.00
0.00 4375.00 8750.00 13125.00 17500.00 21875.00 26250.00
0.00 4480.00 8960.00 13440.00 17920.00 22400.00 26880.00
0.00 4585.00 9170.00 13755.00 18340.00 22925.00 27510.00
0.00 4690.00 9380.00 14070.00 18760.00 23450.00 28140.00
0.00 4795.00 9590.00 14385.00 19180.00 23975.00 28770.00
0.00 4900.00 9800.00 14700.00 19600.00 24500.00 29400.00
0.00 5005.00 10010.00 15015.00 20020.00 25025.00 30030.00
0.00 5110.00 10220.00 15330.00 20440.00 25550.00 30660.00
0.00 5215.00 10430.00 15645.00 20860.00 26075.00 31290.00
0.00 5320.00 10640.00 15960.00 21280.00 26600.00 31920.00
0.00 5425.00 10850.00 16275.00 21700.00 27125.00 32550.00
0.00 5530.00 11060.00 16590.00 22120.00 27650.00 33180.00
0.00 5635.00 11270.00 16905.00 22540.00 28175.00 33810.00
0.00 5740.00 11480.00 17220.00 22960.00 28700.00 34440.00
0.00 5845.00 11690.00 17535.00 23380.00 29225.00 35070.00
0.00 5950.00 11900.00 17850.00 23800.00 29750.00 35700.00
0.00 6055.00 12110.00 18165.00 24220.00 30275.00 36330.00
0.00 6160.00 12320.00 18480.00 24640.00 30800.00 36960.00
0.00 6265.00 12530.00 18795.00 25060.00 31325.00 37590.00
0.00 6370.00 12740.00 19110.00 25480.00 31850.00 38220.00
0.00 6475.00 12950.00 19425.00 25900.00 32375.00 38850.00
0.00 6580.00 13160.00 19740.00 26320.00 32900.00 39480.00
0.00 6685.00 13370.00 20055.00 26740.00 33425.00 40110.00
0.00 6790.00 13580.00 20370.00 27160.00 33950.00 40740.00
0.00 6895.00 13790.00 20685.00 27580.00 34475.00 41370.00
0.00 7000.00 14000.00 21000.00 28000.00 35000.00 42000.00
0.00 7105.00 14210.00 21315.00 28420.00 35525.00 42630.00
0.00 7210.00 14420.00 21630.00 28840.00 36050.00 43260.00
0.00 7315.00 14630.00 21945.00 29260.00 36575.00 43890.00
0.00 7420.00 14840.00 22260.00 29680.00 37100.00 44520.00
*************************************************************************
Done.
鉴于实现更大的SMP和多线程电脑发展趋势,这是极其重要的战略和创建共享内存并行程序的技术成为广为人知。解释如何使用OpenMP的结合主要的编程语言如Fortran,C和C写这样的并行程序,是这篇文章的目的。参考文献并发性的艺术,由粘土Breshears