关于并发:Openmp-Runtime-库函数汇总上

35次阅读

共计 8288 个字符,预计需要花费 21 分钟才能阅读完成。

Openmp Runtime 库函数汇总(上)

  • omp_in_parallel,如果以后线程正在并行域外部,则此函数返回 true,否则返回 false。
#include <stdio.h>
#include <omp.h>

int main()
{
   printf("0> Not In parallel region value = %d\n",
          omp_in_parallel());

   // 在这里函数返回的 false 在 C 语言当中返回值等于 int 类型的 0
   if (omp_in_parallel())
   {
      printf("1> In parallel region value = %d\n",
             omp_in_parallel());
   }
   #pragma omp parallel num_threads(2) default(none)
   {
      // 这里函数的返回值是 1 在 C 语言当中对应 int 类型的 1
      if (omp_in_parallel())
      {
         printf("2> In parallel region value = %d\n",
                omp_in_parallel());
      }
   }
   return 0;
}

下面的程序的输入后果如下所示:

0> Not In parallel region value = 0
2> In parallel region value = 1
2> In parallel region value = 1

须要留神的是在下面的函数 omp_in_parallel 应用时须要留神,如果你的并行域只有一个线程的时候 omp_in_parallel 返回的是 false。比方上面的例子:

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

int main()
{
   printf("0> Not In parallel region value = %d\n",
          omp_in_parallel());

   // 在这里函数返回的 false 在 C 语言当中返回值等于 int 类型的 0
   if (omp_in_parallel())
   {
      printf("1> In parallel region value = %d\n",
             omp_in_parallel());
   }
  // 只有一个线程,因而并不会激活并行域
   #pragma omp parallel num_threads(1) default(none)
   {
      // 这里函数的返回值是 1 在 C 语言当中对应 int 类型的 1
      if (omp_in_parallel())
      {
         printf("2> In parallel region value = %d\n",
                omp_in_parallel());
      }
   }
   return 0;
}

在下面的程序当中,因为并行域当中只有一个线程,因而不会激活,所以即便在 parallel 代码块内不,这个 omp_in_parallel 也不会被触发,下面的程序只会输入第一个 printf 的内容:

0> Not In parallel region value = 0
  • omp_get_thread_num,这个函数的次要作用是用于返回以后并行域的线程组当中的惟一 ID,在一个串行代码当中,这个函数的返回后果之中等于 0,在并行域当中这个函数的返回值的区间等于 [0, num_threads – 1],如果是一个线程组的 master 线程调用这个函数,这个函数的返回值始终是 0,对应的测试代码如下所示:
#include <stdio.h>
#include <omp.h>

int main()
{printf("Non parallel region: omp_get_thread_num() = %d\n", omp_get_thread_num());

#pragma omp parallel num_threads(5) default(none)
   {printf("In parallel region: omp_get_thread_num() = %d\n", omp_get_thread_num());
#pragma omp master
      {printf("In parallel region master: omp_get_thread_num() = %d\n", omp_get_thread_num());
      }
   }

   return 0;
}

下面的程序的输入后果如下所示:

Non parallel region: omp_get_thread_num() = 0
In parallel region: omp_get_thread_num() = 0
In parallel region master: omp_get_thread_num() = 0
In parallel region: omp_get_thread_num() = 4
In parallel region: omp_get_thread_num() = 1
In parallel region: omp_get_thread_num() = 2
In parallel region: omp_get_thread_num() = 3

在下面的代码当中咱们能够看到,在非并行域当中(第一个 printf)函数的输入后果是 0,在并行域当中程序的输入后果范畴是 [0. num_threads],如果一个线程是 master 线程的话,那么它对应的线程组当中的返回值就是 0。

  • omp_get_team_size,这个函数的次要作用就是返回一个线程组当中的线程个数。这个函数承受一个参数,他的函数原型为 int omp_get_team_size(int level);,这个参数的 level 的含意就是并行域的嵌套层级,这个参数的范畴是 [0, omp_get_level],如果传入的参数不在这个范畴,那么这个函数的返回值为 -1,如果你在一个并行域传入的 level 的参数是 omp_get_level,那么这个函数的返回值和 omp_get_num_threads 的返回值相等。
#include <stdio.h>
#include <omp.h>

int main()
{omp_set_nested(1);
   int level = omp_get_level();
   int size = omp_get_team_size(level);
   printf("Non parallel region : level = %d size = %d\n", level, size);

#pragma omp parallel num_threads(5) default(none)
   {int level = omp_get_level();
      int size = omp_get_team_size(level);
      printf("level = %d size = %d\n", level, size);

#pragma omp parallel num_threads(2) default(none)
      {int level = omp_get_level();
         int size = omp_get_team_size(level);
         printf("level = %d size = %d\n", level, size);

         printf("Up one level team size = %d\n", omp_get_team_size(level - 1));
      }
   }
   return 0;
}

下面的程序的输入后果如下所示:

