欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

OpenMP task construct 實(shí)現(xiàn)原理及源碼示例解析

 更新時(shí)間:2023年03月06日 09:01:24   作者:一無是處的研究僧  
這篇文章主要為大家介紹了OpenMP task construct 實(shí)現(xiàn)原理及源碼示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

在本篇文章當(dāng)中主要給大家介紹在 OpenMP 當(dāng)中 task 的實(shí)現(xiàn)原理,以及他調(diào)用的相關(guān)的庫函數(shù)的具體實(shí)現(xiàn)。

在本篇文章當(dāng)中最重要的就是理解整個(gè) OpenMP 的運(yùn)行機(jī)制。

從編譯器角度看 task construct

在本小節(jié)當(dāng)中主要給大家分析一下編譯器將 openmp 的 task construct 編譯成什么樣子,下面是一個(gè) OpenMP 的 task 程序例子:

#include <stdio.h>
#include <omp.h>
int main()
{
#pragma omp parallel num_threads(4) default(none)
  {
#pragma omp task default(none)
    {
       printf("Hello World from tid = %d\n", omp_get_thread_num());
    }
  }
  return 0;
}

首先先捋一下整個(gè)程序被編譯之后的執(zhí)行流程,經(jīng)過前面的文章的學(xué)習(xí),我們已經(jīng)知道了并行域當(dāng)中的代碼會(huì)被編譯器編譯成一個(gè)函數(shù),關(guān)于這一點(diǎn)我們已經(jīng)在前面的很多文章當(dāng)中已經(jīng)討論過了,就不再進(jìn)行復(fù)述。事實(shí)上 task construct 和 parallel construct 一樣,task construct 也會(huì)被編譯成一個(gè)函數(shù),同樣的這個(gè)函數(shù)也會(huì)被作為一個(gè)參數(shù)傳遞給 OpenMP 內(nèi)部,被傳遞的這個(gè)函數(shù)可能被立即執(zhí)行,也可能在函數(shù) GOMP_parallel_end 被調(diào)用后,在到達(dá)同步點(diǎn)之前執(zhí)行被執(zhí)行(線程在到達(dá)并行域的同步點(diǎn)之前需要保證所有的任務(wù)都被執(zhí)行完成)。

整個(gè)過程大致如下圖所示:

上面的 OpenMP task 程序?qū)?yīng)的反匯編程序如下所示:

00000000004008ad <main>:
  4008ad:       55                      push   %rbp
  4008ae:       48 89 e5                mov    %rsp,%rbp
  4008b1:       ba 04 00 00 00          mov    $0x4,%edx
  4008b6:       be 00 00 00 00          mov    $0x0,%esi
  4008bb:       bf db 08 40 00          mov    $0x4008db,%edi
  4008c0:       e8 8b fe ff ff          callq  400750 <GOMP_parallel_start@plt>
  4008c5:       bf 00 00 00 00          mov    $0x0,%edi
  4008ca:       e8 0c 00 00 00          callq  4008db <main._omp_fn.0>
  4008cf:       e8 8c fe ff ff          callq  400760 <GOMP_parallel_end@plt>
  4008d4:       b8 00 00 00 00          mov    $0x0,%eax
  4008d9:       5d                      pop    %rbp
  4008da:       c3                      retq
00000000004008db <main._omp_fn.0>:
  4008db:       55                      push   %rbp
  4008dc:       48 89 e5                mov    %rsp,%rbp
  4008df:       48 83 ec 10             sub    $0x10,%rsp
  4008e3:       48 89 7d f8             mov    %rdi,-0x8(%rbp)
  4008e7:       c7 04 24 00 00 00 00    movl   $0x0,(%rsp)		# 參數(shù) flags
  4008ee:       41 b9 01 00 00 00       mov    $0x1,%r9d			# 參數(shù) if_clause
  4008f4:       41 b8 01 00 00 00       mov    $0x1,%r8d			# 參數(shù) arg_align
  4008fa:       b9 00 00 00 00          mov    $0x0,%ecx			# 參數(shù) arg_size
  4008ff:       ba 00 00 00 00          mov    $0x0,%edx			# 參數(shù) cpyfn
  400904:       be 00 00 00 00          mov    $0x0,%esi			# 參數(shù) data
  400909:       bf 15 09 40 00          mov    $0x400915,%edi # 這里就是調(diào)用函數(shù) main._omp_fn.1
  40090e:       e8 9d fe ff ff          callq  4007b0 <GOMP_task@plt>
  400913:       c9                      leaveq
  400914:       c3                      retq
