【问题标题】:Is there a functional algorithm which is faster than an imperative one?有没有比命令式算法更快的函数式算法?
【发布时间】:2011-06-15 18:48:21
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • “功能性”或“命令性”算法是什么意思?采取任何命令式,对其执行 SSA 转换,将基本块转换为一组相互递归的函数,您将获得具有相同性能配置文件的相同算法的纯函数版本。反向翻译就更简单了。
  • 我认为这就是所有函数式编程炒作的原因。如果这个问题没有肯定的答案,那就证实了我的怀疑,它只是一种时尚:-)
  • @phkahler - 函数式算法更容易推理,就像 C 语言比汇编语言更容易推理一样。因此,尽管原则上编译后的 C 永远不会比汇编更快,但 C 并不是“只是一种时尚”。
  • 您正在设置错误的二分法。算法不分为“功能性”和“命令性”。
  • “开发时间”NOT“比运行时间贵”。如果仅仅是因为对于任何体面的程序,代码将花费比编写/维护更多的时间数量级。特别是。如果您考虑到大量用户(其中​​任何数量的时间可能比您的时间更有价值)。

标签: algorithm scala functional-programming imperative-programming


【解决方案1】:

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

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

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

【讨论】:

  • @Nikita:不要从帐户中撤回Lisp machine! ;-)
  • @Yasir Fantastic :) 从描述中可以看出,Lisp Machine 和其他机器一样重要,只是针对 Lisp 进行了“优化”。但我确实同意有一天(甚至现在)可能会有“功能计算机”感谢您的链接!
  • @Yasir:在物理上,即使是 Lisp 机器也遵循命令式模型。它具有随时间变化的状态(电子位置等)。因此,在某种程度上,它似乎可以用在相同硬件上产生等效命令式程序的术语来描述:诚然,您可能不得不严重破解机器以提供该程序以供执行。但是,如果您可以在功能计算机的柏拉图理想上执行计算:那只是直接从所有计算机状态的无限集合中获取包含结果的计算机,那么您就可以做生意了 ;-)
  • 将多核 CPU 甚至 HPC 集群描述为必要条件是否严格准确?该范式似乎基于严格的评估顺序,而在这些情况下不会发生这种情况......就此而言,无序优化和流水线不会在某种程度上破坏它吗?
  • @Knut 或者可以将它们归类为“红色”和“蓝色”,这真的没有区别。坦率地说,正如 Apocalisp 所指出的,这整个二分法已经构成。
【解决方案2】:

简短的回答:

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

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

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

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

所以在给定并行硬件的情况下,第一种算法的性能会更好,而你拥有的内核越多,差异就会越大。

【讨论】:

  • 虽然如此,但这并不能真正回答问题。如果正确实施,快速排序可以并行完成。就地快速排序甚至没有功能(交换修改状态),但可以并行运行。
  • @phkahler 我认为命令式的本质是命令的顺序执行。这是否意味着任何并行的事情都不能成为命令?
  • @Annan:并行与命令式/函数式讨论不同。每个线程仍然是命令式的。
【解决方案3】:

FWIW 有 Purely functional data structures,它们受益于函数式编程。

还有一本 Chris Okasaki 在Purely Functional Data Structures 上的好书,它从函数式语言的角度介绍了数据结构。

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

嗯,碰巧CnC 概念 一个步骤是一个纯函数。一步 除了读取它的输入和 生成标签和项目作为输出。这 选择设计将CnC带到那个 难以捉摸但美妙的地方叫 确定性并行。这 决定无关 语言偏好。 (事实上​​, 主要的 CnC 实现适用于 C++ 和 Java。)

然而,Haskell 和 CnC 是多么完美的匹配 将使! Haskell 是唯一的专业 我们可以(1)强制执行的语言 步骤是纯的,并且(2)直接 认识(并利用!)事实 步骤和图形执行 纯的。

再加上 Haskell 是 可扩展性极好,因此 CnC“图书馆”感觉就像一个 特定领域的语言。

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

【讨论】:

    【解决方案4】:

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

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

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

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

    【讨论】:

    • 无需停止对机器代码的推理。一旦你看到 CPU 优化,一切都会改变,一旦你达到量子尺度,一切都会改变(到那时,这真的很奇怪)
    【解决方案5】:

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

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

    【讨论】:

      【解决方案6】:

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

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

      【讨论】:

        猜你喜欢
        • 2016-01-30
        • 1970-01-01
        • 1970-01-01
        • 2021-01-13
        • 2017-07-19
        • 2013-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多