你今天推荐什么样的并行编程模型来利用明天的众核处理器?

如果你今天从头开始编写一个新的应用程序,并且希望它能够扩展到你明天可以使用的所有内核,那么你会select什么样的并行编程模型/系统/语言/库? 为什么?

我对这些轴上的答案特别感兴趣:

  1. 程序员的生产力/易用性(人们能成功地使用它吗?)
  2. 目标应用领域(它不擅长什么问题?)
  3. 并发风格(是否支持任务,pipe道,数据并行,消息…?)
  4. 可维护性/面向未来(20年后还会有人使用吗?)
  5. 性能(它如何扩展到什么样的硬件?)

我正在刻意模糊应用程序的性质,以期得到对各种应用程序有用的良好的一般答案。

多核编程实际上可能需要多个范例。 一些目前的竞争者是:

  1. MapReduce 。 这可以很好地解决问题,可以很容易地分解成平行块。
  2. 嵌套数据并行 。 这与MapReduce类似,但实际上支持recursion分解问题,即使recursion块的大小不规则。 寻找NDP是在大规模并行但有限硬件(如GPU)上运行的纯function语言中的一大胜利。
  3. 软件事务内存 。 如果你需要传统的线程,STM使他们可以忍受。 您在关键部分支付了50%的性能,但是您可以无忧地将复杂的lockingscheme扩展到100个处理器。 但是,这不适用于分布式系统。
  4. 具有消息的并行对象线程 。 这个巧妙的模型被Erlang使用。 每个“对象”成为一个轻量级的线程,对象通过asynchronous消息和模式匹配进行通信。 这基本上是真正的并行OO。 这在几个真实世界的应用程序中已经很好地成功了,并且对于不可靠的分布式系统来说它是非常好的。

这些范例中的一些可以为您提供最大的性能,但只有在问题干净地分解时才有效。 其他人牺牲了一些性能,但允许更多种algorithm。 我怀疑上述的一些组合最终将成为一个标准的工具包。

我真正喜欢的两个解决scheme是连接微积分 ( JoCaml , Polyphonic C# , Cω )和演员模型 ( Erlang , Scala , E , Io )。

我对Software Transactional Memory并没有什么特别的印象。 只是觉得只有在那里,线程才能更长时间的坚持下去,即使它们应该在几十年前就已经死了。 但是,它有三大优势:

  1. 人们理解数据库中的交易
  2. 已经讨论过交易式RAM硬件
  3. 尽pipe我们都希望他们走了,但是在接下来的几十年里,线程可能会成为主stream的并发模型,可能会是这样。 STM可以显着减轻疼痛。

mapreduce / hadoop范例是有用的和相关的。 特别是对于习惯perl这样的语言的人来说,映射数组并对每个元素进行一些操作的想法应该是非常stream畅和自然的,而mapreduce / hadoop只是把它带到下一个阶段,并且说没有理由数组中的每个元素都需要在同一台机器上进行处理。

从某种意义上来说,这是更多的战斗testing,因为谷歌正在使用mapreduce和许多人一直在使用hadoop,并表明它适用于通过networking跨多台机器扩展应用程序。 而且,如果您可以跨networking扩展多台计算机,则可以在一台计算机上扩展多个内核。

对于.NET应用程序,我select“ .NET并行扩展(PLINQ) ”,它非常易于使用,并允许我在几分钟内并行处理现有的代码。

  1. 学习很简单
  2. 用于对大型数组对象执行复杂的操作,所以我不能评论其他应用程序
  3. 支持任务和pipe道
  4. 应该在未来几年得到支持,但是谁知道呢?
  5. CTP版本有一些性能问题,但已经看起来非常有前景。

单声道可能会得到 PLINQ的支持,所以它也可能是一个跨平台的解决scheme。

对于繁重的计算等,像Haskell这样的纯函数式语言很容易并行编程,而不需要编程人员的努力。 除了学习Haskell,就是。

但是,我不认为这是(近)未来的方式,只是因为太多的程序员太习惯于命令式的编程范式。

kamaelia是一个用于构build具有大量通信进程的应用程序的python框架

Kamaelia Kamaelia – 并发变得有用,有趣

在Kamaelia中,您可以从简单的组件构build系统,这些组件可以相互交stream 。 这加速了开发,大量的帮助维护,也意味着你build立自然并发的软件 。 它的目的是任何开发人员,包括新手访问。 这也使它很有趣:)