Non parallel region : level = 0 size = 1
level = 1 size = 5
level = 1 size = 5
level = 1 size = 5
level = 1 size = 5
level = 1 size = 5
level = 2 size = 2
Up one level team size = 5
level = 2 size = 2
Up one level team size = 5
level = 2 size = 2
Up one level team size = 5
level = 2 size = 2
level = 2 size = 2
Up one level team size = 5
level = 2 size = 2
Up one level team size = 5
level = 2 size = 2
Up one level team size = 5
Up one level team size = 5
level = 2 size = 2
Up one level team size = 5
level = 2 size = 2
Up one level team size = 5
level = 2 size = 2
Up one level team size = 5

在下面的代码当中在非并行域的代码当中,程序的输入后果和咱们下面谈到的是雷同的,level 等于 0,team size 等于 1,在并行域当中输入的后果也是合乎预期的,咱们来看一下最初两个输入,倒数第一个输入是查看上一个 level 的输入后果,能够看到他的输入后果等于 5,这和咱们设置的 5 个线程是相符的。

  • omp_get_num_procs,这个函数绝对比较简单,次要是返回你的零碎当中处理器的数量。
#include <stdio.h>
#include <omp.h>

int main()
{int nprocs = omp_get_num_procs();
   printf("Number of processors = %d\n", nprocs);
   return 0;
}
  • omp_in_final,这个函数次要是查看以后线程是否在最终工作或者蕴含工作当中,比方在上面的例子当中,咱们就能够测试这个函数:
#include <stdio.h>
#include <omp.h>

int echo(int n)
{
   int ret;
#pragma omp task shared(ret, n) final(n <= 10)
   {if(omp_in_final())
      {printf("In final n = %d\n", n);
         ret = n;
      }
      else
      {ret = 1 + echo(n - 1);
      }
   }
   return ret;
}

int main()
{printf("echo(100) = %d\n", echo(100));
   return 0;
}

下面的程序的输入后果如下所示:

In final n = 10
echo(100) = 100

在下面的程序咱们在函数 echo 当中定义了一个工作,当参数 n <= 10 的时候,这个工作会变成一个 final 工作,因而在 n == 10 的时候,这个函数的 omp_in_final 会返回 true,因而会间接将 10 赋值给 ret,不会进行递归调用,因而咱们失去了下面的输入后果。

  • omp_get_nested,这个函数是用于判断是否开启并行区域的嵌套,如果开启了并行区域的嵌套,那么这个函数就返回 true,否则就返回 false,对应 C 语言的值别离为 1 和 0。在 OpenMP 当中默认是敞开的,你能够应用函数 omp_set_nested(1) 进行开启,或者应用 omp_set_nested(0) 敞开并行区域的嵌套。你能够对并行区域的嵌套有所纳闷,咱们来看上面两个例子,你就恍然大悟了。
#include <stdio.h>
#include <omp.h>


int main()
{//   omp_set_nested(1);
   #pragma omp parallel num_threads(2) default(none)
   {
      printf("Outer tid = %d level = %d num_threads = %d\n",
             omp_get_thread_num(), omp_get_active_level(),
             omp_get_num_threads());
      #pragma omp parallel num_threads(2) default(none)
      {
         printf("Inner tid = %d level = %d num_threads = %d\n",
                omp_get_thread_num(), omp_get_active_level(),
                omp_get_num_threads());
      }
   }
   return 0;
}

下面的程序的输入后果如下所示:

Outer tid = 0 level = 1 num_threads = 2
Inner tid = 0 level = 1 num_threads = 1
Outer tid = 1 level = 1 num_threads = 2
Inner tid = 0 level = 1 num_threads = 1

咱们再来看一下如果咱们开启了并行区域的嵌套之后程序的输入后果:

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


int main()
{omp_set_nested(1);
   #pragma omp parallel num_threads(2) default(none)
   {
      printf("Outer tid = %d level = %d num_threads = %d\n",
             omp_get_thread_num(), omp_get_active_level(),
             omp_get_num_threads());
      #pragma omp parallel num_threads(2) default(none)
      {
         printf("Inner tid = %d level = %d num_threads = %d\n",
                omp_get_thread_num(), omp_get_active_level(),
                omp_get_num_threads());
      }
   }
   return 0;
}

下面的程序的输入后果如下所示:

Outer tid = 0 level = 1 num_threads = 2
Outer tid = 1 level = 1 num_threads = 2
Inner tid = 0 level = 2 num_threads = 2
Inner tid = 1 level = 2 num_threads = 2
Inner tid = 0 level = 2 num_threads = 2
Inner tid = 1 level = 2 num_threads = 2

