深入理解并行编程-分割和同步设计(一)

原文链接     作者:paul    译者:谢宝友,鲁阳,陈渝

在商用计算机中,多核系统已经越来越常见了,本章将描述如何设计能更好利用多核优势的软件。我们将介绍一些习语,或者叫“设计模式”,来帮助您权衡性能、可扩展性和响应时间。在上一章我们说过,您在编写并行软件时最重要的考虑是如何进行分割。正确的分割问题能够让解决办法简单、可扩展并且高性能,而不恰当的分割问题则会产生缓慢且复杂的解决方案。

1.1分割练习

本节用两个例子(哲学家就餐问题和双端队列问题)作为练习,来说明分割的价值。

1.1.1哲学家就餐问题

图1.1:哲学家就餐问题

图1.1是经典的哲学家就餐问题的示意图[Dij71]。问题中的五个哲学家一天无所事事,不是思考就是吃一种需要用两把叉子才能吃下的“滑溜溜的”意大利面。每个哲学家只能用和他左手和右手旁的叉子,一旦哲学家拿起了叉子,那么不吃到心满意足是不会放下的。

我们的目标是构建一种算法来——有点儿文学化——阻止饥饿。一种饥饿的场景是所有哲学家都同时拿起了左手边的叉子。因为他们在吃饱前不会放下叉子,并且他们还需要第二把叉子才能开始就餐,所以所有哲学家都会挨饿。

图1.2:哲学家就餐问题,教科书解法

Dijkstra的解决方法是使用一个全局信号量,假设通信延迟忽略不计的话,这种方法非常完美,可是在随后的几十年里这种假设变得无效了。因此,最近的解决办法是像图5.2一样为叉子编号。每个哲学家都先拿他盘子周围编号最小的叉子,然后再拿编号最高的叉子。这样坐在图中最上方的哲学家会先拿起左手边的叉子,然后是右边的叉子,而其他的哲学家则先拿起右手边的叉子。因为有两个哲学家试着去拿叉子1,而只有一位会成功,所以只有4位哲学家抢5把叉子。至少这4位中的一位肯定能拿到两把叉子,这样就能开始就餐了。

这种为资源编号并按照编号顺序获取资源的通用技术在防止死锁上经常使用。但是很容易就能想象出一个事件序列来产生这种结果:虽然大家都在挨饿,但是一次只有一名哲学家就餐。

1. P2拿起叉子1,阻止P1拿起叉子。

2. P3拿起叉子2。

3. P4拿起叉子3。

4. P5拿起叉子4。

5. P5拿起叉子5,开始就餐。

6. P5放下叉子4和5。

7. P4拿起叉子4,开始就餐。

请在进一步阅读之前,思考哲学家就餐问题的分割方法。

图1.3:哲学家就餐问题,分割解法

图1.3是一种方法,里面只有4位而不是5位哲学家,这样可以更好的说明分割技术。最上方和最右边的哲学家合用一对叉子,而最下方和最左边的哲学家合用一对叉子。如果所有哲学家同时感觉饿了,至少有两位能同时就餐。另外如图中所示,现在叉子可以捆绑成一对儿,这样可以同时拿起或者放下,这就简化了获取和释放算法。

小问题1.1: 哲学家就餐问题还有更好的解法吗?

这是“水平并行化”[Inm85]的一个例子,或者叫“数据并行化”,这么叫是因为哲学家之间没有依赖关系。在数据处理型的系统中,“数据并行化”是指一种类型的数据只会穿过一系列同类型软件组件其中的一个。

小问题1.2:那么“水平并行化”里的“水平”是什么意思呢?

时间: 2025-01-06 16:05:36

深入理解并行编程-分割和同步设计(一)的相关文章

深入理解并行编程-分割和同步设计(二)

双端队列是一种元素可以从两端插入或删除的数据结构[Knu73].据说实现一种基于锁的允许在双端队列的两端进行并发操作的方法非常困难[Gro07].本节将展示一种分割设计策略,能实现合理且简单的解决方案,请看下面的小节中的三种通用方法. 1.1. 右手锁和左手锁 图1.1:带有左手锁和右手锁的双端队列 右手锁和左手锁是一种看起来很直接的办法,为左手端的入列操作加一个左手锁,为右手端的出列操 作加一个右手锁,如图1.1所示.但是,这种办法的问题是当队列中的元素不足四个时,两个锁的范围会发生重叠.这种

深入理解并行编程-分割和同步设计(三)