0000000000400915 <main._omp_fn.1>:
  400915:       55                      push   %rbp
  400916:       48 89 e5                mov    %rsp,%rbp
  400919:       48 83 ec 10             sub    $0x10,%rsp
  40091d:       48 89 7d f8             mov    %rdi,-0x8(%rbp)
  400921:       e8 4a fe ff ff          callq  400770 <omp_get_thread_num@plt>
  400926:       89 c6                   mov    %eax,%esi
  400928:       bf d0 09 40 00          mov    $0x4009d0,%edi
  40092d:       b8 00 00 00 00          mov    $0x0,%eax
  400932:       e8 49 fe ff ff          callq  400780 <printf@plt>
  400937:       c9                      leaveq
  400938:       c3                      retq
  400939:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

從上面程序反匯編的結(jié)果我們可以知道,在主函數(shù)當(dāng)中仍然和之前一樣在并行域前后分別調(diào)用了 GOMP_parallel_start 和 GOMP_parallel_end,然后在兩個(gè)函數(shù)之間調(diào)用并行域的代碼 main._omp_fn.0 ,并行域當(dāng)中的代碼被編譯成函數(shù) main._omp_fn.0 ,從上面的匯編代碼我們可以看到在函數(shù) main._omp_fn.0 調(diào)用了函數(shù) GOMP_task ,這個(gè)函數(shù)的函數(shù)聲明如下所示:

void
GOMP_task (void (*fn) (void *), void *data, void (*cpyfn) (void *, void *),
	   long arg_size, long arg_align, bool if_clause, unsigned flags);

在這里我們重要解釋一下部分參數(shù),首先我們需要了解的是在 x86 當(dāng)中的函數(shù)調(diào)用規(guī)約,這一點(diǎn)我們在前面的文章當(dāng)中已經(jīng)討論過了,這里只是說明一下:

寄存器含義
rdi第一個(gè)參數(shù)
rsi第二個(gè)參數(shù)
rdx第三個(gè)參數(shù)
rcx第四個(gè)參數(shù)
r8第五個(gè)參數(shù)
r9第六個(gè)參數(shù)

根據(jù)上面的寄存器和參數(shù)的對應(yīng)關(guān)系,在上面的匯編代碼當(dāng)中已經(jīng)標(biāo)注了對應(yīng)的參數(shù)。在這些參數(shù)當(dāng)中最重要的一個(gè)參數(shù)就是第一個(gè)函數(shù)指針,對應(yīng)的匯編語句為 mov $0x400915,%edi,可以看到的是傳入的函數(shù)的地址為 0x400915,根據(jù)上面的匯編程序可以知道這個(gè)地址對應(yīng)的函數(shù)就是 main._omp_fn.1,這其實(shí)就是 task 區(qū)域之間被編譯之后的對應(yīng)的函數(shù),從上面的 main._omp_fn.1 匯編程序當(dāng)中也可以看出來調(diào)用了函數(shù) omp_get_thread_num,這和前面的 task 區(qū)域當(dāng)中代碼是相對應(yīng)的。

