首页 > 技术文章 > openMP飞速入门

most-silence 2022-03-17 14:52 原文

openMP 入门指南

参考超算习堂 (easyhpc.net)
主要关注普通用法和锁的使用就行了

parallel

#pragma omp parallel [for|section]
{
    //并行运行句子
}

#pragma omp parallel num_threads(6) //申请6个线程同时处理
{
    
}

for

  1. 与 parallel连用
#pragma omp parallel for
{
    
}
  1. 在并行块中重复使用
#pragma omp parallel
{
    for
        
    for
}

section 和 sections

sections 声明分块 section指定并行块中的分块 每个分块都会并行执行

#pragma omp [parallel] sections
{
    #pragma omp section
    {
        
    }
}

private firstprivate lastprivate threadprivate

因为在并行块中重新定义申请同名变量,等同于只能在并行快分为内使用。

private解决线程的私有变量,每个线程拥有自己的同名变量

#include <stdio.h>
#include <omp.h
int main(int argc, char* argv[])
{
	int i = 20;
	#pragma omp parallel for private(i)
	for (i = 0; i < 10; i++)
	{
		printf("i = %d\n", i);
	}
	printf("outside i = %d\n", i);
	return 0;
}

firstprivate 继承之前的同名变量的值

#include <stdio.h>
#include <omp.h>
int main(int argc, char* argv[])
{
	int t = 20, i;
	#pragma omp parallel for firstprivate(t)
	for (i = 0; i < 5; i++)
	{
		t += i;
		printf("t = %d\n", t);
	}
	printf("outside t = %d\n", t);
	return 0;
}

lastprivate传递值给之后的同名的变量

#include <stdio.h>
#include <omp.h>
int main(int argc, char* argv[])
{
	int t = 20, i;
	#pragma omp parallel forlastprivate(t)
	for (i = 0; i < 5; i++)
	{
		t += i;
		printf("t = %d\n", t);
	}
	printf("outside t = %d\n", t);
	return 0;
}

threadprivate 将变量复制一个私有拷贝给各个线程,各线程有自己的私有全局对象, 最后的写入对象才会对全局写入造成影响. 可同时读写 threadprivate只能用于全局变量或静态变量

#include <stdio.h>
#include <omp.h>

int g = 0;
#pragma omp threadprivate(g)

int main(int argc, char* argv[])
{
	int t = 20, i;
	#pragma omp parallel
	{
		g = omp_get_thread_num(); //并行的线程有自己的副本
	}
	#pragma omp parallel
	{
		printf("thread id: %d g: %d\n", omp_get_thread_num(), g);
	}
	return 0;
}

shared子句

声明内的并行线程共享, 写操作时尽量转为私有变量进行保护

#include <stdio.h>
#include <omp.h>

int main(int argc, char* argv[])
{
	int t = 20, i;
	// YOUR CODE HERE
	#pragma omp parallel for shared(t) //不是副本,大家都可以读写
	// END OF YOUR CODE
	for (i = 0; i < 10; i++)
	{
		if (i % 2 == 0)
			t++;
		printf("i = %d, t = %d\n", i, t);
	}
	return 0;
}

reduction子句

一个操作符, 多个参数, 每个线程创建该参数的私有拷贝, 并行结束后迭代指定运算符,更新参数值

reduction(operator: list, list )

#include <stdio.h>
#include <omp.h>

int main(int argc, char* argv[])
{
	
	int i, sum = 10;
	#pragma omp parallel for reduction(+: sum)
	for (i = 0; i < 10; i++)
	{
		sum += i;
		printf("%d\n", sum);
	}
	printf("sum = %d\n", sum);
	return 0;
}

copyin子句

将main主线程中变量(threadprivate变量) 拷贝刀各线程私有变量中,线程可以访问主线程变量.

#include <stdio.h>
#include <omp.h>

int g = 0;
#pragma omp threadprivate(g) 
int main(int argc, char* argv[])
{
	int i;
	#pragma omp parallel for   
	for (i = 0; i < 4; i++)
	{
		g = omp_get_thread_num();
		printf("thread %d, g = %d\n", omp_get_thread_num(), g);
	}
	printf("global g: %d\n", g);
	// YOUR CODE HERE
	#pragma omp parallel for copyin(g)
	// END OF YOUR CODE
	for (i = 0; i < 4; i++)
		printf("thread %d, g = %d\n", omp_get_thread_num(), g);
	return 0;
}