什么样的系统? networking服务器,客户端,桌面应用程序,基于pygame的游戏,转码系统和pipe道,数字电视系统,垃圾邮件根除者,教学工具,以及相当多的:)

另请参阅多核心和并发问题 – 语言,图书馆和开发技术

我打赌通过承诺交stream事件循环,就像在Twisted , E , AmbientTalk等系统中实现的那样。 它们保留了使用与非并行/并行应用程序相同的执行模型假设编写代码的能力,但可扩展到分布式和并行系统。 (这就是为什么我在Ecru上工作)

看看Erlang 。 谷歌为它,并观看各种演示和video。 我尊重的许多程序员和架构师都非常喜欢它的可扩展性。 我们正在使用它,我工作相当繁重

如前所述,纯粹的function语言本质上是可并行的。 然而,命令式语言对于许多人来说更为直观,而且我们深深扎根于命令式的遗留代码中。 根本的问题是,纯粹的function语言明确地expression副作用,而副作用则按照语句的顺序隐含地expression在命令式语言中。

我相信用声明方式expression副作用(例如,在面向对象的框架中)的技术将允许编译器将命令式语句分解为它们的函数关系。 这应该允许代码自动并行化,就像纯函数代码一样。

当然,就像今天仍然希望用汇编语言编写某些性能关键的代码一样,明天仍然有必要编写关键性能明确的并行代码。 但是,我所概述的技术应该可以帮助自动利用许多核心架构,而且开发人员只需花费很less的精力。

我很惊讶没有人build议MPI(消息传递接口)。 虽然devise用于分布式存储器,但具有基本且频繁的全局耦合(解决具有数十亿未知数的线性和非线性方程)的MPI程序已被certificate可扩展至200k核心。

Qt并发提供了一个实现多核的MapReduce,这真的很容易使用。 这是multiOS。

如果您的问题域允许尝试考虑一个无共享模式。 在进程和线程之间共享的越less,devise复杂的并发模型就越less。

另请参阅多核和并发 – 语言,图书馆和开发技术

这个问题似乎继续出现不同的措辞 – 也许有不同的选民StackOverflow内。 基于stream程的编程 (FBP)是一个已经存在了30多年的概念/方法,目前正用于处理大多数加拿大银行的批处理。 它在Java和C#中具有基于线程的实现,虽然早期的实现是基于光纤的(C ++和大型汇编程序 – 银行使用的)。 利用多核的问题的大多数方法涉及尝试采用传统的单线程程序并找出哪些部分可以并行运行。 FBP采取了不同的方法:应用程序从一开始就被devise为asynchronous运行的多个“黑盒子”组件(想象一下制造assembly线)。 由于组件之间的接口是数据stream,因此FBP本质上是独立于语言的,因此支持混合语言应用程序和特定于领域的语言。 出于同样的原因,副作用最小化。 它也可以被描述成一个“无分身”的模型,和一个MOM(面向消息的中间件)。 MapReduce似乎是FBP的特例。 FBP与Erlang的区别主要在于Erlang在很多短命线程中运行,所以绿线程更适合,而FBP则使用更less的线程(通常为几十到几百)。 对于已经使用了30多年的批量networking ,请参见批量networking的一部分 。 对于交互式应用程序的高级devise ,请参阅Brokerage应用程序高级devise 。 FBP被发现导致更多可维护的应用程序,并且改善了经过时间 – 即使在单核心机器上!

具有多个工作者系统的工作队列(不确定正确的术语 – 消息队列?)

为什么?

主要是因为这是一个荒唐简单的概念。 你有一个需要处理的东西的列表,然后是一堆获取作业并处理它们的进程。

