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

OpenMP中For Construct對(duì)dynamic的調(diào)度方式詳解

 更新時(shí)間:2023年02月03日 15:44:34   作者:一無(wú)是處的研究僧  
在本篇文章當(dāng)中主要給大家介紹 OpenMp for construct 的實(shí)現(xiàn)原理,與他相關(guān)的動(dòng)態(tài)庫(kù)函數(shù)分析以及對(duì) dynamic 的調(diào)度方式進(jìn)行分析,希望對(duì)大家有所幫助

前言

在本篇文章當(dāng)中主要給大家介紹 OpenMp for construct 的實(shí)現(xiàn)原理,以及與他相關(guān)的動(dòng)態(tài)庫(kù)函數(shù)分析,與 for construct 非常相關(guān)的是循環(huán)的調(diào)度方式,在 OpenMP 當(dāng)中一共有四種調(diào)調(diào)方式,auto, dynamic, guided, runtime, 在本篇文章當(dāng)中主要是對(duì) dynamic 的調(diào)度方式進(jìn)行分析。

前置知識(shí)

在介紹 for construct 的實(shí)現(xiàn)原理之前,我們首先需要了解一下編譯器是如何處理函數(shù)參數(shù)傳遞的(本文基于 x86_64 ISA),我們來(lái)看一下下面的代碼在編譯之后函數(shù)參數(shù)的傳遞情況。

在前面的文章當(dāng)中我們已經(jīng)談到過(guò)了,在 x86 當(dāng)中參數(shù)傳遞的規(guī)約,具體的內(nèi)容如下所示:

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

我們現(xiàn)在使用下面的代碼來(lái)分析一下具體的情況(因?yàn)榍懊媸褂眉拇嫫髦荒軌騻鬟f 6 個(gè)參數(shù),而在后面我們要分析的動(dòng)態(tài)庫(kù)函數(shù)當(dāng)中會(huì)傳遞 7 個(gè)參數(shù),因此這里我們使用 8 個(gè)參數(shù)來(lái)測(cè)試一下具體的參數(shù)傳遞情況):

#include "stdio.h"
 
void echo(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8)
{
  printf("%d %d %d %d %d %d %d %d\n", a8, a7, a1, a2, a3, a4, a5, a6);
}
 
int main()
{
  echo(1, 2, 3, 4 ,5 ,6, 7, 8);
  return 0;
}

上面的程序的反匯編結(jié)果如下所示:

000000000040053d <echo>:
  40053d:       55                      push   %rbp
  40053e:       48 89 e5                mov    %rsp,%rbp
  400541:       48 83 ec 30             sub    $0x30,%rsp
  400545:       89 7d fc                mov    %edi,-0x4(%rbp)
  400548:       89 75 f8                mov    %esi,-0x8(%rbp)
  40054b:       89 55 f4                mov    %edx,-0xc(%rbp)
  40054e:       89 4d f0                mov    %ecx,-0x10(%rbp)
  400551:       44 89 45 ec             mov    %r8d,-0x14(%rbp)
  400555:       44 89 4d e8             mov    %r9d,-0x18(%rbp)
  400559:       8b 7d f4                mov    -0xc(%rbp),%edi
  40055c:       8b 75 f8                mov    -0x8(%rbp),%esi
  40055f:       8b 55 fc                mov    -0x4(%rbp),%edx
  400562:       8b 45 18                mov    0x18(%rbp),%eax # a8
  400565:       8b 4d e8                mov    -0x18(%rbp),%ecx
  400568:       89 4c 24 10             mov    %ecx,0x10(%rsp)
  40056c:       8b 4d ec                mov    -0x14(%rbp),%ecx
  40056f:       89 4c 24 08             mov    %ecx,0x8(%rsp)
  400573:       8b 4d f0                mov    -0x10(%rbp),%ecx
  400576:       89 0c 24                mov    %ecx,(%rsp)
  400579:       41 89 f9                mov    %edi,%r9d
  40057c:       41 89 f0                mov    %esi,%r8d
  40057f:       89 d1                   mov    %edx,%ecx
  400581:       8b 55 10                mov    0x10(%rbp),%edx # a7
  400584:       89 c6                   mov    %eax,%esi # a8
  400586:       bf 64 06 40 00          mov    $0x400664,%edi
  40058b:       b8 00 00 00 00          mov    $0x0,%eax
  400590:       e8 8b fe ff ff          callq  400420 <printf@plt>
  400595:       c9                      leaveq 
 