現(xiàn)在我們來解釋一下其他的幾個(gè)參數(shù):

  • fn,task 區(qū)域被編譯之后的函數(shù)地址。
  • data,函數(shù) fn 的參數(shù)。
  • cpyfn,參數(shù)拷貝函數(shù),一般是 NULL,有時(shí)候需要 task 當(dāng)中的數(shù)據(jù)不能是共享的,需要時(shí)私有的,這個(gè)時(shí)候可能就需要數(shù)據(jù)拷貝函數(shù),如果有數(shù)據(jù)需要及進(jìn)行拷貝而且這個(gè)參數(shù)還為 NULL 的話,那么在 OpenMP 內(nèi)部就會(huì)使用 memcpy 進(jìn)行內(nèi)存拷貝。
  • arg_size,參數(shù)的大小。
  • arg_align,參數(shù)多少字節(jié)對齊。
  • if_clause,if 子句當(dāng)中的比較結(jié)果,如果沒有 if 字句的話就是 true 。
  • flags,用于表示 task construct 的特征或者屬性,比如是否是最終任務(wù)。

我們現(xiàn)在使用另外一個(gè)例子,來看看參數(shù)傳遞的變化。

#include <stdio.h>
#include <omp.h>
int main()
{
#pragma omp parallel num_threads(4) default(none)
  {
     int data = omp_get_thread_num();
#pragma omp task default(none) firstprivate(data) if(data > 100)
    {
       data = omp_get_thread_num();
       printf("data = %d Hello World from tid = %d\n", data, omp_get_thread_num());
    }
  }
  return 0;
}

上面的程序被編譯之后對應(yīng)的匯編程序如下所示:

00000000004008ad <main>:
  4008ad:       55                      push   %rbp
  4008ae:       48 89 e5                mov    %rsp,%rbp
  4008b1:       48 83 ec 10             sub    $0x10,%rsp
  4008b5:       ba 04 00 00 00          mov    $0x4,%edx
  4008ba:       be 00 00 00 00          mov    $0x0,%esi
  4008bf:       bf df 08 40 00          mov    $0x4008df,%edi
  4008c4:       e8 87 fe ff ff          callq  400750 <GOMP_parallel_start@plt>
  4008c9:       bf 00 00 00 00          mov    $0x0,%edi
  4008ce:       e8 0c 00 00 00          callq  4008df <main._omp_fn.0>
  4008d3:       e8 88 fe ff ff          callq  400760 <GOMP_parallel_end@plt>
  4008d8:       b8 00 00 00 00          mov    $0x0,%eax
  4008dd:       c9                      leaveq
  4008de:       c3                      retq
00000000004008df <main._omp_fn.0>:
  4008df:       55                      push   %rbp
  4008e0:       48 89 e5                mov    %rsp,%rbp
  4008e3:       48 83 ec 20             sub    $0x20,%rsp
  4008e7:       48 89 7d e8             mov    %rdi,-0x18(%rbp)
  4008eb:       e8 80 fe ff ff          callq  400770 <omp_get_thread_num@plt>
  4008f0:       89 45 fc                mov    %eax,-0x4(%rbp)
  4008f3:       83 7d fc 64             cmpl   $0x64,-0x4(%rbp)
  4008f7:       0f 9f c2                setg   %dl
  4008fa:       8b 45 fc                mov    -0x4(%rbp),%eax
  4008fd:       89 45 f0                mov    %eax,-0x10(%rbp)
  400900:       48 8d 45 f0             lea    -0x10(%rbp),%rax
  400904:       c7 04 24 00 00 00 00    movl   $0x0,(%rsp)	# 參數(shù) flags
  40090b:       41 89 d1                mov    %edx,%r9d	# 參數(shù) if_clause
  40090e:       41 b8 04 00 00 00       mov    $0x4,%r8d	# 參數(shù) arg_align
  400914:       b9 04 00 00 00          mov    $0x4,%ecx	# 參數(shù) arg_size
  400919:       ba 00 00 00 00          mov    $0x0,%edx	# 參數(shù) cpyfn
  40091e:       48 89 c6                mov    %rax,%rsi	# 參數(shù) data
  400921:       bf 2d 09 40 00          mov    $0x40092d,%edi	# 這里就是調(diào)用函數(shù) main._omp_fn.1
  400926:       e8 85 fe ff ff          callq  4007b0 <GOMP_task@plt>
  40092b:       c9                      leaveq
  40092c:       c3                      retq