另外,与Haskell或Erlang的原因不同,Haskell或Erlang是如此并发/并行(?),它完全是语言不可知的 – 你可以使用C,Python或任何其他语言(甚至使用shell脚本)轻易地实现这样一个系统,而我怀疑bash会很快获得软件事务内存或join微积分。

我非常喜欢Clojureselect的模型。 Clojure使用不可变数据结构和软件事务内存的组合。

不可变的数据结构是永远不会改变的。 结构的新版本可以用修改过的数据创build,但是如果你有一个“指向”数据结构的指针,它将永远不会从你的下面改变。 这很好,因为您可以访问数据而不用担心并发问题。

软件事务内存在这些答案中的其他地方被讨论过,但是可以说它是一个机制,多个线程都可以对某些数据起作用,如果它们发生冲突,其中一个线程将被回滚以再次尝试。 当存在碰撞风险时,这允许更快的速度,但不太可能。

有一个作者Rich Hickey的video进入了更多的细节。

一个有价值的path可能是OpenCL ,它提供了一种在异构计算资源之间分配特定types的计算负载的方法,IE相同的代码将运行在多核CPU上,也可以运行在商品GPU上。 ATI最近刚刚发布了这样一个工具链 。 NVidia的CUDA工具链是相似的,虽然有些更受限制。 Nvidia似乎也有一个OpenCL sdk 正在开发中

应该指出的是,在工作负载不具有数据并行性的情况下,这可能无济于事,例如,对于典型的事务处理来说,这不会有多大帮助。 OpenCL主要面向math密集型计算,如科学/工程模拟或财务build模。

如果你今天从头开始编写一个新的应用程序,并且希望它能够扩展到你明天可以使用的所有内核,那么你会select什么样的并行编程模型/系统/语言/库?

也许今天最广泛使用的是Cilk风格的任务队列(现在在.NET 4中可用)。 对于可以使用分而治之的问题,子任务具有可预测的复杂性(例如并行mapreduce函数参数的复杂度以及像quicksort这样的algorithm的数组),并且涵盖许多实际问题,这些问题是非常好的。

而且,这只适用于像今天的多核共享内存架构。 虽然我不相信这个基础架构会很快消失,但我确实相信它必须与分布式并行相结合。 这可以是多核CPU上的多核群集的forms,消息在多核之间传递,或者以核心层次的forms,在它们之间具有可预测的通信时间。 这些将需要大不相同的编程模型来获得最大的效率,我不太了解它们。

我们一直在使用PARLANSE ,这是一种并行编程语言,在过去的十年里,它具有明确的并行部分次序指定,实现了一个可扩展的程序分析和转换系统( DMS Software Reengineering Toolkit ),它主要做符号而不是数字计算。 PARLANSE是一种具有传统标量数据types字符,整数,浮点,dynamic数据typesstring和数组,复合数据types结构和联合以及词汇范围函数的编译的类C语言。 虽然大部分的语言是香草(算术expression式在操作数,if-then-else语句,循环,函数调用),但并行性不是。 并行性通过在代码块(例如,之前的b之前的c,之前的c,d之前)定义“先于”关系来expression

 (|; a (... a's computation) (<< a) b ( ... b's computation ... ) (<< a) c ( ....c's computation ...) (>> c) d ( ... d's computation...) )|; 

<<和>>操作符指的是“及时订货”。 PARLANSE编译器可以看到这些并行计算,并预先分配计算晶粒a,b,c,d所需的所有结构,并生成自定义代码以启动/停止每个结构,从而最小化启动和停止这些并行晶粒的开销。

看到这个环节, 并行迭代深化search最佳解决scheme15拼图 ,这是8拼图的4×4大兄弟。 它只使用潜在的并行作为一个并行结构(|| abcd) ,它表示在计算上没有偏序约束,但是它也使用推测,并asynchronous地中止任务,找不到解。 在很less的代码中有很多想法。

PARLANSE在多核PC上运行。 一个大的PARLANSE程序(我们已经build立了100多万行+以上)将有数以千计的这些部分订单,其中一些调用包含其他function。 到目前为止,我们已经有了多达8个CPU的良好结果,并有16个适度的回报,我们仍在调整系统。 (我们认为当前PC上有大量内核的真正问题是内存带宽:16个内核冲击内存子系统会产生巨大的带宽需求)。

