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

时间:2011-04-05 08:44:17

标签: clojure

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

3 个答案:

答案 0 :(得分:58)

我的个人提示:

  • 首先检查您的算法 - 当它真的应该是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的并发功能:
    • pmap 未来是您在同时进行多项独立计算时利用多核的相对简单的方法。
    • 请记住,由于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性能,这对于动态语言来说非常令人印象深刻!

答案 1 :(得分:5)

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

答案 2 :(得分:3)

这个问题涉及Clojure的分析:Profiling tool for Clojure?

我相信你会在那里找到一些好的提示。

然后直接从马的口中:http://clojure.org/getting_started#Getting%20Started-Profiling