000000000040092d <main._omp_fn.1>:
  40092d:       55                      push   %rbp
  40092e:       48 89 e5                mov    %rsp,%rbp
  400931:       48 83 ec 20             sub    $0x20,%rsp
  400935:       48 89 7d e8             mov    %rdi,-0x18(%rbp)
  400939:       48 8b 45 e8             mov    -0x18(%rbp),%rax
  40093d:       8b 00                   mov    (%rax),%eax
  40093f:       89 45 fc                mov    %eax,-0x4(%rbp)
  400942:       e8 29 fe ff ff          callq  400770 <omp_get_thread_num@plt>
  400947:       89 c2                   mov    %eax,%edx
  400949:       8b 45 fc                mov    -0x4(%rbp),%eax
  40094c:       89 c6                   mov    %eax,%esi
  40094e:       bf f0 09 40 00          mov    $0x4009f0,%edi
  400953:       b8 00 00 00 00          mov    $0x0,%eax
  400958:       e8 23 fe ff ff          callq  400780 <printf@plt>
  40095d:       c9                      leaveq
  40095e:       c3                      retq
  40095f:       90                      nop

在上面的函數(shù)當(dāng)中我們將 data 一個(gè) 4 字節(jié)的數(shù)據(jù)作為線程私有數(shù)據(jù),可以看到給函數(shù) GOMP_task 傳遞的參數(shù)參數(shù)的大小以及參數(shù)的內(nèi)存對齊大小都發(fā)生來變化,從原來的 0 變成了 4,這因?yàn)?int 類型數(shù)據(jù)占 4 個(gè)字節(jié)。

Task Construct 源碼分析

在本小節(jié)當(dāng)中主要談?wù)撛?OpenMP 內(nèi)部是如何實(shí)現(xiàn) task 的,關(guān)于這一部分內(nèi)容設(shè)計(jì)的內(nèi)容還是比較龐雜,首先需要了解的是在 OpenMP 當(dāng)中使用 task construct 的被稱作顯示任務(wù)(explicit task),這種任務(wù)在 OpenMP 當(dāng)中會(huì)有兩個(gè)任務(wù)隊(duì)列(雙向循環(huán)隊(duì)列),將所有的任務(wù)都保存在這樣一張列表當(dāng)中,整體結(jié)構(gòu)如下圖所示:

在上圖當(dāng)中由同一個(gè)線程創(chuàng)建的任務(wù)為 child_task,他們之間使用 next_child 和 prev_child 兩個(gè)指針進(jìn)行連接,不同線程創(chuàng)建的任務(wù)之間可以使用 next_queue 和 prev_queue 兩個(gè)指針進(jìn)行連接。

任務(wù)的結(jié)構(gòu)體描述如下所示:

struct gomp_task
{
  struct gomp_task *parent;	// 任務(wù)的父親任務(wù)
  struct gomp_task *children;	// 子任務(wù)
  struct gomp_task *next_child;	// 下一個(gè)子任務(wù)
  struct gomp_task *prev_child;	// 上一個(gè)子任務(wù)
  struct gomp_task *next_queue;	// 下一個(gè)任務(wù) (不一定是同一個(gè)線程創(chuàng)建的子任務(wù))
  struct gomp_task *prev_queue;	// 上一個(gè)任務(wù) (不一定是同一個(gè)線程創(chuàng)建的子任務(wù))
  struct gomp_task_icv icv; // openmp 當(dāng)中內(nèi)部全局設(shè)置使用變量的值(internal control variable)
  void (*fn) (void *);	// task construct 被編譯之后的函數(shù)
  void *fn_data;	// 函數(shù)參數(shù)
  enum gomp_task_kind kind; // 任務(wù)類型 具體類型如下面的枚舉類型
  bool in_taskwait;	// 是否處于 taskwait 狀態(tài)
  bool in_tied_task; // 是不是在綁定任務(wù)當(dāng)中
  bool final_task; // 是不是最終任務(wù)
  gomp_sem_t taskwait_sem; // 對象鎖 用于保證線程操作這個(gè)數(shù)據(jù)的時(shí)候的線程安全
};
// openmp 當(dāng)中的任務(wù)的狀態(tài)
enum gomp_task_kind
{
  GOMP_TASK_IMPLICIT,
  GOMP_TASK_IFFALSE,
  GOMP_TASK_WAITING,
  GOMP_TASK_TIED
};