大多数其他语言不公开并行性,所以很难find,运行时系统通过使用通用调度原语为调度计算粒度付出了高昂的代价。 我们认为这是由于Amhdahl定律造成的灾难或至less是糟糕的performance:如果计划粮食的机器指令数量大于工作量,那么效率就不高。 OTOH,如果你坚持计算谷物和许多机器指令来保持调度成本相对较低,你不能find独立的计算谷物,所以你没有任何有用的并行计划。 所以PARLANSE背后的关键思想是最小化调度谷物的成本,所以谷物可以很小,所以在真实代码中可以find很多谷物。 对这种权衡的洞察来自于纯粹的数据stream范式的彻底的失败,即所有的事情都与微小的并行块(比如add操作符)并行。

我们一直在为此工作十年。 它很难得到这个权利。 我不明白在这个时间框架内没有构build平行语言并使用/调整它们的人怎么可能build立有效的并行系统。

并行编程有三个部分IMO:确定并行性并指定并行性。 Identify =将algorithm分解为并发的工作块,指定=进行实际的编码/debugging。 识别是独立于你将用来指定并行性的框架,我不认为一个框架可以帮助那么多。 它具有良好的理解您的应用程序,目标平台,常见的并行编程权衡(硬件延迟等),以及最重要的经验。 然后指定可以讨论,这是我试图回答下面:

我尝试了许多框架(在学校和工作)。 既然你问了多核,这些都是共享内存,我会坚持使用三个共享内存框架。

Pthreads(它没有真正的框架,但绝对适用):

临: – 线程是非常普遍的。 对我来说,pthread就像是并行编程的组合。 你可以在pthread中编写任何范例。 – 它是灵活的,所以你可以把它作为你想要的高性能。 减慢你的速度没有固有的限制。 您可以编写自己的构造和原语,并尽可能地获得速度。

Con: – 要求你做所有的pipe道工作,像pipe理工作队列,任务分配,你自己。 – 实际的语法是丑陋的,你的应用程序往往有很多额外的代码,这使得代码很难写,然后难以阅读。

OpenMP的:

优点: – 代码看起来很干净,pipe道和任务分解大部分都在下面 – 半柔性。 它给你几个有趣的工作调度选项

缺点: – 为了简单的for-loop像并行。 (较新的英特尔verion也支持任务,但任务与Cilk相同)。 – 下面的结构可能会或可能不会写得很好。 GNU的实现是正确的。 英特尔的ICC为我工作得更好,但我宁愿自己写些东西来获得更高的性能。

Cilk,Intel TBB,Apple GCD:

优点: – 可用的任务级并行的最佳底层algorithm – 对串行/并行任务的精确控制 – TBB也有一个我使用的stream水线并行机制(这不是最好的坦率) – 消除了大量的写作任务基于任务的系统的代码,如果你是一个短的手,这可能是一个很大的优势

缺点:不能控制底层结构的性能。 我知道英特尔TBB的底层数据结构performance非常差,例如,工作队列糟糕(2008年我看到它)。 – 代码看起来很糟糕,有时需要使用所有关键字和stream行语 – 需要阅读大量的参考资料以了解其“灵活”的API

我会使用Java – 它的便携式,所以未来的处理器不会是一个问题。 我也将我的应用程序与分层接口/逻辑和数据(更像3层web应用程序)的层与标准互斥例程作为库(并行代码debugging较less)编码。 请记住,Web服务器能够很好地扩展到许多处理器,并且是多核最不痛苦的途径。 要么是这样的,要么就是使用与数据绑定的虚拟处理器来查看旧的连接机器模型。

Erlang是更“成熟的解决scheme”,是便携式和开源的。 我用Polyphonic C#拨弄,我不知道如何在它每天编程。

有几乎所有的语言/操作系统在阳光下,谷歌交易记忆库和扩展。 这是来自MS的一个有趣的方法。