并行计算

OpenMP编程

OpenMP是一种面向共享内存以及分布式共享内存的多处理器多线程的并行编程语言。

OpenMP编程模型

Fork-Join

  1. 开始执行时,只有主线程存在
  2. 主线程运行过程中,当遇到需要并行计算的时候,Fork新线程来并行执行任务。
  3. 并行执行时,主线程和派生线程共同工作
  4. 并行部分执行结束后,派生线程退出并挂起,不再工作,控制流回到单独的主线程中(Join)

编译制导语句

编译制导语句是在编译器编译程序时,识别特定的、包含OpenMP程序语义的注释

在C/C++程序中,用#pragma omp parallel来标识一段并行程序块。在一个无法识别OpenMP语义的普通编译器中,这些特定的注释会被视作普通的注释而被忽略。

并行域

并行域中的代码被所有线程执行

1
#pragma omp parallel [clause[[,] clause] ...] newline

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <omp.h>

int main() {
    int nthreads, tid;

    // 创建多个线程
    #pragma omp parallel private(tid) 
    {
        // 获取线程ID
        tid = omp_get_thread_num();
        printf("Hello World from thread = %d\n", tid);

        // 仅主线程执行此段
        if (tid == 0) {
            nthreads = omp_get_num_threads();
            printf("Number of threads = %d\n", nthreads);
        }
    }   // 所有线程在此处回到主线程

    return 0;
}

共享任务

共享任务结构将它所包含的代码划分给线程组的各成员来执行。

for语句

for语句:指定紧随它的循环语句必须由线程组并行执行

1
#pragma omp for[clause[[,] clause]...] newline

schedule(type, [,chunck]):描述如何将循环的迭代划分给线程组中的线程,其中chunck表示每个线程分配的计算量(未指定则尽可能平均分配),type则有static(静态划分)和dynamic(动态划分)

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <omp.h>
#define CHUNKSIZE 100
#define N 1000

int main() {
    int i, chunk;
    float a[N], b[N], c[N];

    for (i=0; i<M; i++) {
        a[i] = b[i] = i * 1.0;
        chunk = CHUNKSIZE;
    }

    #pragma omp parallel shared(a, b, c, chunk) private(i)
    {
        #pragma omp for schedule(dynamic, chunk) nowait
        for (i=0; i<N; i++) {
            c[i] = a[i]+b[i];
        }
    }
    return 0;
}

sections语句

sections编译制导语句指定内部的代码被划分给线程组中的各线程,不同的section由不同的线程执行

1
2
3
4
5
6
7
#pragma omp sections [clause [[,] clause] ...] newline
{
    #pragma omp section newline
    ...
    #pragma omp section newline
    ...
}

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <omp.h>
#define N 1000

int main() {
    int i;
    float a[N], b[N], c[N], d[N];

    for (i=0; i<N; i++) {
        a[i] = i * 1.5;
        b[i] = i + 22.35;

        #pragma omp parallel shared(a, b, c, d) private(i)
        {
            #pragma omp sections nowait
            {
                #pragma omp section
                for (i=0; i<N; i++) {
                    c[i] = a[i] + b[i];
                }
                #pragma omp section
                for (i=0; i<N; i++) {
                    d[i] = a[i] * b[i];
                }
            }
        }
    }

    return 0;
}

single语句

single编译制导语句指定内部代码只有线程组中的一个线程执行,线程组中没有执行single语句的线程会一直等待代码块的结束(使用nowait子句除外)

1
#pragma omp single [clause [[,] clause ...]] newline

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <omp.h>

void work1() {}
void work2() {}

int main() {

    #pragma omp parallel
    {
        #pragma omp single
        printf("Beginning work1. \n");
        work1();

        #pragma omp single
        printf("Finishing work1.\n");

        #pragma omp single nowait
        printf("Finishing work1 and beginning work2.\n");
        work(2);
    }

    return 0;
}

parallel for语句

parallel for编译制导语句表明一个并行域包含一个独立的for语句

1
#pragma omp parallel for [clause ...] newline

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <omp.h>
#define N 1000
#define CHUNKSIZE 100

int main() {
    int i, chunk;
    float a[N], b[N], c[N];

    for (i=0; i<N; i++) {
        a[i] = b[i] = i * 1.0;
    }
    chunk = CHUNKSIZE;
    #pragma omp parallel for shared(a,b,c,chunk) private(i) schedule(static, chunk)
    for (i=0; i<n; i++) {
        c[i] = a[i] + b[i];
    }
}

parallel sections语句

parallel sections编译制导语句表明一个并行域包含单独的一个section语句

1
#pragma omp parallel sections [clause ...] newline

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <omp.h>

void XAXIS();
void YAXIS();
void ZAXIS();

int main() {
    #pragma omp parallel sections
    {
        #pragma omp section
        XAXIS();

        #pragma omp section
        YAXIS();

        #pragma omp section
        ZAXIS();
    }
}

同步

制导语句 作用 格式
master 指定代码段只有主线程执行 #pragma omp master newline
critical 表明域中的代码一次只能在一个线程执行,其他线程被阻塞在临界区 #pragma omp critical [name] newline
barrier 同步一个线程组中的所有线程,先到达的在此阻塞,等待其他线程 #pragma omp barrier newline
atomic 指定特定的存储单元将被原子更新 #pragma omp atomic newline
flush 用以标识一个同步点,用以确保所有的线程看到一致的存储器视图 #pragma omp flush (list) newline
order 指出其所包含循环的执行按照循环次序进行,任何时候只能有一个线程执行被ordered限制的部分 #pragma omp ordered newline

数据域

数据域共享属性子句

子句 作用 格式
private 表示它列出的变量对于每个线程是局部的 private(list)
shared 表示它所列出的变量被线程组中的所有线程共享,所有线程都能对它进行读写访问 shared(list)
default 让用户自行规定在一个并行域的静态范围内所定义的变量的缺省作用范围 default(shared | none)
firstprivate private子句的超集,对变量作原子初始化 firstprivate(list)
lastprivate private子句的超集,将变量从最后的循环迭代或段复制给原始的变量 lastprivate(list)
reduction 使用指定的操作对其列表中出现的变量进行规约,初始时每个线程保留一份私有拷贝,结构尾部对变量进行规约并更新全局值 reduction(operator: list)
threadprivate 使一个全局文件作用域的变量在并行域内变为每个线程私有,复制一份私有拷贝 #pragma omp threadprivate (list) newline
copyin 为线程组中所有线程的threadprivate变量赋相同的值,主线程中该变量的值作为初始值 copyin(list)
copyprivate 用一个私有变量将一个值从一个线程广播到执行同一个并行区域的其他线程 copyprivate(list)

运行库与环境变量

OpenMP标准定义了一个应用编程接口来调用库中的函数

对于C/C++,需要在程序开头引用文件<omp.h>

环境变量 含义
OMP_SCHEDULE 线程调度类型,只能用到forparallel for
OMP_NUM_THREADS 定义执行中最大的线程数
OMP_DYNAMIC 通过设定变量值TRUEFALSE来确定是否动态设定并行域执行的线程数
OMP_NESTED 确定是否可以并行嵌套
网站总访客数:Loading

使用 Hugo 构建
主题 StackJimmy 设计