真实世界的Clojure性能调优技巧?

Zub*_*air 25 clojure

我看到Clojure应用程序的"某些"部分出现了一些严重的减速.有没有人有关于他们如何在clojure应用程序上进行性能调整的真实世界提示?

mik*_*era 60

我的个人提示:

  • 首先检查你的算法 - 当它真的应该是O(n.log n)时你是否会产生O(n ^ 2)成本?如果你选择了一个糟糕的算法,剩下的调整就是浪费时间.
    • 要注意常见的"陷阱",例如遍历列表/序列的O(n)成本.
    • 利用Clojure中的优秀功能,例如复制大型持久数据结构的O(1)成本或映射/设置/向量访问的O(log32 n)成本.
  • 明智地选择Clojure的核心结构:
    • 一个原子是伟大的,当你需要一些可变数据,例如在一个循环中更新的一些数据
    • 如果要按顺序遍历某些数据,请使用列表而不是向量或映射,因为这样可以避免在遍历序列时创建临时对象.
    • 在适当的地方使用deftype/defrecord/defprotocol.这些都经过了大量优化,特别是从Clojure 1.2开始,应优先考虑defstruct/multimethods.
  • 利用Clojure的并发功能:
    • 当您同时进行多个独立计算时,pmapfuture都是利用多个内核的相对简单的方法.
    • 请记住,由于Clojure的不可变持久数据结构,制作和处理多个数据副本非常便宜.拍摄快照时你也不必担心锁定.....
  • 如果要与Java代码连接,请使用"(set!*warn-on-reflection*true)"并消除每个反射警告.反射是最昂贵的操作之一,如果反复执行,它将真正减慢您的应用程序.
  • 如果您仍需要更高性能,请确定代码中性能最关键的部分(例如,应用程序花费90%以上CPU时间的5%行),详细分析此部分并明智地应用以下规则:
    • 避免懒惰.懒惰是一个很好的功能,但带来一些额外的开销.请注意,许多Clojure的常见序列/列表函数都是惰性的(例如,for,map,partition).loop/recur,dotimes和reduce是你不懒的朋友.
    • 使用原始提示未经检查的算法可以更快地生成算术/数字代码.元是比Clojure的默认BigInteger的运算速度更快.
    • 最小化内存分配 - 尽量避免创建太多不必要的中间数据(向量,列表,映射,非原始数字等).所有分配都会产生少量的额外开销,并且随着时间的推移会导致更多/更长的GC暂停(如果您正在编写游戏/软实时应用程序,这可能是一个更大的问题.).
    • (Ab)使用Java数组 - 在Clojure中不是真正的惯用语,但是aget/aset/areduce和朋友非常快(他们从很多JVM优化中受益!!).(Ab)使用原始数组获得额外奖励积分.
    • 使用宏 - 尽可能在编译时生成丑陋但快速的代码

执行上述所有操作应该可以从Clojure代码中获得相当好的性能 - 通过仔细调整,我通常能够合理地接近纯Java性能,这对于动态语言来说非常令人印象深刻!


ent*_*opo 5

这个问题涵盖了 Clojure 的分析:Clojure 的分析工具?

我相信您会在那里找到一些好的建议。

然后直接从马嘴里说:http://clojure.org/getting_started#Getting%20Started-Profiling


Jef*_*ter 5

您可以使用JVisualVM对Clojure代码进行分析(有关示例,请参阅JVisualVM和Clojure).这应该至少指向慢速代码的正确方向.