OpenMP

begin

请添加图片描述
请添加图片描述

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

通过使用预处理指令来让程序并行化

1 #pragma omp 指令

#pragma omp 指令 [子句[子句]…]
#include <stdio.h>
#include <omp.h>

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

在这里插入图片描述
程序并行执行

2 fork/join

用于共享内存并行系统
多处理器程序设计
OpenMP并行执行的程序要全部结束后才会运行后面非并行部分的代码( fork/join并行模式 )

#include <stdio.h>
#include <time.h>

void foo()
{
	int cnt = 0;
	int t1 = clock();
	int i;
	for (i = 0; i < 1e8; i++) {
		cnt++;
	}
	int t2 = clock();
	printf("Time = %d\n", t2 - t1);
}

int main(int argc, char* argv[])
{
	int t1 = clock();
	int i;
	// YOUR CODE HERE
	#pragma omp parallel for 
	// END OF YOUR CODE
	for (i = 0; i < 2; i++) {
		foo();
	}
	int t2 = clock();
	printf("Total time = %d\n", t2 - t1);
	return 0;
}

栗子:
求代码运行的时间
若没进行openmd并行,total time应该接近前两个时间之和
but进行openmd并行后,total time和前两个时间接近
在这里插入图片描述

3 #pragma omp parallel

parallel 是构造并行块的指令
可以配合for, sections一起使用
该指令后面用大括号来指定需要并行计算的代码

#pragma omp parallel [for | sections] [子句[子句]…] 
{ 
//并行部分 
}
#include <stdio.h>
#include <omp.h>

int main(int argc, char* argv[])
{
	// YOUR CODE HERE
    #pragma omp parallel num_threads(6)
	// END OF YOUR CODE
	{
		printf("Thread: %d\n", omp_get_thread_num());
	}
	return 0;
}

#pragma omp parallel num_threads(6) 使程序并行执行
在这里插入图片描述

4 #pragma omp for

for 指令的使用方法
使一个for循环在多个线程中执行
for指令会与parallel指令同时使用(即parallel for指令
还可以在parallel指令的并行块中单独使用
一个并行块中可以使用多个for指令

#pragma omp parallel
	{
		#pragma omp for
	}
#include <stdio.h>
#include <omp.h>

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

在这里插入图片描述

5 sections

sections语句可以将代码分成不同的分块
section指令来指定分块
每一个分块并行执行

#pragma omp [parallel] sections [子句]
{
#pragma omp section
{
//代码
}}

填上使用sections指令使程序并行执行

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

int main(int argc, char* argv[])
{
	// YOUR CODE HERE
	#pragma omp parallel sections
	// END OF YOUR CODE
	{
		#pragma omp section 
		printf("Section 1 ThreadId = %d\n", omp_get_thread_num());
		#pragma omp section
		printf("Section 2 ThreadId = %d\n", omp_get_thread_num());
		#pragma omp section
		printf("Section 3 ThreadId = %d\n", omp_get_thread_num());
		#pragma omp section
		printf("Section 4 ThreadId = %d\n", omp_get_thread_num());
	}
	return 0;
}

在这里插入图片描述

7 private

private 子句可以将变量声明为线程私有
线程私有变量:每个线程都有一个该变量的副本,线程之间不会互相影响,其他线程无法访问其他线程的副本。
原变量在并行部分不起任何作用,也不会受到并行部分内部操作的影响。

#pragma omp parallel for private(i)
#include <stdio.h>
#include <omp.h>

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

在这里插入图片描述

8 firstprivate

private子句不能继承原变量的值
可以使用firstprivate子句来使线程私有变量继承原来变量的值

#pragma omp parallel for firstprivate(t)
#include <stdio.h>
#include <omp.h>

int main(int argc, char* argv[])
{
	int t = 20, i;
	// YOUR CODE HERE
	#pragma omp parallel for firstprivate(t)
	// END OF YOUR CODE
	for (i = 0; i < 5; i++)
	{
		t += i;
		printf("t = %d\n", t);
	}
	printf("outside t = %d\n", t);
	return 0;
}

在这里插入图片描述

9 astprivate

astprivate子句实现:退出并行部分时将计算结果赋值回原变量

根据OpenMP规范:在循环迭代中,是最后一次迭代的值赋值给原变量;如果是section结构,那么是程序语法上的最后一个section语句赋值给原变量。


如果类(class)变量作为lastprivate的参数时,需要一个缺省构造函数,除非该变量也作为firstprivate子句的参数;此外还需要一个拷贝赋值操作符。

#pragma omp parallel for firstprivate(t), lastprivate(t)
#include <stdio.h>
#include <omp.h>

int main(int argc, char* argv[])
{
	int t = 20, i;
	// YOUR CODE HERE
	#pragma omp parallel for firstprivate(t), lastprivate(t)
	// END OF YOUR CODE
	for (i = 0; i < 5; i++)
	{
		t += i;
		printf("t = %d\n", t);
	}
	printf("outside t = %d\n", t);
	return 0;
}

10 Share

Share子句可以将一个变量声明成共享变量
在多个线程内共享
attention:
在并行部分进行写操作时,要求共享变量进行保护,否则不要随便使用共享变量,尽量将共享变量转换为私有变量使用

#pragma omp parallel for shared(t)
#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;
}

在这里插入图片描述

11 reduction

reduction 对一个或者多个参数指定一个操作符
每一个线程都会创建这个参数的私有拷贝
在并行区域结束后,迭代运行指定的运算符,并更新原参数的值。
私有拷贝变量的初始值依赖于redtution的运算类型。

#pragma omp parallel for reduction(+: sum)
#include <stdio.h>
#include <omp.h>

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

