OpenMP中的数据处理子句
相关文档连接:
多核编程中的任务随机竞争模式的概率分析
多核编程中的任务分组竞争模式
多核编程中的负载平衡难题
多核编程中的锁竞争难题
多核编程的几个难题及其应对策略(难题一)
OpenMP并行程序设计(二)
OpenMP并行程序设计(一)
双核CPU上的快速排序效率 
OpenMP创建线程中的锁及原子操作性能比较
10.1.1 private子句
private子句用于将一个或多个变量声明成线程私有的变量,变量声明成私有变量后, 指定每个线程都有它自己的变量私有副本,其他线程无法访问私有副本。即使在并行区域外有同名的共享变量,共享变量在并行区域内不起任何作用,并且并行区域内不会操作到外面的共享变量。
private子句的用法格式如下:
private(list)
下面便是一个使用private子句的代码例子:
int k = 100;
#pragma omp parallel for private(k)
for ( k=0; k < 10; k++)
{
printf("k=%d/n", k);
}
printf("last k=%d/n", k);
上面程序执行后打印的结果如下:
k=6
k=7
k=8
k=9
k=0
k=1
k=2
k=3
k=4
k=5
last k=100
从打印结果可以看出,for循环前的变量k和循环区域内的变量k其实是两个不同的变量。
用private子句声明的私有变量的初始值在并行区域的入口处是未定义的,它并不会继承同名共享变量的值。
出现在reduction子句中的参数不能出现在private子句中。
10.1.2 firstprivate子句
private声明的私有变量不能继承同名变量的值,但实际情况中有时需要继承原有共享变量的值,OpenMP提供了firstprivate子句来实现这个功能。
先看一下以下的代码例子
int k = 100;
#pragma omp parallel for firstprivate(k)
for ( i=0; i < 4; i++)
{
k+=i;
printf("k=%d/n",k);
}
printf("last k=%d/n", k);
上面代码执行后打印结果如下:
k=100
k=101
k=103
k=102
last k=100
从打印结果可以看出,并行区域内的私有变量k继承了外面共享变量k的值100作为初始值,并且在退出并行区域后,共享变量k的值保持为100未变。
10.1.3 lastprivate子句
有时在并行区域内的私有变量的值经过计算后,在退出并行区域时,需要将它的值赋给同名的共享变量,前面的private和firstprivate子句在退出并行区域时都没有将私有变量的最后取值赋给对应的共享变量,lastprivate子句就是用来实现在退出并行区域时将私有变量的值赋给共享变量。
举个例子如下:
int k = 100;
#pragma omp parallel for firstprivate(k),lastprivate(k)
for ( i=0; i < 4; i++)
{
k+=i;
printf("k=%d/n",k);
}
printf("last k=%d/n", k);
上面代码执行后的打印结果如下:
k=100
k=101
k=103
k=102
last k=103
从打印结果可以看出,退出for循环的并行区域后,共享变量k的值变成了103,而不是保持原来的100不变。
由于在并行区域内是多个线程并行执行的,最后到底是将那个线程的最终计算结果赋给了对应的共享变量呢?OpenMP规范中指出,如果是循环迭代,那么是将最后一次循环迭代中的值赋给对应的共享变量;如果是section构造,那么是最后一个section语句中的值赋给对应的共享变量。注意这里说的最后一个section是指程序语法上的最后一个,而不是实际运行时的最后一个运行完的。
如果是类(class)类型的变量使用在lastprivate参数中,那么使用时有些限制,需要一个可访问的,明确的缺省构造函数,除非变量也被使用作为firstprivate子句的参数;还需要一个拷贝赋值操作符,并且这个拷贝赋值操作符对于不同对象的操作顺序是未指定的,依赖于编译器的定义。
10.1.4 threadprivate子句
threadprivate子句用来指定全局的对象被各个线程各自复制了一个私有的拷贝,即各个线程具有各自私有的全局对象。
用法如下:
#pragma omp threadprivate( list ) new-line
下面用threadprivate命令来实现一个各个线程私有的计数器,各个线程使用同一个函数来实现自己的计数。计数器代码如下:
int counter = 0;
#pragma omp threadprivate(counter)
int increment_counter()
{
counter++;
return(counter);
}
如果对于静态变量也同样可以使用threadprivate声明成线程私有的,上面的counter变量如改成用static类型来实现时,代码如下:
int increment_counter2()
{
static int counter = 0;
#pragma omp threadprivate(counter)
counter++;
return(counter);
}
threadprivate和private的区别在于threadprivate声明的变量通常是全局范围内有效的,而private声明的变量只在它所属的并行构造中有效。
threadprivate的对应只能用于copyin,copyprivate,schedule,num_threads和if子句中,不能用于任何其他子句中。
用作threadprivate的变量的地址不能是常数。
对于C++的类(class)类型变量,用作threadprivate的参数时有些限制,当定义时带有外部初始化时,必须具有明确的拷贝构造函数。
对于windows系统,threadprivate不能用于动态装载(使用LoadLibrary装载)的DLL中,可以用于静态装载的DLL中,关于windows系统中的更多限制,请参阅MSDN中有关threadprivate子句的帮助材料。
有关threadprivate命令的更多限制方面的信息,详情请参阅OpenMP2.5规范。
10.1.5 shared子句
shared子句用来声明一个或多个变量是共享变量。
用法如下:
shared( list )
需要注意的是,在并行区域内使用共享变量时,如果存在写操作,必须对共享变量加以保护,否则不要轻易使用共享变量,尽量将共享变量的访问转化为私有变量的访问。
循环迭代变量在循环构造区域里是私有的。声明在循环构造区域内的自动变量都是私有的。
10.1.6 default子句
default子句用来允许用户控制并行区域中变量的共享属性。
用法如下:
default(shared | none)
使用shared时,缺省情况下,传入并行区域内的同名变量被当作共享变量来处理,不会产生线程私有副本,除非使用private等子句来指定某些变量为私有的才会产生副本。
如果使用none作为参数,那么线程中用到的变量必须显示指定是共享的还是私有的,除了那些由明确定义的除外。
10.1.7 reduction子句
reduction子句主要用来对一个或多个参数条目指定一个操作符,每个线程将创建参数条目的一个私有拷贝,在区域的结束处,将用私有拷贝的值通过指定的运行符运算,原始的参数条目被运算结果的值更新。
reduction 子句用法如下 :
reduction( operator : list )
下表列出了可以用于reduction子句的一些操作符以及对应私有拷贝变量缺省的初始值,私有拷贝变量的实际初始值依赖于redtucion变量的数据类型。
表10-4-1:reduction操作中各种操作符号对应拷贝变量的缺省初始值
Operator
Initialization value
+
0
*
1
-
0
&
~0
|
0
^
0
&&
1
||
0
例如一个整数求和的程序如下:
int i, sum = 100;
#pragma omp parallel for reduction(+: sum)
for ( i = 0; i < 1000; i++ )
{
sum += i;
}
printf( "sum = %ld/n", sum);
注意,如果在并行区域内不加锁保护就直接对共享变量进行写操作,存在数据竞争问题,会导致不可预测的异常结果。共享数据作为 private 、 firstprivate 、 lastprivate 、 threadprivate 、 reduction 子句的参数进入并行区域后,就变成线程私有了,不需要加锁保护了。
10.1.8 copyin子句
copyin子句用来将主线程中threadprivate变量的值拷贝到执行并行区域的各个线程的threadprivate变量中,便于线程可以访问主线程中的变量值,
用法如下:
copyin( list )
 
