此后,其他人回答了大多数问题,但我想指出一些特定的情况,其中特定的调度类型比其他类型更适合。 调度控制如何在线程之间分配循环迭代。 选择正确的时间表可能会对应用程序的速度产生重大影响。

schedule(runtime)调度表示以循环方式将迭代块静态映射到执行线程。 静态调度的好处在于,OpenMP运行时可以确保,如果您有两个具有相同迭代次数的单独循环,并使用静态调度以相同数量的线程执行它们,那么每个线程将获得完全相同的迭代范围( s)在两个平行区域中。 这在NUMA系统上非常重要:如果在第一个循环中触摸一些内存,它将驻留在执行线程所在的NUMA节点上。 然后,在第二个循环中,同一线程可以更快地访问相同的内存位置,因为它位于同一NUMA节点上。

假设有两个NUMA节点:节点0和节点1,例如 一个两个插槽的Intel Nehalem板,两个插槽均带有4核CPU。 然后,线程0、1、2和3将驻留在节点0上,线程4、5、6和7将驻留在节点1上:

| | core 0 | thread 0 |

| socket 0 | core 1 | thread 1 |

| NUMA node 0 | core 2 | thread 2 |

| | core 3 | thread 3 |

| | core 4 | thread 4 |

| socket 1 | core 5 | thread 5 |

| NUMA node 1 | core 6 | thread 6 |

| | core 7 | thread 7 |

每个内核都可以从每个NUMA节点访问内存,但是远程访问的速度比本地节点访问的速度慢(Intel的速度是1.5倍-1.9倍)。 您运行的是这样的:

char *a = (char *)malloc(8*4096);

#pragma omp parallel for schedule(static,1) num_threads(8)

for (int i = 0; i < 8; i++)

memset(&a[i*4096], 0, 4096);

在这种情况下,如果不使用大页面,则x86上Linux上一个内存页面的标准大小为4096字节。 此代码将整个32 KiB阵列schedule(runtime)归零。dynamic调用仅保留虚拟地址空间,但实际上并未“接触”物理内存(这是默认行为,除非使用其他版本的dynamic,例如将内存归零的版本)。 像guided一样)。 现在,此数组是连续的,但仅在虚拟内存中。 在物理内存中,一半将位于与套接字0相连的内存中,一半在与套接字1相连的内存中。之所以如此,是因为不同的部分被不同的线程置零,并且这些线程位于不同的内核上,并且有一种叫做“首次触摸”的东西。 NUMA策略,这意味着将内存页面分配在首先“接触”内存页面的线程所在的NUMA节点上。

| | core 0 | thread 0 | a[0] ... a[4095]

| socket 0 | core 1 | thread 1 | a[4096] ... a[8191]

| NUMA node 0 | core 2 | thread 2 | a[8192] ... a[12287]

| | core 3 | thread 3 | a[12288] ... a[16383]

| | core 4 | thread 4 | a[16384] ... a[20479]

| socket 1 | core 5 | thread 5 | a[20480] ... a[24575]

| NUMA node 1 | core 6 | thread 6 | a[24576] ... a[28671]

| | core 7 | thread 7 | a[28672] ... a[32768]

现在让我们运行另一个循环,如下所示:

#pragma omp parallel for schedule(static,1) num_threads(8)

for (i = 0; i < 8; i++)

memset(&a[i*4096], 1, 4096);

每个线程将访问已映射的物理内存,并且与第一个循环期间的线程到内存区域的映射相同。 这意味着线程将仅访问位于其本地内存块中的内存,这将是快速的。

现在想象第二个循环使用另一个调度方案:schedule(runtime)。这将把迭代空间“砍”成两个迭代的块,总共将有4个这样的块。 将会发生的是,我们将具有以下线程到内存的位置映射(通过迭代编号):

| | core 0 | thread 0 | a[0] ... a[8191]

| socket 0 | core 1 | thread 1 | a[8192] ... a[16383]

