OPENMP FOR CONSTRUCT GUIDED 调度方式实现原理和源码分析

前言

在本篇文章当中主要给大家介绍在 OpenMP 当中 guided 调度方式的实现原理。这个调度方式其实和 dynamic 调度方式非常相似的,从编译器角度来说基本上是一样的,在本篇文章当中就不介绍一些相关的必备知识了,如果不了解可以先看这篇文章 OpenMP For Construct dynamic 调度方式实现原理和源码分析

GUIDED 调度方式分析

我们使用下面的代码来分析一下 guided 调度的情况下整个程序的执行流程是怎么样的:

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

编译器会将上面的程序编译成下面的形式:

void subfunction (void *data)
{
 long _s0, _e0;
 while (GOMP_loop_guided_next (&_s0, &_e0))
{
   long _e1 = _e0, i;
   for (i = _s0; i < _e1; i++)
     body;
}
 // GOMP_loop_end_nowait 这个函数的主要作用就是释放数据的内存空间 在后文当中不进行分析
 GOMP_loop_end_nowait ();
}

GOMP_parallel_loop_guided_start (subfunction, NULL, t, lb, ub+1, 1, size);
subfunction (NULL);
// 这个函数在前面的很多文章已经分析过 本文也不在进行分析
GOMP_parallel_end ();

根据上面的代码可以知道,上面的代码当中最主要的两个函数就是 GOMP_parallel_loop_guided_start 和 GOMP_loop_guided_next,现在我们来分析一下他们的源代码:

  • GOMP_parallel_loop_guided_start
void
GOMP_parallel_loop_guided_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_GUIDED, 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;
// 解析到底启动几个线程执行并行域的代码
 num_threads = gomp_resolve_num_threads (num_threads, 0);
 // 创建线程组
 team = gomp_new_team (num_threads);
 // 对共享数据进行初始化操作
 gomp_loop_init (&team->work_shares[0], start, end, incr, sched, chunk_size);
 // 启动线程组执行函数 fn
 gomp_team_start (fn, data, num_threads, team);
}

