15

数値データをシミュレートしてから処理する Clojure コードがいくつかあります。データは基本的に double 値のベクトルです。処理には、主にさまざまな方法で値を合計することが含まれます。以下にいくつかのコードを含めますが、私の質問は (私が思うに) より一般的なものです。hprof の結果を解釈する方法がわかりません。

とにかく、私のテストコードは次のとおりです。

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

(defn -main []
  (spin 4))

どこ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.

プロファイル トレースは次のようになります。

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

これはかなりクールです。ここまで、幸せです。数値の一般的な処理で時間を無駄にしていることがわかります。

だから私は自分のコードを見て、最初のステップとして、次のものに置き換えることにvecしましたd-vec:

(defn d-vec [collection]
  (apply conj (vector-of :double) collection))

これで十分かどうかはわかりませんが、さまざまな場所にいくつかの型注釈を追加する必要もあると思いますが、良いスタートのようです。だから私は再びコンパイルしてプロファイリングします:

> 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.

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

これは私が理解できない部分であるため、ここにさらに行を含めました。

何でこんなに頻繁Vec.count登場するの?ベクターの大きさを返すメソッドです。属性の単一行ルックアップ。

私はまだ Double と double の間を行ったり来たりしているため、速度が遅いと思います。さらに型注釈を追加すると、状況が再び改善される可能性があります。しかし、私は今自分が何を持っているかを理解していないので、前に大失敗することがあまり意味があるかどうかはわかりません.

上記のダンプを一般的な用語で説明できる人はいますか? 私は繰り返し呼び出しをしないことを約束しますcount- 代わりに、たくさんの map と reduce といくつかの明示的なループがあります。

おそらくJITに混乱しているのではないかと思いましたか?関数がインライン化されているため、情報の山が不足している可能性がありますか? ああ、私は 1.3.0-beta1 を使用しています。これは、より賢明な数値処理が行われているように見えるためです。

[更新] http://www.acooke.org/cute/Optimising1.htmlで自分の経験をまとめました- これを理解していないにもかかわらず、5 倍のスピードアップが得られました (実際には、さらにクリーニングして 1.3 に移行すると 10 倍になりました)。

4

2 に答える 2

1

Vec オブジェクト (vector-of によって作成されたオブジェクト) に対して seq を呼び出すと、VecSeq オブジェクトが作成されます。

Veca で作成された VecSeq オブジェクトは、clojure.core/reduce によって使用されるメソッド internal-reduce で Vec.count を呼び出します。

vector-of によって作成されたベクトルは、削減中に Vec.count を呼び出すようです。そして、あなたが言及したように、コードは多くの削減を行いましたが、これが原因のようです

不気味なままなのは、Vec.count が非常に単純に見えることです。

clojure.lang.Counted
  (count [_] cnt)

カウントを行わない単純なゲッター。

于 2011-09-22T12:45:13.937 に答える
0

大声で話すだけで、コードがSeqとの間で多くの変換を行っているように見えます。

RT.seqFromを見ると、これはArraySeq.createFromObjectを呼び出します

if(array == null || Array.getLength(array) == 0)
   return null;

つまり、vecを使用すると高速のベクトルアクセスが使用され、d-vecを使用すると、配列の使用と低速のjava.lang.Array.getLengthメソッド(リフレクションを使用する..)の呼び出しが強制されます。

于 2011-08-29T05:31:24.993 に答える