0000000000400597 <main>:
  400597:       55                      push   %rbp
  400598:       48 89 e5                mov    %rsp,%rbp
  40059b:       48 83 ec 10             sub    $0x10,%rsp
  40059f:       c7 44 24 08 08 00 00    movl   $0x8,0x8(%rsp) # 保存參數(shù) 8 
  4005a6:       00 
  4005a7:       c7 04 24 07 00 00 00    movl   $0x7,(%rsp) # 保存參數(shù) 7 
  4005ae:       41 b9 06 00 00 00       mov    $0x6,%r9d # 保存參數(shù) 6 
  4005b4:       41 b8 05 00 00 00       mov    $0x5,%r8d # 保存參數(shù) 5 
  4005ba:       b9 04 00 00 00          mov    $0x4,%ecx # 保存參數(shù) 4 
  4005bf:       ba 03 00 00 00          mov    $0x3,%edx # 保存參數(shù) 3 
  4005c4:       be 02 00 00 00          mov    $0x2,%esi # 保存參數(shù) 2 
  4005c9:       bf 01 00 00 00          mov    $0x1,%edi # 保存參數(shù) 1
  4005ce:       e8 6a ff ff ff          callq  40053d <echo>
  4005d3:       b8 00 00 00 00          mov    $0x0,%eax
  4005d8:       c9                      leaveq 
  4005d9:       c3                      retq   
  4005da:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

從上面的匯編程序我們可以知道 1 - 6,這幾個(gè)參數(shù)確實(shí)是通過(guò)寄存器傳遞的,對(duì)應(yīng)的寄存器就是上文當(dāng)中我們提到不同的參數(shù)對(duì)應(yīng)的寄存器。但是參數(shù) 7 和參數(shù) 8 是保存在棧上的。根據(jù)上面的 main 函數(shù)的匯編程序分析,他對(duì)應(yīng)的棧幀的內(nèi)存布局如下所示:

我們?cè)趤?lái)分析一下 echo 函數(shù)當(dāng)中 printf 函數(shù)參數(shù)的傳遞情況,第二個(gè)參數(shù)和第三個(gè)參數(shù)分別是 a8, a7,應(yīng)該分別保存到寄存器 rsi/esi, rdx/edx 當(dāng)中,在上面的匯編代碼當(dāng)中已經(jīng)使用注釋的方式進(jìn)行標(biāo)注出來(lái)了,從下往上進(jìn)行分析可以看到 a8 保存在位置 0x18(%rbp),a7 保存在 0x10(%rbp),這個(gè)地址正是 main 函數(shù)保存 a7(當(dāng)進(jìn)入函數(shù) echo 之后,a7,和 a8 的位置分別是 rsp + 0x10), a8(當(dāng)進(jìn)入函數(shù) echo 之后,a7,和 a8 的位置分別是 rsp + 0x10 + 0x8) 的位置,具體可以結(jié)合上面的內(nèi)存布局圖進(jìn)行分析。

dynamic 調(diào)度方式分析

我們使用下面的代碼來(lái)分析一下動(dòng)態(tài)調(diào)度的情況下整個(gè)程序的執(zhí)行流程是怎么樣的:

#pragma omp parallel for num_threads(t) schedule(dynamic, size)
for (i = lb; i <= ub; i++)
  body;

編譯器會(huì)將上面的程序編譯成下面的形式:

void subfunction (void *data)
{
  long _s0, _e0;
  while (GOMP_loop_dynamic_next (&_s0, &_e0))
  {
    long _e1 = _e0, i;
    for (i = _s0; i < _e1; i++)
      body;
  }
  // GOMP_loop_end_nowait 這個(gè)函數(shù)的主要作用就是釋放數(shù)據(jù)的內(nèi)存空間 在后文當(dāng)中不進(jìn)行分析
  GOMP_loop_end_nowait ();
}
 