设计准则 上面的章节中给出了三个并行编程的目标:性能.生产率和通用性.但是还需要更详细的设计准则来真正的指导真实世界中的设计,这就是本节将解决的任务.在真实世界中,这些准则经常在某种程度上冲突,这需要设计者小心的权衡得失. 这些准则可以被认为是设计中的阻力,对这些阻力进行恰当权衡,这就称为"设计模式"[Ale79],[GHJV95]. 基于三个并行编程目标的设计准则是加速.竞争.开销.读写比率和复杂性. 加速:性能增加是花费如此多时间和精力进行并行化的主要原因.加速的定义是运行程序的顺

深入理解并行编程-分割和同步设计(五)

原文链接    作者:paul    译者:谢宝友,鲁阳,陈渝 并行快速路径 细粒度的设计一般要比粗粒度的设计复杂.在许多情况,绝大部分开销只由一小部分代码产生[Knu73].所以为什么不把精力放在这一小块代码上. 这就是并行快速路径设计模式背后的想法,尽可能地并行化常见情况下的代码路径,同时不产生并行化整个算法所带来的复杂性.您必须理解这一点,不只算法需要并行化,算法所属的工作负载也要并行化.构建这种并行快速路径,需要极大的创造性和设计上的努力. 图1.1并行快速路径的设计模式 并行快速路径结

深入理解并行编程-分割和同步设计(四)

原文链接    作者:paul    译者:谢宝友,鲁阳,陈渝 图1.1:设计模式与锁粒度 图1.1是不同程度同步粒度的图形表示.每一种同步粒度都用一节内容来描述.下面几节主要关注锁,不过其他几种同步方式也有类似的粒度问题. 1.1. 串行程序 图1.2:Intel处理器的MIPS/时钟频率变化趋势 如果程序在单处理器上运行的足够快,并且不与其他进程.线程或者中断处理程序发生交互,那么您可以将代码中所有的同步原语删掉,远离它们所带来的开销和复杂性.好多年前曾有人争论摩尔定律最终会让所有程序都变得

《深入理解并行编程》中文版

原文的下载地址:http://kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.html 中文版下载地址:深入理解并行编程V1.0 (4.1M) 本书是linux内核大牛paul的力作,和鲁阳同学一起,花了两个月时间进行翻译. 目前没有翻译问答部分,主要是时间不够,也担心不能将这部分翻译准确. 对内核深度发烧的同学可以看看. 本书目录 1. 简介----------------------------------- 14 1

深入理解JavaScript编程中的同步与异步机制

  这篇文章主要介绍了深入理解JavaScript编程中的同步与异步机制,不仅仅是AJAX已经深入到了各个角落,Node.js的火爆也让JS的异步编程格外引人注目,需要的朋友可以参考下 JavaScript的优势之一是其如何处理异步代码.异步代码会被放入一个事件队列,等到所有其他代码执行后才进行,而不会阻塞线程.然而,对于初学者来说,书写异步代码可能会比较困难.而在这篇文章里,我将会消除你可能会有的任何困惑. 理解异步代码 JavaScript最基础的异步函数是setTimeout和setInt

深入理解并行编程-锁

锁 在过去几十年并发研究领域的出版物中,锁总是扮演着坏人的角色,锁背负的指控包括引起死锁.锁封护(luyang注:lock convoying,多个同优先级的线程重复竞争同一把锁,此时大量虽然被唤醒而得不到锁的线程被迫进行调度切换,这种频繁的调度切换相当影响系统性能).饥饿.不公平.data races以及其他许多并发带来的罪孽.有趣的是,在共享内存并行软件中真正承担重担的是--你猜对了--锁. 图1.1:锁:坏人还是懒汉? 这种截然不同的看法源于下面几个原因: 1. 很多因锁产生的问题大都有实

C#并行编程-线程同步原语

原文:C#并行编程-线程同步原语 菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 背景 有时候必须访问变量.实例.方法.属性或者结构体,而这些并没有准备好用于并发访问,或者有时候需要执行部分代码,而这些代码必须单独运行,这是不得不通过将任务分解的方式让它们独立运行. 当任务和线程要访问共享的数据和资源的时候,您必须添加显示的同步,或者使用原子操作或锁. 之前的.NET Framework提供了昂贵的锁机制以及遗留的多线程模型,新的数据结构允许细粒度的并发和并行化,

模式转变:并行编程方面的设计注意事项

本文以 Visual Studio 工具的预发布版为基础.文中的所有信息均有可能发生变更. 本文将介绍以下内容: 并行计算 并发编程 性能提高 本文使用了以下技术: 多线程 从 1986到 2002 年,微处理器的性能每年提高了 52%.这一惊人的技术进步源自晶体管成本依据摩尔 法则不断地缩减,以及处理器厂商在工程方面的出色表现.微软的研究员 Jim Larus 将上述两种因素的 组合称为"摩尔红利",他解释了这一红利如何造就了现代软件业并使计算机得以广泛普及(请参阅 go.micro