编译器优化的功能代码比命令式代码执行得更好的示例

时间:2013-01-16 11:57:40

标签: performance haskell optimization functional-programming imperative-programming

无副作用,referentially transparent函数式编程的承诺之一是可以对这些代码进行广泛优化。 引用Wikipedia

  

在许多情况下,数据的不变性可以通过允许编译器在命令式语言中做出不安全的假设来提高执行效率,从而增加强调文本 ng内联扩展的机会。 / p>

我希望看到一个示例,其中函数式语言编译器通过生成更好的优化代码来优于命令式编译器。

编辑:我试图给出一个特定的场景,但显然这不是一个好主意。所以我会尝试以不同的方式解释它。

程序员将想法(算法)翻译成机器可以理解的语言。同时,翻译的一个最重要的方面是人类也可以理解产生的代码。不幸的是,在许多情况下需要权衡:简洁易读的代码会受到性能降低的影响,需要手动优化。这容易出错,耗时,并且使代码的可读性降低(直到完全不可读)。

函数式语言的基础,例如不变性和引用透明性,允许编译器执行广泛的优化,这可以取代手动优化代码和免费程序员进行权衡。我正在寻找想法(算法)及其实现的示例,例如:

  1. (功能)实现接近原始想法并且易于理解,
  2. 它由语言编译器进行了广泛优化,
  3. 在命令式语言中编写类似的高效代码很难(或不可能),而无需手动优化,降低其简洁性和可读性。
  4. 如果它有点模糊,我道歉,但我希望这个想法很明确。我不想对答案给予不必要的限制。如果有人知道如何更好地表达它,我愿意接受建议。

    我的兴趣不仅仅是理论上的。我想用这些例子(除其他外)激励学生对函数式编程产生兴趣。

    起初,我对评论中提出的一些例子并不满意。我想再次反对,这些都是很好的例子。请随时将它们扩展为完整的答案,以便人们可以评论并投票给他们。


    (一类这样的例子很可能是并行代码,它可以利用多个CPU内核。通常在函数式语言中,这可以轻松完成而不会牺牲代码的简单性(就像在Haskell中添加par or pseq一样)我也对这些例子感兴趣,但也对其他非平行的例子感兴趣。)

4 个答案:

答案 0 :(得分:20)

在某些情况下,相同的算法将在纯上下文中更好地优化。具体来说,stream fusion允许一个算法,该算法由一系列可能变化很大的循环组成:地图,过滤器,折叠,展开,组成一个循环。

传统命令式设置中的等效优化,循环中的可变数据,必须实现完整的效果分析,没有人能做到。

因此,至少对于作为序列上的ana-和catamorphisms管道实现的算法类,您可以保证在命令设置中无法实现的优化结果。

答案 1 :(得分:5)

Geoff大陆最近的一篇论文Haskell beats C using generalised stream fusion,Simon Peyton Jones,Simon Marlow,Roman Leshchinskiy(提交给ICFP 2013)描述了这样一个例子。摘要(粗体中有趣的部分):

  

Stream fusion [6]是一种强大的自动转换技术   高级序列处理功能转化为高效实现。   它已被用于Haskell库中的巨大效果   用于操作字节数组,Unicode文本和未装箱的向量。   但是,某些操作(如vector append)仍然无法执行   在标准流融合框架内。其他,   像使用SSE和AVX指令的SIMD计算一样   在现代的x86芯片上,似乎不适合在框架中   所有

     

在本文中我们介绍了广义流融合   解决了这些问题。关键的见解是将捆绑组合在一起   流表示,每个都针对特定类的流进行调整   消费者。我们还描述了适合高效的流表示   用SSE指令计算。我们的想法得以实施   在GHC编译器和vector库的修改版本中。   基准测试表明使用高级Haskell代码编写   我们的编译器和库可以生成比两者都快的代码   编译器和手工矢量化C。

答案 2 :(得分:3)

这只是一个注释,而不是答案:gccpure属性,表明它可以考虑纯度;手册here中提到了明显的原因。

我认为'静态单一作业'强加了一种纯粹形式 - 请参阅http://lambda-the-ultimate.org/node/2860或维基百科文章中的链接。

答案 3 :(得分:0)

通过假设各种构建步骤是引用透明的,make和各种构建系统对大型项目执行得更好;因此,他们只需要重新运行已经改变输入的步骤。

对于中小型更改,这比从头开始构建要快得多。