copyin中的参数必须被声明成threadprivate的,对于类类型的变量,必须带有明确的拷贝赋值操作符。
对于前面threadprivate中讲过的计数器函数,如果多个线程使用时,各个线程都需要对全局变量counter的副本进行初始化,可以使用copyin子句来实现,示例代码如下:
int main( int argc, char* argv[])
{
int iterator;
#pragma omp parallel sections copyin(counter)
{
#pragma omp section
{
int count1;
for ( iterator = 0; iterator < 100; iterator++ )
{
count1 = increment_counter();
}
printf("count1 = %ld/n", count1);
}
#pragma omp section
{
int count2;
for ( iterator = 0; iterator < 200; iterator++ )
{
count2 = increment_counter();
}
printf("count2 = %ld/n", count2);
}
}
printf("counter = %ld/n", counter);
}
打印结果如下:
count1 = 100
count2 = 200
counter = 0
 
从打印结果可以看出,两个线程都正确实现了各自的计数。
10.1.9 copyprivate子句
copyprivate子句提供了一种机制用一个私有变量将一个值从一个线程广播到执行同一并行区域的其他线程。
用法如下:
copyprivate( list )
copyprivate 子句可以关联 single 构造,在 single 构造的 barrier 到达之前就完成了广播工作。 copyprivate 可以对 private 和 threadprivate 子句中的变量进行操作,但是当使用 single 构造时, copyprivate 的变量不能用于 private 和 firstprivate 子句中。
下面便是一个使用 copyprivate 的代码例子:
int counter = 0;
#pragma omp threadprivate(counter)
int increment_counter()
{
counter++;
return(counter);
}
#pragma omp parallel
{
int    count;
#pragma omp single copyprivate(counter)
{
counter = 50;
}
count = increment_counter();
printf("ThreadId: %ld, count = %ld/n", omp_get_thread_num(), count);
}
打印结果为:
ThreadId: 2, count = 51
ThreadId: 0, count = 51
ThreadId: 3, count = 51
ThreadId: 1, count = 51
如果没有使用copyprivate子句,那么打印结果为:
ThreadId: 2, count = 1
ThreadId: 1, count = 1
ThreadId: 0, count = 51
ThreadId: 3, count = 1
从打印结果可以看出,使用copyprivate子句后,single构造内给counter赋的值被广播到了其他线程里,但没有使用copyprivate子句时,只有一个线程获得了single构造内的赋值,其他线程没有获取single构造内的赋值。
参考文献:

Ananth Grama, Anshul Gupta,“并行计算导论”,张武等译,机械工业出版社,2005.01