| NUMA node 0 | core 2 | thread 2 | a[16384] ... a[24575]

| | core 3 | thread 3 | a[24576] ... a[32768]

| | core 4 | thread 4 |

| socket 1 | core 5 | thread 5 |

| NUMA node 1 | core 6 | thread 6 |

| | core 7 | thread 7 |

这里发生两件事:

线程4至7保持空闲状态,并且一半的计算能力丢失;

线程2和3会访问非本地内存,这将花费它们大约两倍的时间来完成,在此期间线程0和1将保持空闲状态。

因此,使用静态调度的优点之一是它可以提高内存访问的局部性。 缺点是调度参数选择不当会破坏性能。

schedule(runtime)调度工作以“先到先得”为基础。 两次运行具有相同数量的线程可能(并且很可能会)产生完全不同的“迭代空间”->“线程”映射,因为一个映射可以轻松验证:

$ cat dyn.c

#include

#include

int main (void)

{

int i;

#pragma omp parallel num_threads(8)

{

#pragma omp for schedule(dynamic,1)

for (i = 0; i < 8; i++)

printf("[1] iter %0d, tid %0d\n", i, omp_get_thread_num());

#pragma omp for schedule(dynamic,1)

for (i = 0; i < 8; i++)

printf("[2] iter %0d, tid %0d\n", i, omp_get_thread_num());

}

return 0;

}

$ icc -openmp -o dyn.x dyn.c

$ OMP_NUM_THREADS=8 ./dyn.x | sort

[1] iter 0, tid 2

[1] iter 1, tid 0

[1] iter 2, tid 7

[1] iter 3, tid 3

[1] iter 4, tid 4

[1] iter 5, tid 1

[1] iter 6, tid 6

[1] iter 7, tid 5

[2] iter 0, tid 0

[2] iter 1, tid 2

[2] iter 2, tid 7

[2] iter 3, tid 3

[2] iter 4, tid 6

[2] iter 5, tid 1

[2] iter 6, tid 5

[2] iter 7, tid 4

(当使用schedule(runtime)时,观察到相同的行为)

如果使用dynamic调度运行了schedule(runtime)部分的示例代码,则保留原始位置的机会只有1/70(1.4%),发生远程访问的机会只有69/70(98.6%)。 这个事实经常被忽略,因此实现了次优的性能。

在schedule(runtime)和dynamic调度之间进行选择还有另一个原因-工作负载平衡。 如果每次迭代所花费的时间与平均时间相差很大,那么在静态情况下可能会出现很高的工作失衡。 例如,完成迭代的时间随迭代次数线性增长的情况。 如果在两个线程之间静态分配迭代空间,则第二个线程的工作量将是第一个线程的三倍,因此在2/3的计算时间内,第一个线程将处于空闲状态。 动态计划会带来一些额外的开销,但在这种情况下将导致更好的工作负载分配。 dynamic的一种特殊调度是guided,其中随着工作的进行,每个任务的迭代块越来越小。

由于预编译的代码可以在各种平台上运行,因此,如果最终用户可以控制调度,那就很好了。 这就是OpenMP提供特殊的schedule(runtime)子句的原因。 使用runtime进行调度时,该类型取自环境变量OMP_SCHEDULE的内容。这允许测试不同的调度类型而无需重新编译应用程序,并且还允许最终用户针对其平台进行微调。

