如果你今天从头开始编写一个新的应用程序,并希望它可以扩展到明天你可以投入的所有核心,你会选择什么样的并行编程模型/系统/语言/库?为什么呢?
我对这些方面的答案特别感兴趣:
对于应用程序的性质,我故意模糊不清,期望获得对各种应用程序有用的好的一般答案。
答案 0 :(得分:27)
多核编程实际上可能需要不止一种范例。目前的一些竞争者是:
这些范例中的一些可以为您提供最大的性能,但只有在问题彻底分解时才能发挥作用。其他人牺牲了一些性能,但允许更多种算法。我怀疑上述的某些组合最终将成为标准工具包。
答案 1 :(得分:14)
我非常喜欢的两个解决方案是join calculus(JoCaml,Polyphonic C#,Cω)和actor model(Erlang,{{3 }},Scala,E)。
我对Io并没有特别深刻的印象。它只是感觉它只是允许线程坚持生命一段时间,即使它们应该在几十年前就已经消亡。但是,它确实有三个主要优点:
答案 2 :(得分:11)
mapreduce / hadoop范例很有用,也很有用。特别是对于那些习惯像perl这样的语言的人来说,映射数组并对每个元素进行一些操作的想法应该非常流畅和自然,而mapreduce / hadoop只是把它带到下一个阶段并说没有理由数组的每个元素都需要在同一台机器上处理。
从某种意义上说,它更受战斗考验,因为谷歌正在使用mapreduce,而且很多人一直在使用hadoop,并且已经证明它适用于通过网络在多台机器上扩展应用程序。如果您可以跨网络扩展多台计算机,则可以在一台计算机上扩展多个核心。
答案 3 :(得分:10)
对于.NET应用程序,我选择“.NET Parallel Extensions (PLINQ)”它非常容易使用,并允许我在几分钟内并行化现有代码。
Mono will likely get支持PLINQ,因此它也可以是跨平台的解决方案。
答案 4 :(得分:6)
对于繁重的计算等,像Haskell这样的纯函数式语言很容易parallelizable而不需要程序员的任何努力。除了学习Haskell,就是这样。
然而,我不认为这是(近)未来的方式,仅仅因为太多的程序员已经习惯了命令式编程范式。
答案 5 :(得分:5)
kamaelia是一个 python框架,用于构建具有大量通信进程的应用程序。
Kamaelia - 并发变得有用,有趣
在Kamaelia中,您可以使用相互通信的简单组件构建系统。这加速了开发,大规模地帮助维护,也意味着您构建自然并发的软件。它可以由任何开发人员访问,包括新手。它也很有趣:)
什么样的系统?网络服务器,客户端,桌面应用程序,基于游戏的游戏,转码系统和管道,数字电视系统,垃圾邮件根除者,教学工具以及更多的数据:)
另见问题Multi-Core and Concurrency - Languages, Libraries and Development Techniques
答案 6 :(得分:4)
我打赌用promises传递事件循环,如在Twisted,E,AmbientTalk等系统中实现的那样。它们保留了使用与非并发/并行应用程序相同的执行模型假设编写代码的能力,但可以扩展到分布式和并行系统。 (这就是为什么我正在研究Ecru。)
答案 7 :(得分:2)
结帐Erlang。谷歌为它,并观看各种演示文稿和视频。我尊重的许多程序员和架构师都非常注重其可扩展性。我在相当重要的地方使用它......
答案 8 :(得分:2)
如前所述,纯函数式语言本质上是可并行化的。但是,对于许多人来说,命令式语言更直观,而且我们在命令式遗留代码中根深蒂固。根本问题是纯函数式语言明确表达副作用,而副作用则通过语句顺序在命令式语言中隐含表达。
我认为,以声明方式表达副作用的技术(例如,在面向对象的框架中)将允许编译器将命令式语句分解为其功能关系。这应该允许代码自动并行化,就像纯函数代码一样。
当然,就像今天仍然需要用汇编语言编写某些性能关键代码一样,明天仍然需要编写性能关键的显式并行代码。但是,我概述的技术应该有助于自动利用多核架构,而开发人员可以花费最少的工作量。
答案 9 :(得分:2)
我很惊讶没有人建议使用MPI(消息传递接口)。虽然设计用于分布式存储器,但MPI程序具有基本和频繁的全局耦合(解决具有数十亿未知数的线性和非线性方程),已经显示可扩展到200k核心。
答案 10 :(得分:2)
这个问题似乎一直出现在不同的措辞中 - 也许StackOverflow中有不同的选区。 Flow-Based Programming (FBP)是一种概念/方法,已经存在了30多年,并且被用于处理加拿大主要银行的大部分批处理。它在Java和C#中有基于线程的实现,虽然早期的实现是基于光纤的(C ++和大型机汇编程序 - 银行使用的那个)。利用多核问题的大多数方法都涉及尝试采用传统的单线程程序并确定哪些部分可以并行运行。 FBP采用了不同的方法:应用程序从一开始就根据多个异步运行的“黑盒”组件进行设计(考虑制造装配线)。由于组件之间的接口是数据流,因此FBP本质上与语言无关,因此支持混合语言应用程序和特定于域的语言。出于同样的原因,副作用最小化。它也可以被描述为“无共享”模型和MOM(面向消息的中间件)。 MapReduce似乎是FBP的一个特例。 FBP与Erlang的不同之处主要在于Erlang以许多短期线程运行,因此绿色线程在那里更合适,而FBP使用更少(通常是几十到几百)更长寿命的线程。对于已经日常使用超过30年的批处理网络的件,请参阅 part of batch network。有关交互式应用的高级设计,请参阅 Brokerage app high-level design。已经发现FBP可以产生更多可维护的应用程序,并且可以改善经过的时间 - 即使在单核机器上也是如此!
答案 11 :(得分:1)
IMO并行编程有三个部分:识别并行性并指定并行性。识别=将算法分解为并发工作块,指定=进行实际编码/调试。识别与您将用于指定并行性的框架无关,我认为框架不会对此有所帮助。它充分了解您的应用程序,目标平台,常见的并行编程权衡(硬件延迟等),以及MOST重要的经验。但是可以讨论指定,这是我在下面尝试回答的问题:
我尝试了很多框架(在学校和工作中)。既然您询问了多核,这些都是共享内存,我会坚持使用三种共享内存框架。
Pthreads(它不是一个真正的框架,但绝对适用):
临: -Pthreads非常普遍。对我来说,pthreads就像是并行编程的集合。您可以在pthreads中编写任何范例。 - 它具有灵活性,因此您可以根据需要提高性能。减慢你的速度没有固有的局限性。您可以编写自己的构造和基元,并尽可能快地获得速度。
缺点: - 要求你做所有的管道工作,如管理工作队列,任务分配,你自己。 - 实际的语法是丑陋的,你的应用程序经常有很多额外的代码,这使得代码难以编写,然后难以阅读。
的OpenMP:
优点: - 代码看起来干净,管道和任务分裂大多是在引擎盖下 - 半灵活。它为您提供了几个有趣的工作安排选项
缺点: - 适用于简单的for循环,如并行。 (较新的英特尔版本也支持任务,但任务与Cilk相同)。 - 基础结构可能会或可能不会很好地编写性能。 GNU实现就可以了。英特尔的ICC对我来说效果更好,但我宁愿自己写一些东西以获得更高的性能。
Cilk,英特尔TBB,Apple GCD:
优点: - 可用于任务级并行性的最佳底层算法 - 串行/并行任务的最终控制 -TBB还有一个我使用的管道并行框架(坦白说不是最好的) - 消除为基于任务的系统编写大量代码的任务,如果你是空头的话,这可能是一个很大的优势
缺点: - 低估底层结构的性能。我知道英特尔TBB的底层数据结构非常糟糕,例如,工作队列很糟糕(2008年,当我看到它时)。 - 有时他们希望您使用的所有关键字和流行语代码看起来很糟糕 - 需要阅读大量参考资料以了解其“灵活”API
答案 12 :(得分:1)
如果你今天从头开始编写一个新的应用程序,并希望它可以扩展到你明天可以使用的所有内核,你会选择哪种并行编程模型/系统/语言/库?
今天最广泛适用的可能是Cilk风格的任务队列(现在可在.NET 4中使用)。它们非常适用于可以通过分区和征服来解决的问题,这些问题可以通过子任务的可预测复杂性来解决(例如,在已知函数参数的复杂性的数组上的并行map
和reduce
以及像quicksort这样的算法)这涵盖了许多实际问题。
此外,这仅适用于像今天的多核一样的共享内存架构。虽然我不相信这种基本架构会很快消失,但我相信它必须在某些时候与分布式并行性相结合。这将是多核CPU上的多核群集形式,在多核之间传递消息,或者以核心层次结构的形式,它们之间具有可预测的通信时间。这些将需要截然不同的编程模型以获得最大效率,我不相信它们有太多了解。
答案 13 :(得分:1)
一条有价值的路径可能是OpenCL,它提供了跨异构计算资源分配某些类型的计算负载的方法,IE相同的代码将在多核CPU和商品GPU上运行。 ATI最近发布了这样一个toolchain。 NVidia的CUDA工具链类似,但有些限制。 Nvidia似乎也有一个OpenCL sdk in the works
应该注意的是,在工作负载不具有数据并行性质的情况下,这可能无济于事,例如,它对典型的事务处理没有多大帮助。 OpenCL主要面向数学密集型计算,如科学/工程仿真或财务建模。
答案 14 :(得分:1)
我真的很喜欢模型Clojure选择的模型。 Clojure使用不可变数据结构和软件事务内存的组合。
不可变数据结构永远不会改变。可以使用修改后的数据创建新版本的结构,但如果您有一个指向数据结构的“指针”,它将永远不会从您的下方更改。这很好,因为您可以访问该数据而无需担心并发问题。
软件事务内存在这些答案的其他地方讨论,但足以说它是一种机制,多个线程都可以对一些数据进行操作,如果它们发生冲突,其中一个线程将被回滚再次尝试。当碰撞风险存在但不太可能时,这允许更快的速度。
作者Rich Hickey有一个video可以提供更多细节。
答案 15 :(得分:1)
我们一直在使用PARLANSE,这是一个在过去十年中具有明确的并发部分顺序规范的并行编程语言,用于实现可扩展的程序分析和转换系统(DMS Software Reengineering Toolkit)符号而不是数值计算。 PARLANSE是一种编译的类C语言,具有传统的标量数据类型字符,整数,浮点数,动态数据类型字符串和数组,复合数据类型结构和联合以及词法范围的函数。虽然大多数语言都是vanilla(算术表达式比操作数,if-then-else语句,do循环,函数调用),但并行性却不是。通过在代码块上定义“先前”关系来表示并行性(例如,在b之前,在c之前,在c之前的d之前) 写成
(|; a (... a's computation)
(<< a) b ( ... b's computation ... )
(<< a) c ( ....c's computation ...)
(>> c) d ( ... d's computation...)
)|;
其中&lt;&lt;和&gt;&gt;运营商参考“及时订购”。 PARLANSE编译器可以看到这些并行计算并预分配计算所需的所有结构 颗粒a,b,c,d,并生成自定义代码以启动/停止每个颗粒,从而最大限度地减少启动和停止这些平行颗粒的开销。
请参阅 parallel iterative deepening search for optimals solutions to the 15-puzzle的链接,这是8-puzzle的4x4大哥。它只使用 potential parallel作为并行构造(|| abcd),它表示计算中没有部分顺序约束 a b c d ,但它也使用推测并异步中止无法找到解决方案的任务。在很少的代码中有很多想法。
PARLANSE在多核PC上运行。一个大的PARLANSE程序(我们已经构建了许多具有100万+行或更多)的程序将有数千个这样的部分订单,其中一些调用包含其他部分的函数。 到目前为止,我们已经有多达8个CPU的良好结果,以及高达16的适度回报,我们仍然在调整系统。 (我们认为当前PC上核心数量较多的一个真正问题是内存带宽:16个核心冲击内存子系统会产生巨大的带宽需求)。
大多数其他语言不会暴露并行性,因此很难找到,并且运行时系统通过使用通用调度原语为调度计算粒度付出了高昂的代价。由于Amhdahl定律,我们认为这是灾难或至少表现不佳的一个因素:如果计划粮食的机器指令数量与工作相比较大,那么就无法提高效率。 OTOH,如果你坚持使用许多机器指令计算谷物以保持调度成本相对较低,你就找不到独立的计算粒度,因此你没有任何有用的并行性来安排。所以PARLANSE背后的关键思想是最大限度地减少调度晶粒的成本,因此晶粒可以很小,因此可以在实际代码中找到许多晶粒。对这种权衡的洞察来自于纯数据流范例的严重失败,它完成了与微小并行块(例如,添加运算符)并行的一切。
我们一直在研究这个问题十年。很难做到这一点。我没有看到那些没有建立平行语言并且在这个时间框架内使用/调整它们的人们如何构建有效的并行系统。
答案 16 :(得分:1)
具有多个工作系统的作业队列(不确定正确的术语 - 消息队列?)
为什么?
主要是因为这是一个荒谬简单的概念。你有一个需要处理的东西列表,然后是一堆可以获得工作并处理它们的进程。
另外,不同的原因,比如,Haskell或Erlang是如此并发/并行(?),它完全与语言无关 - 您可以用C,Python或任何其他语言(甚至使用shell)轻松实现这样的系统脚本),而我怀疑bash会很快获得软件事务内存或加入微积分。
答案 17 :(得分:1)
答案 18 :(得分:1)
如果您的问题域允许尝试考虑共享没有任何模型。进程和线程之间的共享越少,设置复杂的并发模型就越少。
答案 19 :(得分:1)
Qt concurrent为多核提供了MapReduce的实现,它非常易于使用。它是multiOS。
答案 20 :(得分:0)
Erlang是更“成熟的解决方案”,是便携式和开源的。我摆弄了Polyphonic C#,我不知道每天如何编程。
几乎所有语言/操作系统都有图书馆和扩展程序,这些是谷歌交易记忆。这是MS的一个有趣的方法。
答案 21 :(得分:0)
我使用Java - 它的便携性,因此未来的处理器不会成为问题。我还将我的应用程序编码为分层接口/逻辑和数据(更像是3层Web应用程序),标准互斥例程作为库(较少调试并行代码)。请记住,Web服务器可以很好地扩展到许多处理器,并且是最难实现的多核路径。或者使用与数据绑定的虚拟处理器查看旧的Connection Machine模型。