咱们能够比照一下两个程序的输入后果的差别,咱们能够看到当咱们容许并行嵌套之后,程序的输入后果会更多一点,这是因为如果咱们没有开启的话,子并行域每个线程只会启动一个线程,如果咱们开启的话,那么每个线程就会重新启动 num_threads 个线程,比方在下面两个代码当中,对于第一份代码内部并行块会启动两个线程,而后在每个线程的外部有一个并行块,然而因为没有启动,因而不论你的 num_threads 设置成多少,每个线程只会启动一个线程,因而最终会有 4 个 printf 语句输入。而对于第二份代码,是启动了嵌套代码的,内部并行块有两个线程,在外部因为是开启了,这两个线程会别离创立 num_threads 个外部线程去执行外部代码块,因而会有 2 个内部线程 4(2×2)个外部线程,一共会有 6 个输入,因而就合乎下面的后果了。其实不仅能够嵌套两层,还能够嵌套更多层,原理也是一样的分析方法。

  • omp_get_active_level,这个函数次要是返回以后线程所在的并行域的嵌套的并行块的级别,从 1 开始,从外往内没加一层这个值就加一,如果没有启动并行嵌套,那么这个函数的返回值就是 1。
  • omp_set_max_active_levels,咱们在下面提到了,咱们能够一直的进行并行域的嵌套,咱们能够应用这个函数进行设置最大的嵌套的层数,如果超过这个层数那么就与不启动嵌套的成果一样了,也就是说 num_threads 不会产生成果了。

咱们当初设置容许设置的最大的并行块的嵌套层数等于 2,然而咱们一共有三个嵌套块,咱们能够看一下对第三层嵌套的代码的影响。

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

int main()
{omp_set_nested(1);
   omp_set_max_active_levels(2);

#pragma omp parallel num_threads(2) default(none)
   {printf("1> omp_get_level = %d omp_get_active_level = %d\n", omp_get_level(), omp_get_active_level());
#pragma omp parallel num_threads(2) default(none)
      {printf("2> omp_get_level = %d omp_get_active_level = %d\n", omp_get_level(), omp_get_active_level());
#pragma omp parallel num_threads(2) default(none)
         {printf("3> omp_get_level = %d omp_get_active_level = %d\n", omp_get_level(), omp_get_active_level());

         }
      }
   }
   return 0;
}

下面的程序的输入后果如下所示(3> 一共输入了 4 次,因为它的内部线程的个数是 4 (2×2)):

1> omp_get_level = 1 omp_get_active_level = 1
1> omp_get_level = 1 omp_get_active_level = 1
2> omp_get_level = 2 omp_get_active_level = 2
3> omp_get_level = 3 omp_get_active_level = 2
2> omp_get_level = 2 omp_get_active_level = 2
2> omp_get_level = 2 omp_get_active_level = 2
3> omp_get_level = 3 omp_get_active_level = 2
2> omp_get_level = 2 omp_get_active_level = 2
3> omp_get_level = 3 omp_get_active_level = 2
3> omp_get_level = 3 omp_get_active_level = 2

咱们当初将函数 omp_set_max_active_levels 的参数设置成 3,也就是说容许的最大的并行块的嵌套层数等于 3,从新看一下程序的输入后果是什么(将 omp_set_max_active_levels(2) 改成 omp_set_max_active_levels(3))之后,程序的输入后果如下所示:

1> omp_get_level = 1 omp_get_active_level = 1
1> omp_get_level = 1 omp_get_active_level = 1
2> omp_get_level = 2 omp_get_active_level = 2
2> omp_get_level = 2 omp_get_active_level = 2
2> omp_get_level = 2 omp_get_active_level = 2
2> omp_get_level = 2 omp_get_active_level = 2
3> omp_get_level = 3 omp_get_active_level = 3
3> omp_get_level = 3 omp_get_active_level = 3
3> omp_get_level = 3 omp_get_active_level = 3
3> omp_get_level = 3 omp_get_active_level = 3
3> omp_get_level = 3 omp_get_active_level = 3
3> omp_get_level = 3 omp_get_active_level = 3
3> omp_get_level = 3 omp_get_active_level = 3
3> omp_get_level = 3 omp_get_active_level = 3

能够看到 3> 的输入次数等于 8,这个次数就表明内部 4 个线程都在第三个并行块产生了两个线程,这就是启动嵌套并行块的成果,这就是设置函数 omp_set_max_active_levels 的成果。

  • omp_get_nested,这个函数的作用就是返回是否启动了并行嵌套,在 C 语言当中如果启动了那么就是返回 1,否则就是返回 0。
#include <stdio.h>
#include <omp.h>

int main()
{printf("omp_get_nested() = %d\n", omp_get_nested());
   omp_set_nested(1);
   printf("omp_get_nested() = %d\n", omp_get_nested());
   return 0;
}

下面的程序的输入后果如下所示:

omp_get_nested() = 0
omp_get_nested() = 1

总结

在本篇文章当中次要给大家介绍了一些在 OpenMP 当中罕用的动静库函数,这篇文章的动静库函数次要是对于并行域和线程状态的函数,在下篇文章当中咱们次要是剖析一些 OpenMP 当中的锁相干函数。心愿大家有所播种!


更多精彩内容合集可拜访我的项目:https://github.com/Chang-LeHu…

关注公众号:一无是处的钻研僧,理解更多计算机(Java、Python、计算机系统根底、算法与数据结构)常识。

正文完
 0