在了解完上面的數(shù)據(jù)結(jié)構(gòu)之后我們來看一下前面的給 OpenMP 內(nèi)部提交任務(wù)的函數(shù) GOMP_task,其源代碼如下所示:

/* Called when encountering an explicit task directive.  If IF_CLAUSE is
   false, then we must not delay in executing the task.  If UNTIED is true,
   then the task may be executed by any member of the team.  */
void
GOMP_task (void (*fn) (void *), void *data, void (*cpyfn) (void *, void *),
	   long arg_size, long arg_align, bool if_clause, unsigned flags)
{
  struct gomp_thread *thr = gomp_thread ();
  // team 是 OpenMP 一個(gè)線程組當(dāng)中共享的數(shù)據(jù)
  struct gomp_team *team = thr->ts.team;
#ifdef HAVE_BROKEN_POSIX_SEMAPHORES
  /* If pthread_mutex_* is used for omp_*lock*, then each task must be
     tied to one thread all the time.  This means UNTIED tasks must be
     tied and if CPYFN is non-NULL IF(0) must be forced, as CPYFN
     might be running on different thread than FN.  */
  if (cpyfn)
    if_clause = false;
  if (flags & 1)
    flags &= ~1;
#endif
  // 這里表示如果是 if 子句的條件為真的時(shí)候或者是孤立任務(wù)(team == NULL )或者是最終任務(wù)的時(shí)候或者任務(wù)隊(duì)列當(dāng)中的任務(wù)已經(jīng)很多的時(shí)候
  // 提交的任務(wù)需要立即執(zhí)行而不能夠放入任務(wù)隊(duì)列當(dāng)中然后在 GOMP_parallel_end 函數(shù)當(dāng)中進(jìn)行任務(wù)的取出
  // 再執(zhí)行
  if (!if_clause || team == NULL
      || (thr->task && thr->task->final_task)
      || team->task_count > 64 * team->nthreads)
    {
      struct gomp_task task;
      gomp_init_task (&task, thr->task, gomp_icv (false));
      task.kind = GOMP_TASK_IFFALSE;
      task.final_task = (thr->task && thr->task->final_task) || (flags & 2);
      if (thr->task)
	task.in_tied_task = thr->task->in_tied_task;
      thr->task = &task;
      if (__builtin_expect (cpyfn != NULL, 0))
	{
        // 這里是進(jìn)行數(shù)據(jù)的拷貝
	  char buf[arg_size + arg_align - 1];
	  char *arg = (char *) (((uintptr_t) buf + arg_align - 1)
				& ~(uintptr_t) (arg_align - 1));
	  cpyfn (arg, data);
	  fn (arg);
	}
      else
        // 如果不需要進(jìn)行數(shù)據(jù)拷貝則直接執(zhí)行這個(gè)函數(shù)
	fn (data);
      /* Access to "children" is normally done inside a task_lock
	 mutex region, but the only way this particular task.children
	 can be set is if this thread's task work function (fn)
	 creates children.  So since the setter is *this* thread, we
	 need no barriers here when testing for non-NULL.  We can have
	 task.children set by the current thread then changed by a
	 child thread, but seeing a stale non-NULL value is not a
	 problem.  Once past the task_lock acquisition, this thread
	 will see the real value of task.children.  */
      if (task.children != NULL)
	{
	  gomp_mutex_lock (&team->task_lock);
	  gomp_clear_parent (task.children);
	  gomp_mutex_unlock (&team->task_lock);
	}
      gomp_end_task ();
    }
  else
    {
    // 下面就是將任務(wù)先提交到任務(wù)隊(duì)列當(dāng)中然后再取出執(zhí)行
      struct gomp_task *task;
      struct gomp_task *parent = thr->task;
      char *arg;
      bool do_wake;
      task = gomp_malloc (sizeof (*task) + arg_size + arg_align - 1);
      arg = (char *) (((uintptr_t) (task + 1) + arg_align - 1)
		      & ~(uintptr_t) (arg_align - 1));
      gomp_init_task (task, parent, gomp_icv (false));
      task->kind = GOMP_TASK_IFFALSE;
      task->in_tied_task = parent->in_tied_task;
      thr->task = task;
    // 這里就是參數(shù)拷貝邏輯 如果存在拷貝函數(shù)就通過拷貝函數(shù)進(jìn)行參數(shù)賦值 否則使用 memcpy 進(jìn)行
    // 參數(shù)的拷貝
      if (cpyfn)
	cpyfn (arg, data);
      else
	memcpy (arg, data, arg_size);
      thr->task = parent;
      task->kind = GOMP_TASK_WAITING;
      task->fn = fn;
      task->fn_data = arg;
      task->in_tied_task = true;
      task->final_task = (flags & 2) >> 1;
    // 在這里獲取全局隊(duì)列鎖 保證下面的代碼在多線程條件下的線程安全
    // 因?yàn)樵谙旅娴拇a當(dāng)中會(huì)對全局的隊(duì)列進(jìn)行修改操作 下面的操作就是隊(duì)列的一些基本操作啦
      gomp_mutex_lock (&team->task_lock);
      if (parent->children)
	{
	  task->next_child = parent->children;
	  task->prev_child = parent->children->prev_child;
	  task->next_child->prev_child = task;
	  task->prev_child->next_child = task;
	}
      else
	{
	  task->next_child = task;
	  task->prev_child = task;
	}
      parent->children = task;
      if (team->task_queue)
	{
	  task->next_queue = team->task_queue;
	  task->prev_queue = team->task_queue->prev_queue;
	  task->next_queue->prev_queue = task;
	  task->prev_queue->next_queue = task;
	}
      else
	{
	  task->next_queue = task;
	  task->prev_queue = task;
	  team->task_queue = task;
	}
      ++team->task_count;
      gomp_team_barrier_set_task_pending (&team->barrier);
      do_wake = team->task_running_count + !parent->in_tied_task
		< team->nthreads;
      gomp_mutex_unlock (&team->task_lock);
      if (do_wake)
	gomp_team_barrier_wake (&team->barrier, 1);
    }
}

