18

我在某处读到函数式编程适合利用计算中的多核趋势。我真的不明白。它与 lambda 演算和冯诺依曼架构有关吗?

4

9 回答 9

13

函数式编程最大限度地减少或消除了副作用,因此更适合分布式编程。即多核处理。

换句话说,许多难题可以同时在不同的内核上独立解决,而不必担心一个操作会影响另一个操作,就像在其他编程风格中一样。

于 2008-10-08T04:02:39.460 回答
13

处理并行处理最困难的事情之一是锁定数据结构以防止损坏。如果两个线程在没有完全锁定的情况下一次改变一个数据结构,则可能导致从无效数据到死锁的任何事情。

相比之下,函数式编程语言倾向于强调不可变数据。任何状态都与逻辑分开,一旦创建数据结构就无法修改。大大减少了对锁定的需求。

另一个好处是一些非常容易并行化的过程,如迭代,被抽象为函数。在 C++ 中,您可能有一个 for 循环,它对列表中的每个项目运行一些数据处理。但是编译器无法知道这些操作是否可以安全地并行运行——也许一个的结果取决于它之前的一个。当使用类似map()or的函数时reduce(),编译器可以知道调用之间没有依赖关系。因此可以同时处理多个项目。

于 2008-10-08T04:05:11.060 回答
10

我在某处读过函数式编程适合利用计算中的多核趋势......我并没有真正明白这个想法。它与 lambda 演算和冯诺依曼架构有关吗?

您引用的信念背后的论点是纯函数式编程控制副作用,这使得引入并行性变得更加容易和安全,因此,纯函数式编程语言在多核计算机的环境中应该是有利的。

不幸的是,由于以下几个原因,这种信念早已被推翻:

  • 纯函数式数据结构的绝对性能很差。因此,纯粹的函数式编程在性能方面是朝着错误方向迈出的一大步(这是并行编程的唯一目的)。

  • 纯函数式数据结构的扩展性很差,因为它们强调共享资源,包括分配器/GC 和主内存带宽。因此,随着内核数量的增加,并行化的纯功能程序通常会获得较差的加速。

  • 纯函数式编程使性能不可预测。因此,真正的纯函数式程序在并行化时经常会出现性能下降,因为粒度实际上是随机的。

例如,Haskell 社区经常引用的混蛋两行快速排序通常比用更传统的语言(如 F#)编写的真正的就地快速排序慢数千倍。此外,尽管您可以轻松地并行化优雅的 Haskell 程序,但您不太可能看到任何性能改进,因为所有不必要的复制会使单核使多核机器的整个主内存带宽饱和,从而使并行性毫无价值。事实上,没有人能够在 Haskell 中编写任何具有竞争性能的通用并行排序。Haskell 标准库提供的最先进的排序通常比传统的替代方法慢数百倍。

然而,函数式编程更常见的定义是一种强调使用一流函数的风格,实际上在多核编程的上下文中非常有用,因为这种范式非常适合分解并行程序。例如,请参阅.NET 4 中命名空间中的新高阶Parallel.For函数。System.Threading.Tasks

于 2010-06-27T20:21:49.027 回答
8

当没有副作用时,评估顺序无关紧要。然后可以并行计算表达式。

于 2008-10-08T04:06:01.747 回答
6

基本论点是很难自动并行化像 C/C++/等这样的语言,因为函数可以设置全局变量。考虑两个函数调用:

a = foo(b, c);
d = bar(e, f);

尽管 foo 和 bar 没有共同的参数,并且一个不依赖于另一个的返回码,但它们仍然可能具有依赖关系,因为 foo 可能会设置 bar 所依赖的全局变量(或其他副作用)。

函数式语言保证 foo 和 bar 是独立的:没有全局变量,也没有副作用。因此 foo 和 bar 可以安全地在不同的内核上自动运行,无需程序员干预。

于 2008-10-08T04:03:59.957 回答
4

上面的所有答案都指向一个关键思想,即“无共享可变存储”是并行执行程序片段的关键推动因素。它并没有真正解决寻找并行执行的同样困难的问题。但是函数式语言中典型的更清晰的功能表达确实使得理论上更容易从顺序表达式中提取并行性。

在实践中,我认为基于垃圾收集和更改时复制语义的语言的“无共享可变存储”属性使它们更容易添加线程。最好的例子可能是 Erlang,它结合了近功能语义和显式线程。

于 2008-10-12T19:18:06.680 回答
1

这是一个有点模糊的问题。多核 CPU 的一个好处是您可以运行一个功能程序并让它连续插入,而不必担心影响与机器正在执行的其他功能有关的任何计算。

多 U 服务器与服务器或 PC 中的多核 CPU 之间的区别在于,通过将其放在同一 BUS 上可以节省速度,从而实现更好、更快的内核通信。

编辑:我可能应该通过说在我所做的大多数脚本中,无论是否有多个内核,我都很少看到通过骇人听闻的并行化获取数据的问题,例如在我的脚本中一次运行多个小脚本所以我不会因为等待 URL 加载等事情而放慢速度。

双重编辑:此外,许多函数式编程语言已经分叉了几十年的并行变体。这些更好地利用并行计算并提高了一些速度,但它们从未真正流行起来。

于 2008-10-08T04:04:32.153 回答
0

省略任何技术/科学术语的原因是因为功能程序不共享数据。数据在函数之间进行复制和传输,因此应用程序中没有共享数据。

共享数据是导致多线程问题一半的原因。

于 2008-10-08T04:04:17.740 回答
0

Joe Armstrong(Erlang的创建者)的《Programming Erlang: Software for a Concurrent World 》一书谈到了将Erlang用于多核(/多处理器)系统。正如维基百科文章所述:

在 Erlang 中创建和管理进程是微不足道的,而线程在大多数语言中被认为是一个复杂且容易出错的主题。尽管所有并发在 Erlang 中都是显式的,但进程使用消息传递而不是共享变量进行通信,这消除了对锁的需求。

于 2008-10-12T19:48:10.957 回答