[荐]平行處理概觀
这篇发表于 ZDNet tw 站上的《Programming for Parallelism》,是 Intel 赞助的内容,虽说有广告嫌疑,而且通篇都是读起来让人亲切之余还有些吃力的 “正体中文” (幸好初中读过一本繁体竖排版的《封神榜》打了点基础),而且满眼尽是晕乎乎的台式技术术语(幸好大学读过一本繁体横排版的《组合语言程式设计》,注: “组合语言” == “汇编” ,又打了点基础)。但是内容确实没得说,相当之有料!清晰简明,三两句就把原来觉得神秘兮兮的东西讲明白了——保持了海峡对岸兄弟们技术文章的一贯高水准。我是一口气读完了,大呼过瘾,如果你也能够耐着性子读完,肯定会很有收获。
“并行编程 (Parallel Programming)” 和 “并发编程 (Concurrent Programming)” 是一码事么?如果不是,那它们之间到底有什么区别呢?
这个问题,在看完这篇文章之前,我还真回答不上来。因为,确实不了解,我也知道 OpenMP、MPI 这些被当作是并行编程的 “必杀密技” 必须要去了解一下,但我一听见这些强烈 C 风格的东西就私底下直犯触,老是没法鼓起勇气(和兴趣)去研究它们。这下好了,这篇文章以极其精炼的语言和高度抽象的视角(主要是这个,老实说,我对细节还真的不是很感兴趣),把这些 “难点” 一下子都讲明白了。
以我不求甚解的标准来看,“并行编程 (Parallel Programming)” 在抽象方法上的:优先考虑并行、大量制造任务、限制锁的使用。以及基础设施中的:基本的可并行算法和数据结构、互斥、计时、任务计划、MPI 的消息传递机制,等等。其实和强调 “并发编程 (Concurrent Programming)” 的 Erlang 在概念上基本 “略同” 。所不同的是,这些设施大多都是为 C 系语言准备的,号召广大 C 程序员向并发 “靠拢” ,而 Erlang 则是彻底决裂,自己搞了一个语言出来(把这些具体的实现细节隐藏在虚拟机里面)。
又长知识了,感谢方块兄的投递,感谢 INET6 的推荐。
呵呵,我倒是非常喜欢看“正体中文”,Firefox 的默认字体用的是“细明体”,买书也是繁体竖排的优先,只可惜这样的书很少,唉!
并行和并发的主要区别是其目标不同。并行是为了一个任务计算的更快,并发是为了多个任务响应的更快。底层技术无非都是手段。呵呵
确切的说:
Concurrent vs parallel
• Concurrency
Do many unrelated things “at once”
Goals are responsiveness and multitasking
• Parallelism
Get a faster answer with multiple CPUs
这个video不错:
Concurrent and Multicore Haskell
http://www.bayfp.org/blog/2008/05/17/video-and-slides-from-bryan-o%e2%80%99sullivans-talk/
我觉得这两个词是用在不同场合的,关系有点微妙
我认为“并发”只是强调同时有很多(同类)事情在一起处理
而“并行”则强调是不同(也可以同类)事情在一些处理单元内各做各的
我记得以前有个人举了这个例子,并发是一个人吃三个馒头,而并行是三个人吃三个馒头
另外似乎两者的侧重的层面不同
现在讲到“并行编程”通常是底层的问题,CPU层面上的,和多核计算有关
而讲到“并发”往往指应用层面上的,比如大量的连接处理。
一个高并发的程序,不一定就实现了并行处理,而一个并行处理的也未必就高并发。
其实一个双核的系统上,同一个程序开两个进程就并行了
Erlang将来会在虚拟机底层为我们实现并行处理
@dsun 那个视频里面提到,有 “任务并行” 和 “资料并行” 这样两个概念,我个人的感觉是 Parallel 比较偏 “资料并行” 一点(按照数据划分任务,各任务的运算逻辑相同),而 Concurrent 比较偏 “任务并行” 一点(比如,按处理流程划分任务,类似 pipe ,各任务的运算逻辑通常不同)。
也不知道这种印象是从哪里来的,如果有误,请纠正我。
“并发是一个人吃三个馒头,而并行是三个人吃三个馒头”,这个例子实在是不妥,呵呵。
并发和并行其实是含义非常明确的两个词,之所以变得“微妙”,就是这些比喻,举例给闹的 :)
并行就是为了通过多个处理器并行计算,让一个任务计算的更快。并发就是为了让多个无关的任务同时进行,这里的重点就是根据问题领域和场景区分出哪些是属于一个任务的计算,而哪些则根本就是不同的任务,以及设计的目标是什么。这是谈论并行还是并发的前提。
至于层面则完全取决于看待、分解问题的角度和方法,Map-Reduce是个并行计算的框架,完全可以把它看成是应用层面的。而为了达成高层的并行计算,也完全可以在底层采用并发手段(比如:IO之间以及IO和实际计算之间可以并发)。
任务并行和数据并行则都是手段,要看它们服务的目标是什么。
比如说,要盖N座房子,如果以最快盖完为目标的话,则这肯定是个并行计算问题,你可以采用数据并行的手段,也可以采用任务并行的手段。我们可以让一部分人专门负责砌墙,一部分人专门负责盖屋顶,一部分人专门负责门窗。。。。,在这些工人看来他们是任务并发的,但是对于工程的总体目标来说只是达成总体并行的一种手段。
还以盖N座房子为例,我们可以让所有工人同时先砌墙,然后同时盖屋顶。。。,这个就有些数据并行的味道,不过和上面的那个任务并行一样,对于总体目标来说都是手段。
因此在明确场景和目标的前提下,并行和并发其实是非常明确的概念。
OpenMP和MPI在烟酒僧阶段用过,OpenMP理念上类似.net 4.0里面的并行包。
MPI是对等并行,消息是广播式的。
erlang的process更自由,可以通讯,切非对等。
是否可以认为:“并行”的问题域是研究如何让“一个(相同的)任务”在“多个CPU”上跑得更快,换句话说,它只针对“一个(相同的)任务”,并且和“多个CPU”密切相关;而“并发”的问题域是研究如何让“多个任务”同时运行,它不强调被同时运行的多个任务是“一个(相同的)任务”,也和CPU的数量无关。
这里我不清楚的一点是,“并发”在概念上是否强调了这些被同时运行的多个任务,一定要是彼此不相同的呢?那么,如果某种情况下,这些被同时运行的多个任务其实就是“一个(相同的)任务”,此时,“并发”和“并行”之间的区别是否就只有“多个CPU”这么一条了呢?
更进一步,如果把“任务”概念扩大化,将“并发”系统在一个单CPU上的“运行实例”当作是“一个(相同的)任务”,通过“并行”手段,让它能在“多个CPU”上得到加速(貌似Erlang的SMP就是如此)。从这个意义上讲,我认为这两个概念在问题领域上的差异是:“并行”更强调“多核”,比如“显卡/CUDA/FPGA”的并行之类,似乎更接近硬件;而“并发”则更“抽象”一些,相比“并行”,要离硬件远一些(离软件更近一些)。
我们是否可以说,并发和并行的研究领域不同,并发要比并行的抽象层次更高(上面的例子实际上是用“并行”技术支持了“并发”系统),在具体应用中,也更加灵活呢(在有“并行”支持的“并发”系统上,也可以通过将相同的任务分发出去,来达到与“并行”相同的加速效果)?
我不确定上面的理解是否正确,让我们把这个问题继续挖下去,搞清楚。
@jackyz “”"这里我不清楚的一点是,“并发”在概念上是否强调了这些被同时运行的多个任务,一定要是彼此不相同的呢?那么,如果某种情况下,这些被同时运行的多个任务其实就是“一个(相同的)任务”,此时,“并发”和“并行”之间的区别是否就只有“多个CPU”这么一条了呢?”"”
关键的一点应该是,这些同时运行的任务是为了更快地完成同一个计算的目标呢?还是仅仅是为了能够同时运行以提升系统的响应能力呢?至于任务本身是不是一定相同倒不是关键问题,比如:数据并行中,任务是相同的,但是任务并行中,任务就不相同。
“”"更进一步,如果把“任务”概念扩大化,将“并发”系统在一个单CPU上的“运行实例”当作是“一个(相同的)任务”,通过“并行”手段,让它能在“多个CPU”上得到加速(貌似Erlang的SMP就是如此)。”"”
你这样做的目的是啥。如果一开始设计系统时,就考虑到系统可以在多个CPU上得到加速时,其实这就是一个并行设计的问题了,Map-Reduce框架就是这样一个例子。另外,如果我们开发一个Web server,希望能够同时处理更多的用户请求,在这个场景下,处理用户请求的任务看似也相同,但是这些任务并不是为了一个更大的计算目标服务,某些任务失败仅仅会影响系统的服务质量。此时,我们可以使用多核,但是目标是为了提升系统的可用性和响应能力。这就是并发设计了。
“”"我们是否可以说,并发和并行的研究领域不同,并发要比并行的抽象层次更高(上面的例子实际上是用“并行”技术支持了“并发”系统),在具体应用中,也更加灵活呢(在有“并行”支持的“并发”系统上,也可以通过将相同的任务分发出去,来达到与“并行”相同的加速效果)?”"”
并行和并发都是编程范型,它们需要的底层基础设施支持有很大的重叠。它们本身我觉得并没有层次高低之分。
@dsun :“并行和并发都是编程范型,而且它们所需要的底层基础支持设施又有很大的重叠”——为什么两种彼此不同的编程范型,它们的基础设施会出现这样的重叠?我们(在大部分的应用中)是否同时需要这两种编程范型?其中的一种范型是否(在部分情况下)可以“包装”另外一种?
这样做的目的是试图(至少是在一部分场景下)简化编程范型(用并发来包装并行)。当然,这必须要建立在这种“简化”本身是可行的基础之上。Erlang 承诺使用面向“并行”编程写出来的程序,在多核的系统中能“自动”的获得多核CPU上的“并发”加速。这是一个很漂亮的“简化”。那么这是否就意味着“底层的并行问题已经被(部分的)包装在并发技术之中”了呢?
当然,一些诸如大规模科学运算一样的应用,是不适用这种简化的。这需要另当别论,但对于“常规”应用而言,是否上面这样的简化就已经足够了呢。毕竟 OpenMP/MPI/CUDA/OpenCL/… 这些“并行”设施和技术都需要相当的精力去啃(尤其是 CUDA 和 OpenCL,好像都是很专注于“数据并行”风格的东西,确实适合数据并行,但并非所有的项目都能通用)。
如果这样说是成立的,那么,比较“接近应用”的程序员们就只需要用“并发”来解决问题,不需要了解太多关于“并行”的背景知识,就能很好的应付大部分的状况。因为“并行”问题已经被“并发”包装起来,被更“接近硬件”的底层程序员们解决掉了。
@jackyz
不能这么理解。呵呵
James Reinders 在 Programming for Parallelism 中说:
Joe Armstrong 在 Joe Thesis CN 中的表述是:
说法很有一些相似……。
Ulf Wiger 在 Erlang Multicore 中则说:
诚然 SMP 并不等于 Parallel (或者说只是后者的一部分),而且“被并发包装起来的并行”也不能涵盖全部情况(…but sometimes you must)。但,除此之外,“SMP should be transparent to the programmer in much the same way as distribution is”。{对于(Erlang)程序员来说,和分布式(distribution)一样,对称多处理(SMP)也是透明的}。
这是一个极具诱惑力的范型简化(并发范型包装并行范型)。如果不能这么理解,那又是为什么?(比如,具一个反例?)
@jackyz 我说的“并行和并发在底层支撑机制上有很大重叠”意思是指在当前的通用计算平台和操作系统平台上,这两种范型在实现时基本上都是采用多线程、多进程等手段。我说不能这么理解,是指“并发包装起来的并行,并行包装起来的并发”这种说法不妥。之所以会有这样的理解,我觉得可能是把并发和多线(进)程等起来的缘故吧,不知道是不是这样?在有些上下文中把这两者等同并没有问题,但是在探讨并行和并发范型时,这种等同会造成问题。
固然,现在有很多库对当前通用计算平台上这些primitive的手段进行了包装,但绝不是为了简化编程范型,而只是为了让程序员能够更关注于范型本身,而不被当前实现手段的繁琐分散了注意。这两种范型有清晰地目标。
你引用的几段话,也主要是并行系统设计的一些原则、手段以及好的支撑应该做的事情。
虽然Joe Armstrong现在到处宣扬Erlang在并行编程方面的优势,并把并行/并发编程作为Erlang最重要的特征进行推介,但这只是Joe在当前的形势下的一种推销Erlang的手段。从JoeArmstrong的一些严肃的访谈和论文中,可以看出,Erlang最核心的特征是软件容错,Erlang非常优雅、深刻地解决了容错这个问题,从而像并发/并行/分布这些问题都很容易地被同样优雅地解决了。其他语言无论做出怎样的更改、扩展、创新,只要没有正确地解决容错的问题,在这些方面都无法和Erlang相提并论,因为:you can’t fix stupid. 跑题了,呵呵。
@dsun 了解了,有道理。