关于多核:您今天建议使用哪种并行编程模型来利用未来的众核处理器?

What parallel programming model do you recommend today to take advantage of the manycore processors of tomorrow?

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

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

  • 程序员的生产力/易用性(凡人能成功使用它吗?)
  • 目标应用程序域(它(不)擅长什么问题?)
  • 并发风格(它是否支持任务,管道,数据并行,消息......?)

  • 可维护性/面向未来(任何人都会在20年内使用它吗?)
  • 性能(它如何在什么类型的硬件上扩展?)
  • 我故意对应用程序的性质含糊不清,以期获得对各种应用程序有用的良好通用答案。


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

  • MapReduce的。这很适用于可以将问题轻松分解为并行块的情况。
  • 嵌套数据并行。这类似于MapReduce,但实际上支持问题的递归分解,即使递归块的大小不规则。寻找NDP在大规模并行但有限的硬件(如GPU)上运行的纯函数式语言中取得巨大成功。
  • 软件事务内存。如果你需要传统的线程,STM让它们变得可以忍受。您在关键部分获得了50%的性能影响,但您可以将复杂的锁定方案扩展到100个处理器而不会感到痛苦。但是,这不适用于分布式系统。
  • 带消息传递的并行对象线程Erlang使用这个非常聪明的模型。每个"对象"都变成一个轻量级线程,对象通过异步消息和模式匹配进行通信。它基本上是真正的并行OO。这在几个实际应用程序中取得了很好的成功,并且对于不可靠的分布式系统非常有用。
  • 其中一些范例可以为您提供最大的性能,但只有在问题彻底分解时才能发挥作用。其他人牺牲了一些性能,但允许更多种算法。我怀疑上述的某些组合最终将成为标准工具包。


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

    软件事务内存给我留下了特别深刻的印象。它只是感觉它只是允许线程坚持生命一段时间,即使它们应该在几十年前就已经消亡。但是,它确实有三个主要优点:

  • 人们了解数据库中的交易
  • 已有关于事务RAM硬件的讨论
  • 尽管我们都希望它们消失,但线程可能会成为未来几十年的主要并发模型,这可能是令人遗憾的。 STM可以显着减轻疼痛。

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

    从某种意义上说,它更受战斗考验,因为谷歌正在使用mapreduce,并且很多人一直在使用hadoop,并且已经证明它适用于通过网络在多台机器上扩展应用程序。如果您可以跨网络扩展多台计算机,则可以在一台计算机上扩展多个核心。


    对于.NET应用程序,我选择".NET Parallel Extensions(PLINQ)",它非常易于使用,并允许我在几分钟内并行化现有代码。

  • 这很容易学习
  • 用于对大型对象数组执行复杂操作,因此我无法对其他应用程序发表评论
  • 支持任务和piplines
  • 应该支持未来几年,但谁知道肯定?
  • CTP版本有一些性能问题,但看起来非常有前途。
  • Mono可能会获得对PLINQ的支持,因此它也可能是一个跨平台的解决方案。


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

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


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

    Kamaelia - Concurrency made useful, fun

    In Kamaelia you build systems from simple components that talk to each other. This speeds development, massively aids maintenance and also means you build naturally concurrent software. It's intended to be accessible by any developer, including novices. It also makes it fun :)

    What sort of systems? Network servers, clients, desktop applications, pygame based games, transcode systems and pipelines, digital TV systems, spam eradicators, teaching tools, and a fair amount more :)

    另请参阅问题多核和并发 - 语言,库和开发技术


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


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


    看看Erlang。谷歌为它,并观看各种演示文稿和视频。我尊重的许多程序员和架构师都非常注重其可扩展性。我在相当重要的地方使用它...


    如前所述,纯函数式语言本质上是可并行化的。但是,对于许多人来说,命令式语言更直观,而且我们在命令式遗留代码中根深蒂固。基本问题是纯函数式语言明确表达副作用,而副作用则通过语句顺序在命令式语言中隐含表达。

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

    当然,就像今天仍然需要用汇编语言编写某些性能关键代码一样,明天仍然需要编写性能关键的显式并行代码。但是,我概述的技术应该有助于自动利用多核架构,而开发人员可以花费最少的精力。


    这个问题似乎一直出现在不同的措辞中 - 也许StackOverflow中有不同的选区。基于流程的编程(FBP)是一种概念/方法,已经存在了30多年,并且被用于处理加拿大主要银行的大部分批处理。它在Java和C#中有基于线程的实现,虽然早期的实现是基于光纤的(C ++和大型机汇编程序 - 银行使用的那个)。利用多核问题的大多数方法都涉及尝试采用传统的单线程程序并确定哪些部分可以并行运行。 FBP采用了不同的方法:应用程序从一开始就根据多个异步运行的"黑盒"组件进行设计(考虑制造装配线)。由于组件之间的接口是数据流,因此FBP本质上与语言无关,因此支持混合语言应用程序和特定于域的语言。出于同样的原因,副作用最小化。它也可以被描述为"无共享"模型和MOM(面向消息的中间件)。 MapReduce似乎是FBP的一个特例。 FBP与Erlang的不同之处主要在于Erlang以许多短期线程运行,因此绿色线程在那里更合适,而FBP使用更少(通常是几十到几百)更长寿命的线程。对于已经日常使用超过30年的批处理网络,请参阅批处理网络的一部分。有关交互式应用程序的高级设计,请参阅Brokerage app高级设计。已经发现FBP可以产生更多可维护的应用程序,并且可以改善经过的时间 - 即使在单核机器上也是如此!


    我真的很喜欢Clojure选择的模型。 Clojure使用不可变数据结构和软件事务内存的组合。

    不可变数据结构永远不会改变。可以使用修改后的数据创建新版本的结构,但如果您有一个指向数据结构的"指针",它将永远不会从您的下方更改。这很好,因为您可以访问该数据而无需担心并发问题。

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

    作者Rich Hickey的视频详细介绍了该视频。


    Qt concurrent为多核提供了MapReduce的实现,它非常易于使用。它是multiOS。


    If you were writing a new application from scratch today, and wanted it to scale to all the cores you could throw at it tomorrow, what parallel programming model/system/language/library would you choose?

    今天最广泛适用的可能是Cilk风格的任务队列(现在可在.NET 4中使用)。它们非常适用于可以通过分区和征服来解决的问题,这些问题可以通过子任务的可预测复杂性来实现(例如,在函数参数的复杂性已知的数组上并行mapreduce以及像quicksort这样的算法)许多真正的问题。

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


    如果您的问题域允许尝试考虑共享没有任何模型。进程和线程之间的共享越少,设置复杂的并发模型就越少。


    我们一直在使用PARLANSE,这是一个在过去十年中具有明确的并发部分顺序规范的并行编程语言,用于实现可扩展的程序分析和转换系统(DMS Software Reengineering Toolkit),它主要进行符号而非数值计算。 PARLANSE是一种编译的类C语言,具有传统的标量数据类型字符,整数,浮点数,动态数据类型字符串和数组,复合数据类型结构和联合以及词法范围的函数。虽然大多数语言都是vanilla(算术表达式比操作数,if-then-else语句,do循环,函数调用),但并行性却不是。通过在代码块上定义"先前"关系来表示并行性(例如,在b之前,在c之前,在c之前的d之前)
    写作

    1
    2
    3
    4
    5
    (|;  a  (... a's computation)
         (<< a) b ( ... b's computation ... )
         (<< a) c ( ....c's computation ...)
         (>> c) d ( ... d's computation...)
    )|;

    其中<<和>>运算符指的是"时间顺序"。 PARLANSE编译器可以看到这些并行计算并预分配计算所需的所有结构
    颗粒a,b,c,d,并生成自定义代码以启动/停止每个颗粒,从而最大限度地减少启动和停止这些平行颗粒的开销。

    请参阅此链接,了解并行迭代深化搜索15拼图的最佳解决方案,这是8拼图的4x4大哥。它只使用潜在的并行作为并行构造(|| a b c d),它表示对计算a b c d没有部分顺序约束,但它也使用推测并异步中止无法找到解决方案的任务。在很少的代码中有很多想法。

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

    大多数其他语言不暴露并行性,因此很难找到,并且运行时系统通过使用通用调度原语为调度计算粒度付出了高昂的代价。由于Amhdahl定律,我们认为这是灾难或至少表现不佳的一个因素:如果计划粮食的机器指令数量与工作相比较大,那么就无法提高效率。 OTOH,如果你坚持使用许多机器指令计算谷物以保持调度成本相对较低,你就找不到独立的计算粒度,因此你没有任何有用的并行性来安排。所以PARLANSE背后的关键思想是最大限度地减少调度晶粒的成本,因此晶粒可以很小,因此可以在实际代码中找到许多晶粒。对这种权衡的洞察来自于纯数据流范例的严重失败,它完成了与微小并行块(例如,添加运算符)并行的所有事情。

    我们一直在研究这个问题十年。很难做到这一点。我没有看到那些没有建立平行语言并且在这个时间框架内使用/调整它们的人们如何构建有效的并行系统。


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

    为什么?

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

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


    IMO并行编程有三个部分:识别并行性并指定并行性。识别=将算法分解为并发工作块,指定=进行实际编码/调试。识别与您将用于指定并行性的框架无关,我认为框架不会对此有所帮助。它充分了解您的应用程序,目标平台,常见的并行编程权衡(硬件延迟等),以及MOST重要的经验。但是可以讨论指定,这是我在下面尝试回答的内容:

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

    Pthreads(它不是??一个真正的框架,但绝对适用):

    优点:
    -Pthreads非常普遍。对我来说,pthreads就像是并行编程的集合。您可以在pthreads中编写任何范例。
    - 它具有灵活性,因此您可以根据需要提高性能。减慢你的速度没有固有的局限性。您可以编写自己的构造和基元,并尽可能快地获得。

    缺点:
    - 要求你做所有的管道工作,如管理工作队列,任务分配,你自己。
    - 实际的语法是丑陋的,你的应用程序经常有很多额外的代码,这使得代码难以编写,然后难以阅读。

    OpenMP的:

    优点:
    - 代码看起来干净,管道和任务分裂大多是在引擎盖下
    - 半灵活。它为您提供了几个有趣的工作安排选项

    缺点:
    - 适用于简单的for循环,如并行。 (较新的英特尔版本也支持任务,但任务与Cilk相同)。
    - 基础结构可能会或可能不会很好地编写性能。 GNU实现就可以了。英特尔的ICC对我来说效果更好,但我宁愿自己写一些东西以获得更高的性能。

    Cilk,英特尔TBB,Apple GCD:

    优点:
    - 可用于任务级并行性的最佳底层算法
    - 串行/并行任务的最终控制
    -TBB还有一个我使用的管道并行框架(坦白说不是最好的)
    - 消除为基于任务的系统编写大量代码的任务,如果你是空头的话,这可能是一个很大的优势

    缺点:
    - 低估底层结构的性能。我知道英特尔TBB的底层数据结构非常糟糕,例如,工作队列很糟糕(2008年,当我看到它时)。
    - 有时他们希望您使用的所有关键字和流行语代码看起来很糟糕
    - 需要阅读大量参考资料才能理解他们的"灵活"API


    另请参阅多核和并发 - 语言,库和开发技术这一问题


    一条有价值的路径可能是OpenCL,它提供了跨异构计算资源分配某些类型的计算负载的方法,IE相同的代码将运行在多核CPU和商品GPU上。 ATI最近发布了这样一个工具链。 NVidia的CUDA工具链类似,但有些限制。 Nvidia似乎还有一个OpenCL sdk正在开发中

    应该注意的是,在工作负载不具有数据并行性质的情况下,这可能无济于事,例如,它对典型的事务处理没有多大帮助。 OpenCL主要面向数学密集型计算,如科学/工程仿真或财务建模。


    我使用Java - 它的便携式,因此未来的处理器不会成为问题。我还将我的应用程序编码为分层接口/逻辑和数据(更像是3层Web应用程序),标准互斥例程作为库(较少调试并行代码)。请记住,Web服务器可以很好地扩展到许多处理器,并且是最难实现的多核路径。或者使用与数据绑定的虚拟处理器查看旧的Connection Machine模型。


    Erlang是更"成熟的解决方案",是便携式和开源的。我摆弄了Polyphonic C#,我不知道每天如何编程。

    几乎所有语言/操作系统都有图书馆和扩展程序,这些是谷歌交易记忆。这是MS的一个有趣的方法。


    推荐阅读