mysql C openmp_c ++-OpenMP中的“静态”和“动态”时间表有什么区别?相关推荐

  1. 静态页面和动态页面中的静态和动态到底指的是什么

    今天讨论一个话题,这是在考研的专业课考试中的一个问题:静态页面和动态页面的联系和区别 ,这考的我就很猝不及防 ,这个方面虽然以前有了解 ,但是理解得并不深刻,网安方向涉及到的东西太多了,慢慢学习吧~首 ...

  2. java中的静态、动态代理模式以及Spring中的CgLib动态代理解读(面试必问)

    java中的静态.动态代理模式以及Spring中的CgLib动态代理解读(面试必问) 静态代理 动态代理 CgLib动态代理     基础知: 反射知识 代理(Proxy)是一种设计模式,提供了对目标 ...

  3. angr原理与实践(三)——Arbiter:一种弥合二进制程序漏洞发现中的静态和动态鸿沟

    转载请说明出处:信安科研人 please subscribe my official wechat :信安科研人 获取更多安全资讯 原文链接:sec22-vadayath.pdf (usenix.or ...

  4. fragment在activity中的静态和动态用法_使用Matlab修改压缩Gif动态图片制作微信表情...

    脚本之家 你与百万开发者在一起 作者:theOwlAndPussyCat/焦旭光 引言 电脑里存了很多有意思的Gif动态图片,闲暇想把这些动图全导入微信表情,可是这些动图很多大小超过了微信表情大小1M ...

  5. Eclipse中的静态和动态web项目

    在Eclipse中奖web项目分为了Dynamic Web Project和Static Web Project.那么这两种有什么区别呢?其实这里的Dynamic和Static是通过页面来区分的.创建 ...

  6. html的动态页面包含,在html页面中包含静态或动态页面方法

    1.可以用 download 行为下载后再用 innerHTML 特性显示出来. 具体步骤: style="behavior:url(#default#download)"> ...

  7. 静态和动态include

    今天突然意识到野猪对静态和动态的include的区别理解有点模糊,现在总结一下: 动态include:<jsp: include page="include.jsp" ful ...

  8. 如何在nginx中缓存静态文件

    这篇教程说明你应该怎样配置 nginx.设置 HTTP 头部过期时间,用 Cache-Control 中的 max-age 标记为静态文件(比如图片. CSS 和 Javascript 文件)设置一个 ...

  9. mysql 在大型应用中的架构演变

    文正整理自:http://www.csdn.net/article/2014-06-10/2820160 可扩展性 架构的可扩展性往往和并发是息息相关,没有并发的增长,也就没有必要做高可扩展性的架构, ...

最新文章

  1. SQL拆分实现与注意事项
  2. 云栖Android精华文章合集
  3. 开箱即用Bumblebee独立部署搭建webapi网关详解
  4. [Python] 制作启动uiautomator2 的web版 uiautomatorviewer2 批处理启动
  5. numpy ndarray可用的常规函数
  6. 公司git服务器记录
  7. [数字图像处理·冈萨雷斯 ] 图像文件格式
  8. asp.net/net/c#电子购物商城系统-成品
  9. 传送带计数器c语言程序,脉搏计数器的程序(用C语言编写程序)
  10. PS去掉图片上的文字的6种基本方法
  11. php除数不能为零,0为什么不能做除数(为什么0不能作为除数)
  12. 如何撰写搜索引擎广告创意
  13. 人工智能时代,数据标注产业将迎来黄金时期?丨曼孚科技
  14. 安卓虚拟机_VMOS虚拟大师-独立的安卓虚拟机系统(已ROOT)「安卓」
  15. SCORM标准及支持SCORM标准学习平台的设计
  16. linux下iconv()函数的用法
  17. 汪源:数据分析热词迭出,“三个统一”值得关注
  18. 关于error: The following untracked working tree files would be overwritten by checkout的解决方案
  19. K12526 找双亲和孩子
  20. MindManager思维导图制作教程案例

热门文章

  1. 2020年支付宝红包、支付宝转账、支付宝动态码原理
  2. 留听阁--多线程系列之锁(十四)
  3. 网易云项目 【前端】【黑马】
  4. 回顾理解Triplet-loss
  5. 你的滤镜大师 Polarr5.11.4泼辣修图2023免费版
  6. Qihoo360/wayne 部署教程
  7. 51nod 1264 线段相交(跨立实验)
  8. PRISM(棱镜)监听计划的善与恶
  9. 信息与通信的数学基础——第二章 解析函数
  10. 「电子乐之父」的遗作还没完成?由人工智能接手?