對于上述所討論的內(nèi)容大家只需要了解相關(guān)的整體流程即可,細(xì)節(jié)除非你是 openmp 的開發(fā)人員,否則事實(shí)上沒有多大用,大家只需要了解大致過程即可,幫助你進(jìn)一步深入理解 OpenMP 內(nèi)部的運(yùn)行機(jī)制。

但是需要了解的是上面的整個(gè)過程還只是將任務(wù)提交到 OpenMP 內(nèi)部的任務(wù)隊(duì)列當(dāng)中,還沒有執(zhí)行,我們在前面談到過在線程執(zhí)行完并行域的代碼會(huì)執(zhí)行函數(shù) GOMP_parallel_end 在這個(gè)函數(shù)內(nèi)部還會(huì)調(diào)用其他函數(shù),最終會(huì)調(diào)用函數(shù) gomp_barrier_handle_tasks 將內(nèi)部的所有的任務(wù)執(zhí)行完成。

void
gomp_barrier_handle_tasks (gomp_barrier_state_t state)
{
  struct gomp_thread *thr = gomp_thread ();
  struct gomp_team *team = thr->ts.team;
  struct gomp_task *task = thr->task;
  struct gomp_task *child_task = NULL;
  struct gomp_task *to_free = NULL;
  // 首先對全局的隊(duì)列結(jié)構(gòu)進(jìn)行加鎖操作
  gomp_mutex_lock (&team->task_lock);
  if (gomp_barrier_last_thread (state))
    {
      if (team->task_count == 0)
	{
	  gomp_team_barrier_done (&team->barrier, state);
	  gomp_mutex_unlock (&team->task_lock);
	  gomp_team_barrier_wake (&team->barrier, 0);
	  return;
	}
      gomp_team_barrier_set_waiting_for_tasks (&team->barrier);
    }
  while (1)
    {
      if (team->task_queue != NULL)
	{
	  struct gomp_task *parent;
	// 從任務(wù)隊(duì)列當(dāng)中拿出一個(gè)任務(wù)
	  child_task = team->task_queue;
	  parent = child_task->parent;
	  if (parent && parent->children == child_task)
	    parent->children = child_task->next_child;
	  child_task->prev_queue->next_queue = child_task->next_queue;
	  child_task->next_queue->prev_queue = child_task->prev_queue;
	  if (child_task->next_queue != child_task)
	    team->task_queue = child_task->next_queue;
	  else
	    team->task_queue = NULL;
	  child_task->kind = GOMP_TASK_TIED;
	  team->task_running_count++;
	  if (team->task_count == team->task_running_count)
	    gomp_team_barrier_clear_task_pending (&team->barrier);
	}
      gomp_mutex_unlock (&team->task_lock);
      if (to_free) // 釋放任務(wù)的內(nèi)存空間 to_free 在后面會(huì)被賦值成 child_task
	{
	  gomp_finish_task (to_free);
	  free (to_free);
	  to_free = NULL;
	}
      if (child_task) // 調(diào)用任務(wù)對應(yīng)的函數(shù)
	{
	  thr->task = child_task;
	  child_task->fn (child_task->fn_data);
	  thr->task = task;
	}
      else
	return; // 退出 while 循環(huán)
      gomp_mutex_lock (&team->task_lock);
      if (child_task)
	{
	  struct gomp_task *parent = child_task->parent;
	  if (parent)
	    {
	      child_task->prev_child->next_child = child_task->next_child;
	      child_task->next_child->prev_child = child_task->prev_child;
	      if (parent->children == child_task)
		{
		  if (child_task->next_child != child_task)
		    parent->children = child_task->next_child;
		  else
		    {
		      /* We access task->children in GOMP_taskwait
			 outside of the task lock mutex region, so
			 need a release barrier here to ensure memory
			 written by child_task->fn above is flushed
			 before the NULL is written.  */
		      __atomic_store_n (&parent->children, NULL,
					MEMMODEL_RELEASE);
		      if (parent->in_taskwait)
			gomp_sem_post (&parent->taskwait_sem);
		    }
		}
	    }
	  gomp_clear_parent (child_task->children);
	  to_free = child_task;
	  child_task = NULL;
	  team->task_running_count--;
	  if (--team->task_count == 0
	      && gomp_team_barrier_waiting_for_tasks (&team->barrier))
	    {
	      gomp_team_barrier_done (&team->barrier, state);
	      gomp_mutex_unlock (&team->task_lock);
	      gomp_team_barrier_wake (&team->barrier, 0);
	      gomp_mutex_lock (&team->task_lock);
	    }
	}
    }
}

