关于openmp:OpenMP-Sections-Construct-实现原理以及源码分析

34次阅读

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

OpenMP Sections Construct 实现原理以及源码剖析

前言

在本篇文章当中次要给大家介绍 OpenMP 当中次要给大家介绍 OpenMP 当中 sections construct 的实现原理以及他调用的动静库函数剖析。如果曾经理解过了后面的对于 for 的调度形式的剖析,本篇文章就非常简单了。

编译器角度剖析

在这一大节当中咱们将从编译器角度去剖析编译器会怎么解决 sections construct,咱们以上面的 sections construct 为例子,看看编译器是如何解决 sections construct 的。

#pragma omp sections
{
  #pragma omp section
  stmt1;
  #pragma omp section
  stmt2;
  #pragma omp section
  stmt3;
}

下面的代码会被编译器转换成上面的模式,其中 GOMP_sections_start 和 GOMP_sections_next 是并发平安的,他们都会返回一个数据表示第几个 omp section 代码块,其中 GOMP_sections_start 的参数是示意有几个 omp section 代码块,并且返回给线程一个整数示意线程须要执行第几个 section 代码块,这两个函数的意义不同的是在 GOMP_sections_start 当中会进行一些数据的初始化操作。当两个函数返回 0 的时候示意所有的 section 都被执行完了,从而退出 for 循环。

for (i = GOMP_sections_start (3); i != 0; i = GOMP_sections_next ())
  switch (i)
    {
    case 1:
      stmt1;
      break;
    case 2:
      stmt2;
      break;
    case 3:
      stmt3;
      break;
    }
GOMP_barrier ();

动静库函数剖析

事实上在函数 GOMP_sections_start 和函数 GOMP_sections_next 当中调用的都是咱们之前剖析过的函数 gomp_iter_dynamic_next,这个函数实际上就是让线程始终原子指令去竞争数据块(chunk),这个特点和 sections 须要实现的语意是雷同的,只不过 sections 的块大小(chunk size)都是等于 1 的,因为一个线程一次只可能执行一个 section 代码块。

unsigned
GOMP_sections_start (unsigned count)
{
  // 参数 count 的含意就是示意一共有多少个 section 代码块
  // 失去当线程的相干数据
  struct gomp_thread *thr = gomp_thread ();
  long s, e, ret;
  // 进行数据的初始化操作
  // 将数据的 chunk size 设置等于 1
  // 宰割 chunk size 的起始地位设置成 1 因为依据下面的代码剖析 0 示意退出循环 因而不可能应用 0 作为宰割的起始地位
  if (gomp_work_share_start (false))
    {
    // 这里传入 count 作为参数的起因是须要设置 chunk 调配的最终地位 具体的源代码在下方
      gomp_sections_init (thr->ts.work_share, count);
      gomp_work_share_init_done ();}
  // 如果获取到一个 section 的执行权 gomp_iter_dynamic_next 返回 true 否则返回 false 
  // s 和 e 别离示意 chunk 的起始地位和终止地位 然而在 sections 当中须要留神的是所有的 chunk size 都等于 1
  // 这也很容易了解一次执行一个 section 代码块
  if (gomp_iter_dynamic_next (&s, &e))
    ret = s;
  else
    ret = 0;
  return ret;
}

// 上面是局部 gomp_sections_init 的代码
static inline void
gomp_sections_init (struct gomp_work_share *ws, unsigned count)
{
  ws->sched = GFS_DYNAMIC;
  ws->chunk_size = 1; // 设置 chunk size 等于 1
  ws->end = count + 1L; // 因为一共有 count 个 section 块
  ws->incr = 1; // 每次增长一个
  ws->next = 1; // 从 1 开始进行 chunk size 的调配 因为 0 示意退出循环(编译器角度剖析)}

unsigned
GOMP_sections_next (void)
{
  // 这个函数就比拟容易了解了 就是获取一个 chunk 拿到对应的 section 的执行权
  long s, e, ret;
  if (gomp_iter_dynamic_next (&s, &e))
    ret = s;
  else
    ret = 0;
  return ret;
}

// 上面的函数在之前的很多文章当中都剖析过了 这里不再进行剖析
// 上面的函数的次要过程就是应用 CAS 指令一直的进行尝试,直到获取胜利或者全副获取实现 没有 chunk 须要调配
bool
gomp_iter_dynamic_next (long *pstart, long *pend)
{struct gomp_thread *thr = gomp_thread ();
  struct gomp_work_share *ws = thr->ts.work_share;
  long start, end, nend, chunk, incr;

  end = ws->end;
  incr = ws->incr;
  chunk = ws->chunk_size;

  if (__builtin_expect (ws->mode, 1))
    {long tmp = __sync_fetch_and_add (&ws->next, chunk);
      if (incr > 0)
    {if (tmp >= end)
        return false;
      nend = tmp + chunk;
      if (nend > end)
        nend = end;
      *pstart = tmp;
      *pend = nend;
      return true;
    }
      else
    {if (tmp <= end)
        return false;
      nend = tmp + chunk;
      if (nend < end)
        nend = end;
      *pstart = tmp;
      *pend = nend;
      return true;
    }
    }

  start = ws->next;
  while (1)
    {
      long left = end - start;
      long tmp;

      if (start == end)
    return false;

      if (incr < 0)
    {if (chunk < left)
        chunk = left;
    }
      else
    {if (chunk > left)
        chunk = left;
    }
      nend = start + chunk;

      tmp = __sync_val_compare_and_swap (&ws->next, start, nend);
      if (__builtin_expect (tmp == start, 1))
    break;

      start = tmp;
    }

  *pstart = start;
  *pend = nend;
  return true;
}

总结

在本篇文章当中次要介绍了 OpenMP 当中 sections 的实现原理和相干的动静库函数剖析,对于 sections 重点在编译器会如何对 sections 的编译领导语句进行解决的,动静库函数和 for 循环的动静调度形式是一样的,只不过 chunk size 设置成 1,分块的起始地位等于 1,分块的最终值是 section 代码块的个数,最终在动静调度的形式应用 CAS 一直获取 section 的执行权,领导所有的 section 被执行实现。


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

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

正文完
 0