在上面的程序当中 GOMP_parallel_loop_guided_start,有 7 个参数,我们接下来仔细解释一下这七个参数的含义:

  • fn,函数指针也就是并行域被编译之后的函数。
  • data,指向共享或者私有的数据,在并行域当中可能会使用外部的一些变量。
  • num_threads,并行域当中指定启动线程的个数。
  • start,for 循环迭代的初始值,比如 for(int i = 0; 这个 start 就是 0 。
  • end,for 循环迭代的最终值,比如 for(int i = 0; i < 100; i++) 这个 end 就是 100 。
  • incr,这个值一般都是 1 或者 -1,如果是 for 循环是从小到达迭代这个值就是 1,反之就是 -1,实际上这个值指的是 for 循环 i 大的增量。
  • chunk_size,这个就是给一个线程划分块的时候一个块的大小,比如 schedule(dynamic, 1),这个 chunk_size 就等于 1 。

事实上上面的代码和 GOMP_parallel_loop_dynamic_start 基本上一模一样,函数参数也一致,唯一的区别就是调度方式的不同,上面的代码和前面的文章 OpenMP For Construct dynamic 调度方式实现原理和源码分析 基本一样因此不再进行详细的分析。

  • GOMP_loop_guided_next,这是整个 guided 调度方式的核心代码(整个过程仍然使用 CAS 进行原子操作,保证并发安全)
static bool
gomp_loop_guided_next (long *istart, long *iend)
{
 bool ret;
 ret = gomp_iter_guided_next (istart, iend);
 return ret;
}

bool
gomp_iter_guided_next (long *pstart, long *pend)
{
 struct gomp_thread *thr = gomp_thread ();
 struct gomp_work_share *ws = thr->ts.work_share;
 struct gomp_team *team = thr->ts.team;
 unsigned long nthreads = team ? team->nthreads : 1;
 long start, end, nend, incr;
 unsigned long chunk_size;  // 下一个分块的起始位置
 start = ws->next;
 // 最终位置 不能够超过这个位置
 end = ws->end;
 incr = ws->incr;
 // chunk_size 是每个线程的分块大小
 chunk_size = ws->chunk_size;

 while (1)
  {
     unsigned long n, q;
     long tmp;
// 如果下一个分块的起始位置等于最终位置 那就说明没有需要继续分块的了 因此返回 false 表示没有分块需要执行了
     if (start == end)
return false;
  // 下面就是整个划分的逻辑 大家可以吧 incr = 1 带入 就能够知道每次线程分得的数据就是当前剩下的数据处以线程的个数
     n = (end - start) / incr;
     q = (n + nthreads - 1) / nthreads;

     if (q < chunk_size)
q = chunk_size;
     if (__builtin_expect (q <= n, 1))
nend = start + q * incr;
     else
nend = end;
  // 进行比较并交换操作 比较 start 和 ws->next 的值,如果相等则将 ws->next 的值变为 nend 并且返回 ws->next 原来的值
     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;
}

从上面的整个分析过程来看,guided 调度方式之所以每个线程的分块呈现递减趋势,是因为每次执行完一个 chunk size 之后,剩下的总的数据就少了,然后又除以线程数,因此每次得到的 chunk size 都是单调递减的。

总结

在本篇文章当中主要介绍了 OpenMP 当中 guided 调度方式当中数据的划分策略以及具体的实现代码, OpenMP 当中 for 循环的几种调度策略的越代码是非常相似的,只有具体的划分策略的 xxx_next 代码实现不同,因此整体来说是相对比较好阅读的。guided 调度方式主要是用剩下的数据个数除以线程的个数就是线程所得到的 chunk size 的大小,然后更新剩下的数据个数再次除以线程的个数就是下一个线程所得到的 chunk size 大小,如此反复直到划分完成。


更多精彩内容合集可访问项目:https://github.com/Chang-LeHung/CSCore

关注公众号:一无是处的研究僧,了解更多计算机(Java、Python、计算机系统基础、算法与数据结构)知识。

OPENMP FOR CONSTRUCT GUIDED 调度方式实现原理和源码分析的更多相关文章

  1. OpenMP For Construct dynamic 调度方式实现原理和源码分析

    OpenMP For Construct dynamic 调度方式实现原理和源码分析 前言 在本篇文章当中主要给大家介绍 OpenMp for construct 的实现原理,以及与他相关的动态库函数 ...

  2. Java并发编程(七)ConcurrentLinkedQueue的实现原理和源码分析

    相关文章 Java并发编程(一)线程定义.状态和属性 Java并发编程(二)同步 Java并发编程(三)volatile域 Java并发编程(四)Java内存模型 Java并发编程(五)Concurr ...

  3. Kubernetes Job Controller 原理和源码分析(一)

    概述什么是 JobJob 入门示例Job 的 specPod Template并发问题其他属性 概述 Job 是主要的 Kubernetes 原生 Workload 资源之一,是在 Kubernete ...

  4. Kubernetes Job Controller 原理和源码分析(二)

    概述程序入口Job controller 的创建Controller 对象NewController()podControlEventHandlerJob AddFunc DeleteFuncJob ...

  5. Kubernetes Job Controller 原理和源码分析(三)

    概述Job controller 的启动processNextWorkItem()核心调谐逻辑入口 - syncJob()Pod 数量管理 - manageJob()小结 概述 源码版本:kubern ...

  6. Java1.7 HashMap 实现原理和源码分析

    HashMap 源码分析是面试中常考的一项,下面一篇文章讲得很好,特地转载过来. 本文转自:https://www.cnblogs.com/chengxiao/p/6059914.html 参考博客: ...

  7. 深入ReentrantLock的实现原理和源码分析

    ReentrantLock是Java并发包中提供的一个可重入的互斥锁.ReentrantLock和synchronized在基本用法,行为语义上都是类似的,同样都具有可重入性.只不过相比原生的Sync ...

  8. ☕【Java深层系列】「并发编程系列」让我们一起探索一下CyclicBarrier的技术原理和源码分析

    CyclicBarrier和CountDownLatch CyclicBarrier和CountDownLatch 都位于java.util.concurrent这个包下,其工作原理的核心要点: Cy ...

  9. Express工作原理和源码分析一:创建路由

    Express是一基于Node的一个框架,用来快速创建Web服务的一个工具,为什么要使用Express呢,因为创建Web服务如果从Node开始有很多繁琐的工作要做,而Express为你解放了很多工作, ...

  10. Android AsyncTask运作原理和源码分析

    自10年大量看源码后,很少看了,抽时间把最新的源码看看! public abstract class AsyncTask<Params, Progress, Result> {     p ...

随机推荐

  1. C#.NET实现二分查找

    二分搜索法 定义 二分法查找,也称为折半法,是一种在有序数组中查找特定元素的搜索算法. 适用范围 当数据量很大并且有序时,适宜采用该方法. 基本思想 假设数据是按升序排序的,对于给定值key,从序列的 ...

  2. 4 c++编程-提高篇-STL简介

    ​ 重新系统学习c++语言,并将学习过程中的知识在这里抄录.总结.沉淀.同时希望对刷到的朋友有所帮助,一起加油哦!  生命就像一朵花,要拼尽全力绽放!死磕自个儿,身心愉悦! 写在前面,本篇章主要简单介 ...

  3. 大趋智能打印机java api

    对接飞鹅和易联云后 ,网上几乎没资料对大趋智能打印机java api分享,故此分享一波. 官方文档地址 SnParam.java package com.shanheyongmu.openapi.pa ...

  4. JDK中内嵌JS引擎介绍及使用

    原文: JDK中内嵌JS引擎介绍及使用 - Stars-One的杂货小窝 最近研究阅读这个APP,其主要功能就是通过一个个书源,从而实现移动端阅读的体验 比如说某些在线小说阅读网站,会加上相应的广告, ...

  5. 4.9:MapReduce之WordCount

    〇.概述 1.拓扑结构 2.目标 使用hadoop的MapReduce来实现计数. 一.启动服务 二.新建计算的文件 三.上传文件 四.分析文件 cd ~/bigdatahadoop jar Word ...

  6. 【CDH数仓】Day02:业务数仓搭建、Kerberos安全认证+Sentry权限管理、集群性能测试及资源管理、邮件报警、数据备份、节点添加删除、CDH的卸载

    五.业务数仓搭建 1.业务数据生成 建库建表gmall 需求:生成日期2019年2月10日数据.订单1000个.用户200个.商品sku300个.删除原始数据. CALL init_data('201 ...

  7. rate-limit 一款 java 开源渐进式分布式限流框架使用介绍

    项目简介 rate-limit 是一个为 java 设计的渐进式限流工具. 目的是为了深入学习和使用限流,后续将会持续迭代. 特性 渐进式实现 支持独立于 spring 使用 支持整合 spring ...

  8. python各种小知识

    一.三元表达式 1. 简化步骤1:代码简单且只有一行,可以直接在冒号后面编写 三元表达式: 数据值1+ if 条件+else 数据值2条件成立则使用数据值1,条件不成立则使用数据值2: 当结果是二选一 ...

  9. Nmap常用方法

    1.扫描单个目标地址  在Nmap后面直接添加目标地址即可扫描  nmap 目标地址  2.扫描多个目标地址  如果目标不在同一网段,或在同一网段但不连续且数量不多,可以使用该方法进行扫描  nmap ...

  10. 源码解读之TypeScript类型覆盖检测工具type-coverage

    因为团队内部开启了一个持续的前端代码质量改进计划,其中一个专项就是TS类型覆盖率,期间用到了type-coverage这个仓库,所以借这篇文章分享一下这个工具,并顺便从源码阅读的角度来分析一下该工具的 ...