4

我正在寻找一种比命令式更快的函数式算法(或这种算法的参数)。

我喜欢函数式代码,因为它比命令式挂件更具表现力,而且更容易阅读。但我也知道这种表现力会消耗运行时开销。并不总是由于尾递归之类的技术 - 但它们通常更慢。

在编程时,我不考虑功能代码的运行时成本,因为现在 PC 速度非常快,并且开发时间比运行时更昂贵。此外,对我来说,可读性比性能更重要。尽管如此,我的程序足够快,所以我很少需要以命令的方式解决问题。

有些算法在实践中应该以命令式的方式实现(如排序算法),否则在大多数情况下它们太慢或需要大量内存。相比之下,由于像用函数式语言编写的解析器这样的模式匹配整个程序之类的技术,可能比用命令式语言编写的程序快得多,因为编译器可以优化代码。

但是是否有任何算法在函数式风格上更快,或者是否有可能设置这种算法的参数?

4

6 回答 6

14

一个简单的推理。我不保证术语,但它似乎是有道理的。

  • 要执行的功能程序将需要转换为某些机器指令集。
  • 所有机器(我听说过)都是必不可少的。
  • 因此,对于每个函数式程序,都有一个命令式程序(粗略地说,在汇编语言中),等价于它。

因此,您可能必须对“表现力”感到满意,直到我们获得“功能计算机”。

于 2011-01-26T11:17:54.203 回答
5

简短的回答:

任何可以轻松并行化的东西,因为它没有副作用,在多核处理器上会更快。

例如,QuickSort 在与不可变集合一起使用时可以很好地扩展:http ://en.wikipedia.org/wiki/Quicksort#Parallelization

在其他条件相同的情况下,如果您有两种可以合理描述为等效的算法,除了一种在不可变数据上使用纯函数,而第二种依赖于就地突变,那么第一种算法将轻松扩展到多个核心.

甚至可能您的编程语言可以为您执行此优化,就像 scalaCL 插件将编译代码以在您的 GPU 上运行一样。(我现在想知道 SIMD 指令是否使它成为“功能性”处理器)

所以给定并行硬件,第一个算法会表现得更好,你拥有的内核越多,差异就会越大。

于 2011-01-26T11:17:17.187 回答
3

FWIW 有纯函数式数据结构,它们受益于函数式编程。

还有一本 Chris Okasaki 撰写的关于纯函数式数据结构的好书,它从函数式语言的角度介绍了数据结构。

另一篇关于并行编程的有趣文章Announcing Intel Concurrent Collections for Haskell 0.1,他们指出:

好吧,CnC 的 step 概念恰好是一个纯函数。一个步骤除了读取其输入并生成标签和项目作为输出之外什么都不做。选择这种设计是为了将 CnC 带到那个难以捉摸但美妙的地方,称为 确定性并行。该决定与语言偏好无关。(事实上​​,主要的 CnC 实现是针对 C++ 和 Java 的。)

然而,Haskell 和 CnC 将是多么伟大的匹配!Haskell 是唯一一种我们可以(1)强制执行纯步骤和(2)直接识别(并利用!)步骤和图形执行都是纯的事实的主要语言 。

除此之外,Haskell 具有极好的可扩展性,因此 CnC“库”感觉就像是一种特定领域的语言。

它没有提到性能——他们承诺在以后的文章中讨论一些实现细节和性能,但 Haskell 的“纯粹性”非常适合并行编程。

于 2011-01-26T11:27:50.350 回答
1

有人可能会争辩说,所有程序都归结为机器代码。

因此,如果我反汇编(命令式程序的)机器代码并调整汇编程序,我可能最终会得到一个更快的程序。或者我可以想出一个利用某些特定 CPU 功能的“汇编程序算法”,因此它确实比命令式语言版本更快。

这种情况是否导致我们应该到处使用汇编程序的结论?不,我们决定使用命令式语言,因为它们不那么麻烦。我们用汇编程序编写代码是因为我们确实需要。

理想情况下,我们还应该使用 FP 算法,因为它们编写起来不那么繁琐,并且在我们真正需要时使用命令式代码。

于 2011-01-26T17:47:26.613 回答
0

好吧,我猜你的意思是问是否有一个用函数式编程语言实现的算法,它比用命令式语言实现的相同算法的另一个实现更快。“更快”是指根据我们认为值得信赖的一些测量,它在某些输入上的执行时间或内存占用方面表现更好。

我不排除这种可能性。:)

于 2011-01-26T15:23:18.637 回答
0

为了详细说明 Yasir Arsanukaev 的回答,在某些情况下,纯函数数据结构可能比可变数据结构更快,因为它们共享其结构的一部分。因此,在您可能必须以命令式语言复制整个数组或列表的地方,您可以避免复制的一小部分,因为您只能更改(和复制)数据结构的一小部分。函数式语言中的列表是这样的——多个列表可以共享同一个尾部,因为什么都不能修改。(这可以在命令式语言中完成,但通常不是,因为在命令式范式中,人们通常不习惯谈论不可变数据。)

此外,函数式语言中的惰性求值(特别是默认情况下惰性的 Haskell)也非常有利,因为它可以在代码的结果实际上不被使用时消除代码执行。(但是,首先要非常小心,不要在命令式语言中运行此代码。)

于 2011-01-27T00:44:30.827 回答