GOMP_parallel_loop_dynamic_start (subfunction, NULL, t, lb, ub+1, 1, size);
subfunction (NULL);
// 這個(gè)函數(shù)在前面的很多文章已經(jīng)分析過(guò) 本文也不在進(jìn)行分析
GOMP_parallel_end ();
void
GOMP_parallel_loop_dynamic_start (void (*fn) (void *), void *data,
				  unsigned num_threads, long start, long end,
				  long incr, long chunk_size)
{
  gomp_parallel_loop_start (fn, data, num_threads, start, end, incr,
			    GFS_DYNAMIC, chunk_size);
}
 
static void
gomp_parallel_loop_start (void (*fn) (void *), void *data,
			  unsigned num_threads, long start, long end,
			  long incr, enum gomp_schedule_type sched,
			  long chunk_size)
{
  struct gomp_team *team;
  // 解析具體創(chuàng)建多少個(gè)線(xiàn)程
  num_threads = gomp_resolve_num_threads (num_threads, 0);
  // 創(chuàng)建一個(gè)含有 num_threads 個(gè)線(xiàn)程的線(xiàn)程組
  team = gomp_new_team (num_threads);
  // 對(duì)線(xiàn)程組的數(shù)據(jù)進(jìn)行初始化操作
  gomp_loop_init (&team->work_shares[0], start, end, incr, sched, chunk_size);
  // 啟動(dòng) num_threads 個(gè)線(xiàn)程執(zhí)行函數(shù) fn 
  gomp_team_start (fn, data, num_threads, team);
}
 
enum gomp_schedule_type
{
  GFS_RUNTIME, // runtime 調(diào)度方式
  GFS_STATIC,	 // static  調(diào)度方式
  GFS_DYNAMIC, // dynamic 調(diào)度方式
  GFS_GUIDED,	 // guided  調(diào)度方式
  GFS_AUTO     // auto    調(diào)度方式
};
 

在上面的程序當(dāng)中 GOMP_parallel_loop_dynamic_start,有 7 個(gè)參數(shù),我們接下來(lái)仔細(xì)解釋一下這七個(gè)參數(shù)的含義:

  • fn,函數(shù)指針也就是并行域被編譯之后的函數(shù)。
  • data,指向共享或者私有的數(shù)據(jù),在并行域當(dāng)中可能會(huì)使用外部的一些變量。
  • num_threads,并行域當(dāng)中指定啟動(dòng)線(xiàn)程的個(gè)數(shù)。
  • start,for 循環(huán)迭代的初始值,比如 for(int i = 0; ?? 這個(gè) start 就是 0 。
  • end,for 循環(huán)迭代的最終值,比如 for(int i = 0; i < 100; i++) 這個(gè) end 就是 100 。
  • incr,這個(gè)值一般都是 1 或者 -1,如果是 for 循環(huán)是從小到達(dá)迭代這個(gè)值就是 1,反之就是 -1。
  • chunk_size,這個(gè)就是給一個(gè)線(xiàn)程劃分塊的時(shí)候一個(gè)塊的大小,比如 schedule(dynamic, 1),這個(gè) chunk_size 就等于 1 。

在函數(shù) GOMP_parallel_loop_dynamic_start 當(dāng)中會(huì)調(diào)用函數(shù) gomp_parallel_loop_start ,這個(gè)函數(shù)的主要作用就是將整個(gè)循環(huán)的起始位置信息保存到線(xiàn)程組內(nèi)部,那么就能夠在函數(shù) GOMP_loop_dynamic_next 當(dāng)中直接使用這些信息進(jìn)行不同線(xiàn)程的分塊劃分。GOMP_loop_dynamic_next 最終會(huì)調(diào)用函數(shù) gomp_loop_dynamic_next ,其源代碼如下所示:

static bool
gomp_loop_dynamic_next (long *istart, long *iend)
{
  bool ret;
  ret = gomp_iter_dynamic_next (istart, iend);
  return ret;
}

gomp_loop_dynamic_next 函數(shù)的返回值是一個(gè)布爾值:

  • 如果返回值為 true ,則說(shuō)明還有剩余的分塊需要執(zhí)行。
  • 如果返回值為 false,則說(shuō)明沒(méi)有剩余的分塊需要執(zhí)行了,根據(jù)前面 dynamic 編譯之后的結(jié)果,那么就會(huì)退出 while 循環(huán)。

gomp_iter_dynamic_next 是劃分具體的分塊,并且將分塊的起始位置保存到變量 istart 和 iend 當(dāng)中,因?yàn)閭鬟f的是指針,就能夠使用 s0 和 e0 得到數(shù)據(jù)的值,下面是 gomp_iter_dynamic_next 的源代碼,就是具體的劃分算法了。