static子句调度

parallel没带schedule,默认static 均分调度,每个线程n/t次迭代,相差1次左右

pragma omp parallel for schedule(method)

#include <stdio.h>
#include <omp.h>

int main(int argc, char* argv[])
{
	int i;
	// YOUR CODE HERE
	#pragma omp parallel for schedule(static)
	// END OF YOUR CODE
	for (i = 0; i < 10; i++)
	{
		printf("i = %d, thread %d\n", i, omp_get_thread_num());
	}
	return 0;
}

Size 指定最小迭代次数

静态调度,分配给线程size的最小迭代次数, 每个线程最多可能相差size次迭代(可以有线程不迭代)

pragma omp parallel for schedule(static, size)

#include <stdio.h>
#include <omp.h>

int main(int argc, char* argv[])
{
	int i;
	// YOUR CODE HERE
	#pragma omp parallel for schedule(static, 3)
	// END OF YOUR CODE
	for (i = 0; i < 10; i++)
	{
		printf("i = %d, thread %d\n", i, omp_get_thread_num());
	}
	return 0;
}

dynamic子句动态内存分配

无法预先确定哪个线程先后与时间, 取决于系统资源和线程的调度情况

#include <stdio.h>
#include <omp.h>

int main(int argc, char* argv[])
{
	int i;
	// YOUR CODE HERE
	#pragma omp parallel for schedule(dynamic)
	// END OF YOUR CODE
	for (i = 0; i < 10; i++)
	{
		printf("i = %d, thread %d\n", i, omp_get_thread_num());
	}
	return 0;
}

常用参数

omp_get_num_procs() 可用处理器数

omp_get_thread_num()当前的线程号

omp_get_num_threads() 进入并行前要创建 线程的数量

omp_set_num_threads() 创建指定数目线程

omp_in_parallel() 判定并行状态

omp_get_max-threads() 这个最大数量是指在不使用num_threads的情况下,OpenMP可以创建的最大线程数量

互斥锁的用法

void omp_init_lock(omp_lock) 初始化互斥锁
void omp_destroy_lock(omp_lock
) 销毁互斥锁
void omp_set_lock(omp_lock) 获得互斥锁
void omp_unset_lock(omp_lock
) 释放互斥锁

bool omp_test_lock(omp_lock*) 该函数可以看作是omp_set_lock的非阻塞版本。

#include <stdio.h>
#include <omp.h>

static omp_lock_t lock;

int main(int argc, char* argv[])
{
    int i;
	omp_init_lock(&lock); 
	#pragma omp parallel for   
	for (i = 0; i < 5; ++i)
	{
	    // YOUR CODE HERE
		omp_set_lock(&lock);
		// END OF YOUR CODE
		printf("%d+\n", omp_get_thread_num());
		printf("%d-\n", omp_get_thread_num());
		// YOUR CODE HERE
		omp_unset_lock(&lock); 
		// END OF YOUR CODE
	}
	omp_destroy_lock(&lock);
	return 0;
}
#include <stdio.h>
#include <omp.h>

static omp_lock_t lock;

int main(int argc, char* argv[])
{
    int i;
	omp_init_lock(&lock); 
	#pragma omp parallel for   
	for (i = 0; i < 5; ++i)
	{
	    // YOUR CODE HERE
		if (omp_test_lock(&lock))
		// END OF YOUR CODE
		{
			printf("%d+\n", omp_get_thread_num());
			printf("%d-\n", omp_get_thread_num());
			omp_unset_lock(&lock);
		}
		else
		{
			printf("fail to get lock\n");
		}
	}
	omp_destroy_lock(&lock);
	return 0;
}

omp_set_dynamic

运行时动态调整并行区域的线程数

void omp_set_dynamic(int)
当参数为0时,动态调整被禁用。
当参数为非0值时,系统会自动调整线程以最佳利用系统资源

omp_get_dynamic

int omp_get_dynamic() = 1 可以动态调整线程数, 0 不可以调整

推荐阅读