请帮我理解这些Clojure Hprof的痕迹

and*_*oke 15 profiling jvm clojure hprof

我有一些Clojure代码,它模拟然后处理数值数据.数据基本上是双值的向量; 处理主要涉及以各种方式对它们的值进行求和.我将在下面包含一些代码,但我的问题是(我认为)更一般 - 我只是不知道如何解释hprof结果.

无论如何,我的测试代码是:

(defn spin [n]
  (let [c 6000
        signals (spin-signals c)]
      (doseq [_ (range n)] (time (spin-voxels c signals)))))

(defn -main []
  (spin 4))
Run Code Online (Sandbox Code Playgroud)

哪里spin-voxels应该比更贵spin-signals(特别是重复多次).我可以提供较低级别的例程,但我认为这个问题更多的是关于我不理解跟踪的基础知识(下面).

当我用lein编译它然后做一些简单的分析:

> java -cp classes:lib/clojure-1.3.0-beta1.jar -agentlib:hprof=cpu=samples,depth=10,file=hprof.vec com.isti.compset.stack
"Elapsed time: 14118.772924 msecs"
"Elapsed time: 10082.015672 msecs"
"Elapsed time: 9212.522973 msecs"
"Elapsed time: 12968.23877 msecs"
Dumping CPU usage by sampling running threads ... done.
Run Code Online (Sandbox Code Playgroud)

和配置文件跟踪看起来像:

CPU SAMPLES BEGIN (total = 4300) Sun Aug 28 15:51:40 2011
rank   self  accum   count trace method
   1  5.33%  5.33%     229 300791 clojure.core$seq.invoke
   2  5.21% 10.53%     224 300786 clojure.core$seq.invoke
   3  5.05% 15.58%     217 300750 clojure.core$seq.invoke
   4  4.93% 20.51%     212 300787 clojure.lang.Numbers.add
   5  4.74% 25.26%     204 300799 clojure.core$seq.invoke
   6  2.60% 27.86%     112 300783 clojure.lang.RT.more
   7  2.51% 30.37%     108 300803 clojure.lang.Numbers.multiply
   8  2.42% 32.79%     104 300788 clojure.lang.RT.first
   9  2.37% 35.16%     102 300831 clojure.lang.RT.more
  10  2.37% 37.53%     102 300840 clojure.lang.Numbers.add
Run Code Online (Sandbox Code Playgroud)

这很酷.到此为止,我很高兴.我可以看到我浪费时间处理数值的通用处理.

所以我看看我的代码并决定,作为第一步,我将替换vecd-vec:

(defn d-vec [collection]
  (apply conj (vector-of :double) collection))
Run Code Online (Sandbox Code Playgroud)

我不确定这是否足够 - 我怀疑我还需要在不同的地方添加一些类型的注释 - 但这似乎是一个好的开始.所以我再次编译和配置文件:

> java -cp classes:lib/clojure-1.3.0-beta1.jar -agentlib:hprof=cpu=samples,depth=10,file=hprof.d-vec com.isti.compset.stack
"Elapsed time: 15944.278043 msecs"
"Elapsed time: 15608.099677 msecs"
"Elapsed time: 16561.659408 msecs"
"Elapsed time: 15416.414548 msecs"
Dumping CPU usage by sampling running threads ... done.
Run Code Online (Sandbox Code Playgroud)

EWWW.所以它明显变慢了.和个人资料?

CPU SAMPLES BEGIN (total = 6425) Sun Aug 28 15:55:12 2011
rank   self  accum   count trace method
   1 26.16% 26.16%    1681 300615 clojure.core.Vec.count
   2 23.28% 49.45%    1496 300607 clojure.core.Vec.count
   3  7.74% 57.18%     497 300608 clojure.lang.RT.seqFrom
   4  5.59% 62.77%     359 300662 clojure.core.Vec.count
   5  3.72% 66.49%     239 300604 clojure.lang.RT.first
   6  3.25% 69.74%     209 300639 clojure.core.Vec.count
   7  1.91% 71.66%     123 300635 clojure.core.Vec.count
   8  1.03% 72.68%      66 300663 clojure.core.Vec.count
   9  1.00% 73.68%      64 300644 clojure.lang.RT.more
  10  0.79% 74.47%      51 300666 clojure.lang.RT.first
  11  0.75% 75.22%      48 300352 clojure.lang.Numbers.double_array
  12  0.75% 75.97%      48 300638 clojure.lang.RT.more
  13  0.64% 76.61%      41 300621 clojure.core.Vec.count
  14  0.62% 77.23%      40 300631 clojure.core.Vec.cons
  15  0.61% 77.84%      39 300025 java.lang.ClassLoader.defineClass1
  16  0.59% 78.43%      38 300670 clojure.core.Vec.cons
  17  0.58% 79.00%      37 300681 clojure.core.Vec.cons
  18  0.54% 79.55%      35 300633 clojure.lang.Numbers.multiply
  19  0.48% 80.03%      31 300671 clojure.lang.RT.seqFrom
  20  0.47% 80.50%      30 300609 clojure.lang.Numbers.add
Run Code Online (Sandbox Code Playgroud)

我在这里添加了更多行,因为这是我不理解的部分.

为什么在地球上Vec.count出现如此频繁?这是一个返回向量大小的方法.单个行查找属性.

我假设我比较慢,因为我仍然在Double和double之间来回跳跃,当我添加更多类型注释时,事情可能会再次改善.但是我不明白我现在拥有什么,所以我不太确定浮躁的事情有多大意义.

请问,任何人都可以一般性地解释上面的转储吗?我保证不会反复调用count- 相反,我有很多地图,并减少了一些显式循环.

我想知道我是否可能被JIT弄糊涂了?也许我错过了一堆信息,因为函数被内联了?哦,我正在使用1.3.0-beta1,因为它似乎有更明智的数字处理.

[ 更新 ]我在http://www.acooke.org/cute/Optimising1.html上总结了我的经历- 获得了5倍的加速(实际上是在清理了一些并且移动到1.3之后的10倍),尽管从未理解这一点.

Ver*_*erg 1

对 Vec 对象(由 vector-of 创建的对象)调用 seq 会创建一个 VecSeq 对象。

在 Veca 上创建的 VecSeq 对象在其内部减少方法中调用 Vec.count,该方法由 clojure.core/reduce 使用。

所以看起来由 vector-of 创建的向量在减少时调用 Vec.count 。正如您提到的,代码做了很多减少这似乎是原因

令人毛骨悚然的是 Vec.count 是 Vec.count 看起来非常简单:

clojure.lang.Counted
  (count [_] cnt)
Run Code Online (Sandbox Code Playgroud)

一个不进行任何计数的简单 getter。