在这里插入图片描述

12 omp_get_dynamic

omp_get_dynamic
返回当前程序是否允许在运行时动态调整并行区域的线程数

int omp_get_dynamic()
#include <stdio.h>
#include <omp.h>

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

在这里插入图片描述

13 omp_set_dynamic

omp_set_dynamic
设置是否允许在运行时动态调整并行区域的线程数

void omp_set_dynamic(int)

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

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

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

在这里插入图片描述

14 copyin

copyin
将主线程中变量的值拷贝到各个线程的私有变量中,让各个线程可以访问主线程中的变量。
其参数必须要被声明称threadprivate,对于类的话则并且带有明确的拷贝赋值操作符。

#pragma omp parallel for copyin(g)
#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;
}

加copyin
在这里插入图片描述没加:
在这里插入图片描述

15 static调度

static
parallel for没有带schedule时,系统默认采用static调度方式。
假设有n次循环迭代,t个线程,那么每个线程大约分到n/t次迭代。
这种调度方式会将循环迭代均匀的分布给各个线程,各个线程迭代次数可能相差1次。

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;
}

在这里插入图片描述

16 size参数

静态调度,通过指定size参数来分配一个线程的最小迭代次数。
指定size后,每个线程最多可能相差size次迭代。
可以推断出[0,size-1]的迭代是在第一个线程上运行
依次类推。

#pragma omp parallel for schedule(static, 3)
#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;
}

在这里插入图片描述
SIZE=3 差3次迭代

17 dynamic

dynamic
动态分配是将迭代动态分配到各个线程
依赖运行状态来确定
所以无法像静态调度一样事先预计进程的分配
哪一个线程先启动,哪一个线程迭代多久,都取决于系统的资源和线程的调度

#pragma omp parallel for schedule(dynamic)
#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;
}

在这里插入图片描述
thread值很动态 @.@

18 omp_get_num_procs

omp_get_num_procs

返回调用函数时可用的处理器数目。

int omp_get_num_procs(void)
#include <stdio.h>
#include <omp.h>

int main(int argc, char* argv[])
{
	printf("%d\n", omp_get_num_procs());
	#pragma omp parallel  
	{
	    // YOUR CODE HERE
	    printf("%d\n", omp_get_num_procs());
	    // END OF YOUR CODE
	}
	return 0;
}

在这里插入图片描述

19 omp_get_num_procs

omp_get_num_threads
返回当前并行区域中的活动线程个数
如果在并行区域外部调用,返回1

int omp_get_num_threads(void)
#include <stdio.h>
#include <omp.h>

int main(int argc, char* argv[])
{
	printf("%d\n", omp_get_num_threads());
	#pragma omp parallel  
	{
	   	// YOUR CODE HERE
		printf("%d\n", omp_get_num_threads());
		// YOUR CODE HERE
	}
	return 0;
}

在这里插入图片描述

20 omp_get_thread_num

omp_get_thread_num
返回当前的线程号

int omp_get_thread_num(void)
#include <stdio.h>
#include <omp.h>

int main(int argc, char* argv[])
{
	printf("%d\n", omp_get_thread_num());
	#pragma omp parallel  
	{	    
	    // YOUR CODE HERE
	    printf("%d\n", omp_get_thread_num());
	    // END OF YOUR CODE
	}
	return 0;
}

在这里插入图片描述

21 omp_set_num_threads

omp_set_num_threads
设置进入并行区域时,将要创建的线程个数

int omp_set_num_threads(void)
#include <stdio.h>
#include <omp.h>

int main(int argc, char* argv[])
{
    // YOUR CODE HERE
    omp_set_num_threads(4);
    // END OF YOUR CODE
	#pragma omp parallel  
	{
		printf("%d of %d threads\n", omp_get_thread_num(), omp_get_num_threads());
	}
	return 0;
}

在这里插入图片描述

22 omp_in_parallel

omp_in_parallel
可以判断当前是否处于并行状态

int omp_in_parallel();
#include <stdio.h>
#include <omp.h>

int main(int argc, char* argv[])
{
	printf("%d\n", omp_in_parallel());
	omp_set_num_threads(4);
	#pragma omp parallel  
	{
	    // YOUR CODE HERE
		printf("%d\n", omp_in_parallel());
	    // END OF YOUR CODE
	}
	return 0;
}

在这里插入图片描述

23 omp_get_max_threads

omp_get_max_threads
可以用于获得最大的线程数量,根据OpenMP文档中的规定,这个最大数量是指在不使用num_threads的情况下,OpenMP可以创建的最大线程数量。
需要注意的是这个值是确定的,与它是否在并行区域调用没有关系。

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

int main(int argc, char* argv[])
{
    // YOUR CODE HERE
	printf("%d\n", omp_get_max_threads());
	// END OF YOUR CODE
	omp_set_num_threads(4);
	#pragma omp parallel  
	{
		printf("%d\n", omp_get_max_threads());
	}
	return 0;
}

在这里插入图片描述

24 OpenMP互斥锁

OpenMP中互斥锁
Openmp提供一系列函数来进行锁的操作
常用的4个函数:

void omp_init_lock(omp_lock*) 初始化互斥锁
void omp_destroy_lock(omp_lock*) 销毁互斥锁
void omp_set_lock(omp_lock*) 获得互斥锁
void omp_unset_lock(omp_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;
}

在这里插入图片描述

25 omp_test_lock

omp_test_lock
还有一个与互斥锁的相关的函数,用来尝试获得锁。
该函数可以看作是omp_set_lock的非阻塞版本。

bool omp_test_lock(omp_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
		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;
}

在这里插入图片描述

end

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值