bool
gomp_iter_dynamic_next (long *pstart, long *pend)
{
  // 得到當(dāng)前線(xiàn)程的指針
  struct gomp_thread *thr = gomp_thread ();
  // 得到線(xiàn)程組共享的數(shù)據(jù)
  struct gomp_work_share *ws = thr->ts.work_share;
  long start, end, nend, chunk, incr;
  
  // 保存迭代的最終值
  end = ws->end;
  // 這個(gè)值一般都是 1
  incr = ws->incr;
  // 保存分塊的大小 chunk size
  chunk = ws->chunk_size;
  
  // ws->mode 在數(shù)據(jù)分塊比較小的時(shí)候就是 1 在數(shù)據(jù)分塊比較大的時(shí)候就是 0
  if (__builtin_expect (ws->mode, 1))
    {
    // __sync_fetch_and_add 函數(shù)是一個(gè)原子操作 ws->next 的初始值為 for 循環(huán)的起始位置值
    // 這個(gè)函數(shù)的返回值是 ws->next 的舊值 然后會(huì)將 ws->next 的值加上 chunk
    // 并且整個(gè)操作是原子的 是并發(fā)安全的
      long tmp = __sync_fetch_and_add (&ws->next, chunk);
    // 從小到大迭代
      if (incr > 0)
	{
	  if (tmp >= end)
	    return false;
    // 分塊的最終位置
	  nend = tmp + chunk;
    // 溢出保護(hù)操作 分塊的值需要小于最終的迭代位置
	  if (nend > end)
	    nend = end;
    // 將分塊的值賦值給 pstart 和 pend 這樣就能夠在并行域當(dāng)中得到這個(gè)分塊的區(qū)間了
	  *pstart = tmp;
	  *pend = nend;
	  return true;
	}
      else
	{
    // 同樣的原理不過(guò)是從大到小達(dá)迭代
	  if (tmp <= end)
	    return false;
	  nend = tmp + chunk;
	  if (nend < end)
	    nend = end;
	  *pstart = tmp;
	  *pend = nend;
	  return true;
	}
    }
  
  // 當(dāng)數(shù)據(jù)分塊比較大的時(shí)候執(zhí)行下面的操作
  // 下面的整體的流程相對(duì)比較容易理解整個(gè)過(guò)程就是一個(gè)比較并交換的過(guò)程
  // 當(dāng)比較并交換成功之后就返回結(jié)果 返回為 true 或者分塊已經(jīng)分完的話(huà)也進(jìn)行返回
  start = ws->next;
  while (1)
    {
      long left = end - start;
      long tmp;
      // 如果分塊已經(jīng)完全分完 就直接返回 false 
      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;
}

gomp_iter_dynamic_next 函數(shù)當(dāng)中有兩種情況的劃分方式:

  • 當(dāng)數(shù)據(jù)塊相對(duì)比較小的時(shí)候,說(shuō)明劃分的次數(shù)就會(huì)相對(duì)多一點(diǎn),在這種情況下如果使用 CAS 的話(huà)成功的概率就會(huì)相對(duì)低,對(duì)應(yīng)的就會(huì)降低程序執(zhí)行的效率,因此選擇 __sync_fetch_and_add 以減少多線(xiàn)程的競(jìng)爭(zhēng)情況,降低 CPU 的消耗。
  • 當(dāng)數(shù)據(jù)塊比較大的時(shí)候,說(shuō)明劃分的次數(shù)相對(duì)比較小,就使用比較并交換的操作(CAS),這樣多個(gè)線(xiàn)程在進(jìn)行競(jìng)爭(zhēng)的時(shí)候開(kāi)銷(xiāo)就比較小。

在上面的文章當(dāng)中我們提到了,gomp_loop_init 函數(shù)是對(duì)線(xiàn)程共享數(shù)據(jù) work_share 進(jìn)行初始化操作,如果你對(duì)具體 work_share 中的數(shù)據(jù)初始化規(guī)則感興趣,下面是對(duì)其初始化的程序:

static inline void
gomp_loop_init (struct gomp_work_share *ws, long start, long end, long incr,
		enum gomp_schedule_type sched, long chunk_size)
{
  ws->sched = sched;
  ws->chunk_size = chunk_size;
  /* Canonicalize loops that have zero iterations to ->next == ->end.  */
  ws->end = ((incr > 0 && start > end) || (incr < 0 && start < end))
	    ? start : end;
  ws->incr = incr;
  ws->next = start;
  if (sched == GFS_DYNAMIC)
    {
      ws->chunk_size *= incr;
 
#ifdef HAVE_SYNC_BUILTINS
      {
	/* For dynamic scheduling prepare things to make each iteration
	   faster.  */
	struct gomp_thread *thr = gomp_thread ();
	struct gomp_team *team = thr->ts.team;
	long nthreads = team ? team->nthreads : 1;
 
	if (__builtin_expect (incr > 0, 1))
	  {
	    /* Cheap overflow protection.  */
	    if (__builtin_expect ((nthreads | ws->chunk_size)
				  >= 1UL << (sizeof (long)
					     * __CHAR_BIT__ / 2 - 1), 0))
	      ws->mode = 0;
	    else
	      ws->mode = ws->end < (LONG_MAX
				    - (nthreads + 1) * ws->chunk_size);
	  }
	/* Cheap overflow protection.  */
	else if (__builtin_expect ((nthreads | -ws->chunk_size)
				   >= 1UL << (sizeof (long)
					      * __CHAR_BIT__ / 2 - 1), 0))
	  ws->mode = 0;
	else
	  ws->mode = ws->end > (nthreads + 1) * -ws->chunk_size - LONG_MAX;
      }
#endif
    }
}

實(shí)例分析

在本小節(jié)當(dāng)中我們將使用一個(gè)實(shí)際的例子去分析上面我們所談到的整個(gè)過(guò)程:

#include <stdio.h>
#include <omp.h>
 
int main()
{
#pragma omp parallel for num_threads(4) default(none) schedule(dynamic, 2)
  for(int i = 0; i < 12; ++i)
  {
    printf("i = %d tid = %d\n", i, omp_get_thread_num());
  }
  return 0;
}

上面的程序被編譯之后的結(jié)果如下所示,具體的程序分析和注釋都在下面的匯編程序當(dāng)中:

000000000040073d <main>:
  40073d:       55                      push   %rbp
  40073e:       48 89 e5                mov    %rsp,%rbp
  400741:       48 83 ec 20             sub    $0x20,%rsp
  400745:       48 c7 04 24 02 00 00    movq   $0x2,(%rsp) # 這個(gè)就是 chunk size 符合上面的代碼當(dāng)中指定的 2
  40074c:       00 
  40074d:       41 b9 01 00 00 00       mov    $0x1,%r9d # 因?yàn)槭菑男〉竭_(dá) incr 這個(gè)參數(shù)是 1
  400753:       41 b8 0c 00 00 00       mov    $0xc,%r8d # 這個(gè)參數(shù)是 end 符合上面的程序 12
  400759:       b9 00 00 00 00          mov    $0x0,%ecx # 這個(gè)參數(shù)是 start 符合上面的程序 1
  40075e:       ba 04 00 00 00          mov    $0x4,%edx # num_threads(4) 線(xiàn)程的個(gè)數(shù)是 4
  400763:       be 00 00 00 00          mov    $0x0,%esi # 因?yàn)樯厦娴拇a當(dāng)中并沒(méi)有在并行域當(dāng)中使用數(shù)據(jù) 因此這個(gè)數(shù)據(jù)為 0 也就是 NULL 
  400768:       bf 88 07 40 00          mov    $0x400788,%edi # 函數(shù)指針 main._omp_fn.0
  40076d:       e8 ce fe ff ff          callq  400640 <GOMP_parallel_loop_dynamic_start@plt>
  400772:       bf 00 00 00 00          mov    $0x0,%edi
  400777:       e8 0c 00 00 00          callq  400788 <main._omp_fn.0>
  40077c:       e8 5f fe ff ff          callq  4005e0 <GOMP_parallel_end@plt>
  400781:       b8 00 00 00 00          mov    $0x0,%eax
  400786:       c9                      leaveq 
  400787:       c3                      retq
  
0000000000400788 <main._omp_fn.0>:
  400788:       55                      push   %rbp
  400789:       48 89 e5                mov    %rsp,%rbp
  40078c:       53                      push   %rbx
  40078d:       48 83 ec 38             sub    $0x38,%rsp
  400791:       48 89 7d c8             mov    %rdi,-0x38(%rbp)
  400795:       c7 45 ec 00 00 00 00    movl   $0x0,-0x14(%rbp)
  40079c:       48 8d 55 e0             lea    -0x20(%rbp),%rdx
  4007a0:       48 8d 45 d8             lea    -0x28(%rbp),%rax
  4007a4:       48 89 d6                mov    %rdx,%rsi
  4007a7:       48 89 c7                mov    %rax,%rdi
  4007aa:       e8 21 fe ff ff          callq  4005d0 <GOMP_loop_dynamic_next@plt>
  4007af:       84 c0                   test   %al,%al # 如果 GOMP_loop_dynamic_next 返回值是 0 則跳轉(zhuǎn)到 4007fb 執(zhí)行函數(shù) GOMP_loop_end_nowait
  4007b1:       74 48                   je     4007fb <main._omp_fn.0+0x73>
  4007b3:       48 8b 45 d8             mov    -0x28(%rbp),%rax
  4007b7:       89 45 ec                mov    %eax,-0x14(%rbp)
  4007ba:       48 8b 45 e0             mov    -0x20(%rbp),%rax
  4007be:       89 c3                   mov    %eax,%ebx
  # ===========================下面的代碼就是執(zhí)行循環(huán)和 body =================
  4007c0:       e8 2b fe ff ff          callq  4005f0 <omp_get_thread_num@plt>
  4007c5:       89 c2                   mov    %eax,%edx
  4007c7:       8b 45 ec                mov    -0x14(%rbp),%eax
  4007ca:       89 c6                   mov    %eax,%esi
  4007cc:       bf 94 08 40 00          mov    $0x400894,%edi
  4007d1:       b8 00 00 00 00          mov    $0x0,%eax
  4007d6:       e8 25 fe ff ff          callq  400600 <printf@plt>
  4007db:       83 45 ec 01             addl   $0x1,-0x14(%rbp)
  4007df:       39 5d ec                cmp    %ebx,-0x14(%rbp)
  4007e2:       7c dc                   jl     4007c0 <main._omp_fn.0+0x38>
  # ======================================================================
  # ============下面的代碼主要是進(jìn)行 while 循環(huán)查看循環(huán)是否執(zhí)行完成==============
  4007e4:       48 8d 55 e0             lea    -0x20(%rbp),%rdx
  4007e8:       48 8d 45 d8             lea    -0x28(%rbp),%rax
  4007ec:       48 89 d6                mov    %rdx,%rsi
  4007ef:       48 89 c7                mov    %rax,%rdi
  4007f2:       e8 d9 fd ff ff          callq  4005d0 <GOMP_loop_dynamic_next@plt>
  4007f7:       84 c0                   test   %al,%al
  4007f9:       75 b8                   jne    4007b3 <main._omp_fn.0+0x2b>
  # ======================================================================
  4007fb:       e8 10 fe ff ff          callq  400610 <GOMP_loop_end_nowait@plt>
  400800:       48 83 c4 38             add    $0x38,%rsp
  400804:       5b                      pop    %rbx
  400805:       5d                      pop    %rbp
  400806:       c3                      retq   
  400807:       66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
  40080e:       00 00

總結(jié)

在本篇文章當(dāng)中我們主要分析了 OpenMP 當(dāng)中 for 循環(huán)動(dòng)態(tài)調(diào)度方式的具體實(shí)現(xiàn)原理,以及動(dòng)態(tài)庫(kù)函數(shù)的分析。整個(gè)過(guò)程主要有兩大部分,一個(gè)是編譯角度,編譯器會(huì)將 for construct 編譯成什么樣子,以及動(dòng)態(tài)庫(kù)函數(shù)具體是如何劃分迭代分塊的。在迭代分塊當(dāng)中主要分為兩種方式,當(dāng)分塊數(shù)目多的時(shí)候不使用 CAS 因?yàn)檫@樣線(xiàn)程之間競(jìng)爭(zhēng)比較激烈,但是當(dāng)分塊數(shù)目比較小的時(shí)候就使用 CAS ,這種做法可以提高程序執(zhí)行的效率。