Michael J. Quinn, “MPI与OpenMP并行程序设计”,陈文光等译,清华大学出版社,2004.10

Shameem Akhter等,“多核程序设计技术-通过软件多线程提升性能”,电子工业出版社,2007.03
OpenMP2.5规范 http://www.openmp.org/
OpenMP2.0规范 http://www.openmp.org/
MSDN帮助材料 ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.VisualStudio.v80.chs/dv_vclang/html/652414c5-78ed-4b7f-8283-1a9fe4c5e78d.htm  
注:本文的写作主要参考OpenMP2.5规范等参考文献,限于水平,可能存在对规范理解不足之处,请大家发现问题时不吝指正。

OpenMP中的数据处理子句相关推荐

  1. 【OpenMP学习笔记】2:OpenMP中的归约和reduction子句

    简述归约 归约操作在MPI里也学过,不过那时候还不太熟悉这种操作.当时只知道MPI_Reduce可以把全局求和和集合通信封装起来,非常方便.实际上将相同的二元归约操作符重复地应用到一个序列上得到结果的 ...

  2. Javascript中二进制数据处理方法

    Javascript中二进制数据处理方法 转载于:https://www.cnblogs.com/motadou/archive/2012/02/19/2358514.html

  3. select sqlite 唯一_SQLite中的SELECT子句使用通配符

    SQLite中的SELECT子句使用通配符 在SELECT子句中支持两个通配符,分别为*和table_name.*.其中,*通配符会将FROM子句中每个源表的所有用户定义的列进行输出.例如,下面的SQ ...

  4. SQLite中的WHERE子句

    SQLite中的WHERE子句 WHERE子句用于从FROM子句生成的工作表中过滤行.它提供了对每一行进行判断的表达式.当表达式返回的值为false或NULL时,此行就会被丢弃.这种丢弃只是删除记录, ...

  5. SQLite中的FROM子句

    SQLite中的FROM子句 FROM子句从数据库中可以获取到一个或多个源表.源表通常是数据库命名的表,但也可以是视图或子查询.子查询相关的更多详细信息,我们会在后面进行介绍.当获取到多个源表时,JO ...

  6. SQLite中的SELECT子句使用表达式

    SQLite中的SELECT子句使用表达式 在SELECT语句中可以只有SELECT子句不写FROM子句.这些表达式列表将被单次计算,生成一行,然后组成工作表.这对于测试和判断独立表达式很有用.例如, ...

  7. SQLite中的SELECT子句使用通配符

    SQLite中的SELECT子句使用通配符 在SELECT子句中支持两个通配符,分别为*和table_name.*.其中,*通配符会将FROM子句中每个源表的所有用户定义的列进行输出.例如,下面的SQ ...

  8. SQLite中的SELECT子句使用别名

    SQLite中的SELECT子句使用别名 开发者可以使用AS关键字为指定的列名提供一个新的别名,其语法形式如下 SELECT column_name AS Alias [,-] 例如,下面的SQL语句 ...

  9. SQLite中的SELECT子句

    SQLite中的SELECT子句 目前为止,最常见的SELECT语句由三个子句组成,分别为SELECT.FROM与WHERE.本小节我们首先讲解SELECT子句.SELECT子句可以用来定义最终结果表 ...

最新文章

  1. oracle 12c dg新特性,oracle 12c:新特性-网格(Grid)基础架构的增强
  2. 关于int.Parse()的异常
  3. idea初始化git 初始化git仓库
  4. Javascript调试之console对象,它的一些小技巧你都知道吗?
  5. kafka key的作用_震惊了,原来这才是Kafka的“真面目”!
  6. Oracle MERGE用法
  7. python email 附件_使用python调用email模块实现附件发送
  8. hibernate中cascade和inverse中的设置问题
  9. 谷歌浏览器插件离线安装
  10. 卡常数技巧(未完结)
  11. 利用spring AOP 实现 sql注入检测
  12. 微信小程序 转发功能实例讲解
  13. 中国关系型社会的环境如何生存发展------总结程序员如何做人做事
  14. 双非二本计算机学生是应该考研还是就业
  15. wxpython使用简介_wxPython AUI介绍
  16. 用不了tradingview?那自己捣鼓一个属于自己的量化分析平台
  17. 不同型号的二极管模块并联_原来我不懂二极管…
  18. Mac上使用IE浏览器
  19. 【vue作业】vue实现海贼王网页介绍--动漫网站设计
  20. 用梯度下降法求根号2的值

热门文章

  1. 应用哈希值更改图片hash
  2. Top22:RHEL学习札记-5-用户身份与管理权限
  3. ubuntu系统修改用户权限
  4. linux 自带强大调试工具sysRQ(高级片)
  5. css设置字符间距后无法完全居中问题
  6. dpkg 删除软件_在Ubuntu中使用dpkg命令卸载软件的方法
  7. 前端面试题目搜集——理论知识篇
  8. 数据科学中的陷阱II:定性变量的处理
  9. 啥?PCB拼版对SMT组装有影响!
  10. 访问ftp服务器文件夹权限设置,访问ftp服务器文件夹权限设置