總結(jié)

在本篇文章當(dāng)中主要給大家介紹了,OpenMP 內(nèi)部對于任務(wù)的處理流程,這其中的細(xì)節(jié)非常復(fù)雜,大家只需要了解它的整個(gè)工作流程即可,這已經(jīng)能夠幫助大家理清楚整個(gè) OpenMP 內(nèi)部是如何對任務(wù)進(jìn)行處理的,如果大家感興趣可以自行研讀源程序。

更多精彩內(nèi)容合集可訪問項(xiàng)目:github.com/Chang-LeHun…

以上就是OpenMP task construct 實(shí)現(xiàn)原理及源碼示例解析的詳細(xì)內(nèi)容,更多關(guān)于OpenMP task construct原理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++?Qt開發(fā)之運(yùn)用QJSON模塊解析數(shù)據(jù)

    C++?Qt開發(fā)之運(yùn)用QJSON模塊解析數(shù)據(jù)

    JSON(JavaScript?Object?Notation)是一種輕量級(jí)的數(shù)據(jù)交換格式,它易于人閱讀和編寫,也易于機(jī)器解析和生成,本文主要介紹了Qt如何運(yùn)用QJson組件的實(shí)現(xiàn)對JSON文本的靈活解析功能,需要的可以參考下
    2024-01-01
  • C++?LeetCode542矩陣示例詳解

    C++?LeetCode542矩陣示例詳解

    這篇文章主要為大家介紹了C++?LeetCode542矩陣示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • C語言指針和數(shù)組深入探究使用方法

    C語言指針和數(shù)組深入探究使用方法

    在C語言和C++等語言中,數(shù)組元素全為指針變量的數(shù)組稱為指針數(shù)組,指針數(shù)組中的元素都必須具有相同的存儲(chǔ)類型、指向相同數(shù)據(jù)類型的指針變量。指針數(shù)組比較適合用來指向若干個(gè)字符串,使字符串處理更加方便、靈活
    2022-08-08
  • C++迭代器iterator詳解

    C++迭代器iterator詳解

    這篇文章主要為大家詳細(xì)介紹了C++迭代器模式Iterator,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下希望能給你帶來幫助
    2021-08-08
  • C++?動(dòng)態(tài)規(guī)劃算法使用分析

    C++?動(dòng)態(tài)規(guī)劃算法使用分析

    動(dòng)態(tài)規(guī)劃算法通常用于求解具有某種最優(yōu)性質(zhì)的問題。在這類問題中,可能會(huì)有許多可行解。每一個(gè)解都對應(yīng)于一個(gè)值,我們希望找到具有最優(yōu)值的解
    2022-03-03
  • C++ Array容器的顯示和隱式實(shí)例化詳細(xì)介紹

    C++ Array容器的顯示和隱式實(shí)例化詳細(xì)介紹

    這篇文章主要介紹了C++中Array容器的隱式實(shí)例化和顯式實(shí)例化,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-10-10
  • C語言代碼實(shí)現(xiàn)三子棋游戲

    C語言代碼實(shí)現(xiàn)三子棋游戲

    這篇文章主要為大家詳細(xì)介紹了C語言代碼實(shí)現(xiàn)三子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-11-11
  • MFC修改編輯框光標(biāo)顯示位置方法詳解

    MFC修改編輯框光標(biāo)顯示位置方法詳解

    這篇文章主要介紹了在MFC中利用CComboBox控件修改編輯框光標(biāo)顯示位置的兩種解決方法,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-02-02
  • STL容器之list源碼詳細(xì)解讀

    STL容器之list源碼詳細(xì)解讀

    這篇文章主要介紹了STL容器之list源碼詳細(xì)解讀,相對于vector的連續(xù)線性空間,list就顯得更加復(fù)雜,它每插入或者刪除一個(gè)元素,就配置或釋放一個(gè)元素空間,需要的朋友可以參考下
    2024-01-01
  • C語言中輸入函數(shù)(scanf()、fgets()和gets())的區(qū)別詳解

    C語言中輸入函數(shù)(scanf()、fgets()和gets())的區(qū)別詳解

    這篇文章主要給大家介紹了關(guān)于C語言中三種輸入函數(shù)(scanf()、fgets()和gets())區(qū)別的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11

最新評論