Performance 函数式编程是否允许更好的运行时编译器优化?

Performance 函数式编程是否允许更好的运行时编译器优化?,performance,functional-programming,clojure,Performance,Functional Programming,Clojure,注意:已将此设置为Wiki。我不在乎这个问题被贴上什么标签,只要有一个好的讨论 我听说在纯函数程序中,没有副作用,值也不会发生变化,这使得编译器更容易进行运行时优化。这在多大程度上是正确的 如果这是真的,我下一个关心的问题是,我们用它来换取的自由损失是什么?我的意思是,在像C++/C这样的语言中,开发人员完全可以控制并且可以调整很多事情。如果我们把这个工作交给编译器,我们就失去了这个机会。好的一面是,即使是非专家程序员也能写出好的代码。此外,如今机器体系结构中有如此多的缓存层,即使是专家也无法真

注意:已将此设置为Wiki。我不在乎这个问题被贴上什么标签,只要有一个好的讨论

我听说在纯函数程序中,没有副作用,值也不会发生变化,这使得编译器更容易进行运行时优化。这在多大程度上是正确的

如果这是真的,我下一个关心的问题是,我们用它来换取的自由损失是什么?我的意思是,在像C++/C这样的语言中,开发人员完全可以控制并且可以调整很多事情。如果我们把这个工作交给编译器,我们就失去了这个机会。好的一面是,即使是非专家程序员也能写出好的代码。此外,如今机器体系结构中有如此多的缓存层,即使是专家也无法真正做任何有价值的事情。因此,将这项工作委托给比程序员更了解底层体系结构的编译器是一个好主意

你有什么建议

你看到了吗

我用C、汇编和OCaml编程。我有时会查看由C编译器生成的程序集。它效率极低。我经常注意到全局变量被一次又一次地重新加载,当它在函数执行过程中(对于知道程序逻辑的人)很明显没有改变时,但是由于函数操纵指针,编译器不能假设没有触及该变量。当你注意到这一点时,一个简单的解决方法是在函数开始时将全局的内容复制到一个局部,这样你就保持了C的可移植性,但获得了类似于汇编的性能


但是你不必这么做,事实上,使用更高级的语言,你也不必这么做,因为指针、类型转换和其他低级构造较少,它们让你在想要接近硬件的时候,实际上却妨碍了自动优化。

请查看Haskell与C的比较:

您会注意到Haskell verison要短得多。然而,该页接着说,虽然c版本可以说更复杂,但它也可以进行排序,而Haskell版本必须分配更多的内存来工作


因此,从定义上讲,如果您希望能够对性能进行极端微调,那么c算法是更好的方法。

简单地说,仅仅因为函数式语言的编译器可以更好地优化,并不意味着它实际上可以。根据定义,任何疯狂的优化都会导致运行时的意外行为,这几乎肯定会导致bug。因此,我们要澄清一个严重的误解:理论上,Haskell程序可以由编译器“免费”进行并行化,但实际上它们不是,也不应该是

我认为编译器优化为王的时代已经结束了。当时钟速度成为性能优化的主要瓶颈时,像循环展开这样的优化是必不可少的。然而,对于绝大多数应用程序来说,问题不在于原始的CPU速度,而在于其他因素,如网络带宽、磁盘IO和/或正在使用的算法的速度

据我所知,例如,没有编译器优化来重新实现代码以使用并行设计模式。换句话说,如果你在你能想到的每一个编译器优化中都使用冒泡排序,而我使用的是快速排序。在大多数情况下,智能货币是基于快速排序实现的


函数式编程有很多价值,但是“因为它们运行得更快”不应该被考虑

作为一般规则,编译器优化会使代码有所不同:

  • 它实际上是编译的。如果调用堆栈的底部大部分时间都在编译器从未看到的代码中,那么它可以优化所有它想要的,但不会产生任何效果

  • 当用户或其他人实际在等待程序时,它会消耗相当一部分(比如10%或更多)的运行时间。如果没有人等待If,那么优化将永远不会被注意到

  • 不包含函数调用的。如果正在编译的代码包含函数调用,那么程序计数器几乎总是在别处


  • 所以你永远不会注意到,除非你编写了大量处理数据的算法,而没有调用你不编译的代码。就像你在排序算法中加入字符串比较一样,优化也是学术性的。

    我认为你应该谈论语言模型,而不是编译器优化。强制性程序性/功能性都有自己的领域。我举两个例子:

    命令式-程序性

    Erlang(一种函数式语言)。采用与内置Mnesia数据库共享状态的方法——它本质上是不起作用的,因为更新事务锁定数据库资源获取值、更改值并将其写回。他们这样做是为了提高性能,因为他们认识到速度在这方面很重要,如果他们不这样做,Erlang对于他们试图解决的问题将毫无用处(你能想象每次进行更改时都写出一个完整的数据库文件吗?=D

    纯功能性

    就模型允许的性能而言,功能性与非功能性是一个有趣的主题领域。从功能的角度看;当问题本质上是面向并发的时,Erlang会使机器运行时的核心内存饱和。一些测试表明YAWS web服务器:D Ejabberd也可以处理比传统消息交换机多得多的负载。我想说的是,纯函数式语言可以有一个运行时引擎,它可以跨大量内核大规模并行化应用程序。使用命令式过程代码很难做到这一点

    不同的模型适用于不同的问题。
    (map some-fn big-list)
    
    (pmap some-fn big-list)