以上就是OpenMP中For Construct對(duì)dynamic的調(diào)度方式詳解的詳細(xì)內(nèi)容,更多關(guān)于OpenMP For Construct dynamic的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • boost.asio框架系列之定時(shí)器Timer

    boost.asio框架系列之定時(shí)器Timer

    這篇文章介紹了boost.asio框架系列之定時(shí)器Timer,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • 學(xué)生成績(jī)管理系統(tǒng)C語(yǔ)言代碼實(shí)現(xiàn)

    學(xué)生成績(jī)管理系統(tǒng)C語(yǔ)言代碼實(shí)現(xiàn)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言代碼實(shí)現(xiàn)學(xué)生成績(jī)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • C++如何使用new來(lái)初始化指向類(lèi)的指針

    C++如何使用new來(lái)初始化指向類(lèi)的指針

    這篇文章主要介紹了C++如何使用new來(lái)初始化指向類(lèi)的指針問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Qt實(shí)現(xiàn)簡(jiǎn)單的TCP通信

    Qt實(shí)現(xiàn)簡(jiǎn)單的TCP通信

    這篇文章主要為大家詳細(xì)介紹了Qt實(shí)現(xiàn)簡(jiǎn)單的TCP通信,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • C++之try catch 異常處理入門(mén)實(shí)例

    C++之try catch 異常處理入門(mén)實(shí)例

    我們通常希望自己編寫(xiě)的程序能夠在異常的情況下也能作出相應(yīng)的處理,而不至于程序莫名其妙地中斷或者中止運(yùn)行了。在設(shè)計(jì)程序時(shí)應(yīng)充分考慮各種異常情況,并加以處理
    2018-01-01
  • C語(yǔ)言用Easyx繪制圍棋和象棋的棋盤(pán)

    C語(yǔ)言用Easyx繪制圍棋和象棋的棋盤(pán)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言用Easyx繪制圍棋和象棋的棋盤(pán),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • C++實(shí)現(xiàn)矩陣原地轉(zhuǎn)置算法

    C++實(shí)現(xiàn)矩陣原地轉(zhuǎn)置算法

    這篇文章主要介紹了C++實(shí)現(xiàn)矩陣原地轉(zhuǎn)置算法,非常經(jīng)典的算法,需要的朋友可以參考下
    2014-08-08
  • C++ QT智能指針的使用詳解

    C++ QT智能指針的使用詳解

    這篇文章主要介紹了C++ QT智能指針的使用,Qt是一個(gè)跨平臺(tái)的C++框架,主要用來(lái)開(kāi)發(fā)圖形用戶(hù)界面程序,也可以開(kāi)發(fā)不帶界面的命令行程序,下面我們來(lái)了解QT智能指針是如何使用的
    2023-12-12
  • C語(yǔ)言詳解冒泡排序?qū)崿F(xiàn)

    C語(yǔ)言詳解冒泡排序?qū)崿F(xiàn)

    冒泡排序是一種簡(jiǎn)單的排序算法,它也是一種穩(wěn)定排序算法。其實(shí)現(xiàn)原理是重復(fù)掃描待排序序列,并比較每一對(duì)相鄰的元素,當(dāng)該對(duì)元素順序不正確時(shí)進(jìn)行交換。一直重復(fù)這個(gè)過(guò)程,直到?jīng)]有任何兩個(gè)相鄰元素可以交換,就表明完成了排序
    2022-04-04
  • C 標(biāo)準(zhǔn)I/O庫(kù)的粗略實(shí)現(xiàn)教程

    C 標(biāo)準(zhǔn)I/O庫(kù)的粗略實(shí)現(xiàn)教程

    下面小編就為大家分享一篇C 標(biāo)準(zhǔn)I/O庫(kù)的粗略實(shí)現(xiàn)教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12

最新評(píng)論