HPC-高性能计算
说起高性能当然是atri了!
robo真是太屑了(
不过真的好多刀,建议不玩呜呜
不扯了,正经点
由于学校server里的c版本比较低,所以大部分是用c语言写的
mpi
这部分打到服务器上去了。。找时间把他们下回来qwq
openmp
常用编译指导语句
并行域结构
parallel语句:创建线程组并行执行程序
共享任务结构
for循环语句:将for循环分配给各线程并行执行
sections语句:非迭代共享任务结构,将任务分配给各线程
single语句:将代码段交由1个线程执行
同步与临界区
barrier语句:实现线程组中所有线程同步
ordered语句:位于循环内,按序串行执行
critical语句:同一时刻只能有一个线程执行
atomic语句:指定的存储单元被原子地更新
master语句:代码段由主线程执行,其他线程忽略
•OpenMP程序在同一个共享内存空间上执行
•可以任意使用这个共享内存空间上的变量进行线程间的数据传递
•内存分布结构如图
•每一个线程的栈空间都是私有的
•全局变量以及程序代码都是全局共享
•动态分配的堆空间也是共享的
•通过threadprivate指出的数据结构在每一个线程中都会有一个副本
•shared定义变量作用域是共享的
•private定义变量作用域是私有的
1 hello world并行化高性能版
1 2 3 4 5 6 7 8 9 10 11
| #include <stdio.h> #include <omp.h>
int main() { #pragma omp parallel { printf("hello world\n"); } return 0; }
|
2 高性能算pi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <stdlib.h> #include <omp.h> #include <stdio.h> #define NUM_THREADS 16
int main() { static long num_steps=1000000000; double step; int i; double x,pi,sum=0.0; step=1.0/(double) num_steps; omp_set_num_threads(NUM_THREADS); #pragma omp parallel for reduction(+:sum) private(x) for(i=0;i<num_steps;i++) { x=(i+0.5)*step; sum+=4.0/(1.0+x*x); } pi=step*sum; printf("Pi=%21.20f(%ld steps)\n",pi,num_steps); return 0; }
|
3 私有变量的初始化和终结
private子句用于将一个或多个变量声明成线程私有的变量,变量声明成私有变量后,指定每个线程都有它自己的变量私有副本,其他线程无法访问私有副本。即使在并行区域外有同名的共享变量,共享变量在并行区域内不起任何作用,并且并行区域内不会操作到外面的共享变量。
private声明的私有变量不能继承同名变量的值,但实际情况中有时需要继承原有共享变量的值,OpenMP提供了firstprivate子句来实现这个功能。若上述程序使用firstprivate(k),则并行区域内的私有变量k继承了外面共享变量k的值100作为初始值,并且在退出并行区域后,共享变量k的值保持为100未变。
有时在并行区域内的私有变量的值经过计算后,在退出并行区域时,需要将它的值赋给同名的共享变量,前面的private和firstprivate子句在退出并行区域时都没有将私有变量的最后取值赋给对应的共享变量,lastprivate子句就是用来实现在退出并行区域时将私有变量的值赋给共享变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <stdio.h> #include <omp.h> int main(){ int val=8; #pragma omp parallel for firstprivate(val) lastprivate(val) for(int i=0;i<2;i++) { printf("i=%d val=%d\n",i,val); if(i==1) val=10000; printf("i=%d val=%d\n",i,val); } printf("val=%d\n",val); return 0; }
|
4 循环嵌套
在一个循环体内包涵另一个循环体,循环体产生嵌套
循环并行化编译指导语句可以加在任一循环之前,对应的最近的循环语句被并行化,其它部分不变
并行化作用于嵌套循环中的某一个循环,其他部分由执行到的线程负责执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <stdio.h> #include <omp.h>
const int maxn=30; int main() {
int i,j; int a[maxn][maxn]; #pragma omp parallel for for(i=0;i<maxn;i++){ a[0][i]=1; }
for(i=1; i<maxn; i++) { #pragma omp parallel for for (j = 0; j < maxn; j++) { a[i][j] = 2 * a[i - 1][j]; printf("a[%d][%d] = %d\n", i, j, a[i][j]); } } return 0; }
|
下面这个是并行化i的,但是运行时出现了一些问题?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <stdio.h> #include <omp.h>
const int maxn=30; int main() {
int i,j; int a[maxn][maxn]; #pragma omp parallel for for(i=0;i<maxn;i++){ a[0][i]=1; }
#pragma omp parallel for for(i=1; i<maxn; i++) { for (j = 0; j < maxn; j++) { a[i][j] = 2 * a[i - 1][j]; printf("a[%d][%d] = %d\n", i, j, a[i][j]); } } return 0; }
|
5 循环调度策略
static
循环变量区域分为n等份,每个线程平分n份任务
适用:各个cpu性能差距不大
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <stdio.h> #include <omp.h>
const int maxn=30; int main() { int i; #pragma omp parallel for schedule(static, 2) for(i = 0; i < 10; ++i) { printf("thread id = %d value = %d\n",omp_get_thread_num(),i); } return 0; }
|
dynamic
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <stdio.h> #include <omp.h>
const int maxn=30; int main() { int i; #pragma omp parallel for schedule(dynamic, 2) for(i = 0; i < 10; ++i) { printf("thread id = %d value = %d\n",omp_get_thread_num(),i); } return 0; }
|
guided
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <stdio.h> #include <omp.h>
const int maxn=30; int main() { int i; #pragma omp parallel for schedule(guided, 80) for(i = 0; i < 800; ++i) { printf("thread id = %d value = %d\n",omp_get_thread_num(),i); } return 0; }
|
两个cpu,那么任务分配如下:
•第一个任务: [800/(2*2)] = 200
•第二个任务:第一个任务分了200,还有600,那么[600/(2*2)] = 150
•第三个任务:第二个任务分了150,还有450,那么[450/2*2)] = 113
•第四个人任务:第三个任务分了113,还有337,那么[337/(2*2)] = 85
•第五个任务:第四个任务分了85,还有252,那么[252/(2*2)] = 63, 小于声明的80,那么这里为80
•第六个任务:第五个任务分了80,还有172,根据声明,这里为80(因为会小于80)
•第七个任务:第六个任务分了80,还有92,根据声明,这里为80(因为会小于80)
•第八个任务:第七个任务分了80,还有12,根据声明,这里为12(因为不够80)
runtime
schedule(runtime)
6 规约reduction
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <stdio.h> #include <omp.h>
const int maxn=30; int main() { int a=0; int i; #pragma omp parallel for num_threads(10) private(i) for(i=1;i<101;i++) a=a+i; printf("a1=%d\n",a); a=0; #pragma omp parallel for num_threads(10) private(i) reduction(+:a) for(i=1;i<101;i++) a=a+i; printf("a2=%d\n",a); a=0; for(i=1;i<101;i++) a=a+i; printf("a3=%d\n",a); return 0;
}
|
7 section
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <stdio.h> #include <omp.h>
const int maxn=30; int main() { int a,b,c; #pragma omp parallel sections { #pragma omp section a = 1; #pragma omp section b = 2; #pragma omp section c = 3;
} int d=a+b+c; printf("d = %d",d); return 0; }
|
8 互斥锁