23

私は現在、Rの代わりにClojureとIncanterを検討しています(Rが嫌いなわけではありませんが、新しい言語を試すのは面白いです)。Incanterが好きで、構文が魅力的ですが、ベクトル化された操作は比較するとかなり遅いです。たとえば、RまたはPythonに。

例として、Incanterベクトル演算、Clojureマップ、およびRを使用して、ベクトルの1次差分を取得したいと思いました。以下は、すべてのバージョンのコードとタイミングです。ご覧のとおり、Rの方が明らかに高速です。

IncanterとClojure:

(use '(incanter core stats)) 
(def x (doall (sample-normal 1e7))) 
(time (def y (doall (minus (rest x) (butlast x))))) 
"Elapsed time: 16481.337 msecs" 
(time (def y (doall (map - (rest x) (butlast x))))) 
"Elapsed time: 16457.850 msecs"

R:

rdiff <- function(x){ 
   n = length(x) 
   x[2:n] - x[1:(n-1)]} 
x = rnorm(1e7) 
system.time(rdiff(x)) 
   user  system elapsed 
  1.504   0.900   2.561

だから私はIncanter/Clojureでベクトル演算を高速化する方法があるのだろうかと思っていましたか?また、Clojureのループ、Java配列、ライブラリの使用を含むソリューションも歓迎します。

また、この質問をIncanter Googleグループに投稿しましたが、これまでのところ回答はありません。

更新:私はJouniの回答を承認済みとしてマークしました。彼のコードを少しクリーンアップし、いくつかのベンチマークを追加した私自身の回答については、以下を参照してください。

4

5 に答える 5

20

私の最終的な解決策

すべてのテストの後、十分な速度で計算を行うための2つのわずかに異なる方法を見つけました。

最初にdiff、さまざまなタイプの戻り値で関数を使用しました。以下はベクトルを返すコードですが、二重配列((vec y)をyに置き換えます)とIncanter.matrix((replace( vec y)と行列y)。この関数は、Java配列のみに基づいています。これは、いくつかの余分な型のヒントが削除されたJouniのコードに基づいています。

もう1つのアプローチは、Java配列を使用して計算を行い、値を一時ベクトルに格納することです。タイミングからわかるように、関数を返して配列したくない場合、これはアプローチ1よりもわずかに高速です。これは関数に実装されていますdifft

したがって、選択は、データをどのように処理したくないかによって異なります。呼び出しで使用されたのと同じ型を返すように関数をオーバーロードするのが良いオプションだと思います。実際には、Java配列をベクトルの代わりにdiffに渡すと、約1秒速くなります。

さまざまな機能のタイミング:

ベクトルを返すdiff:

(time (def y (diff x)))
"Elapsed time: 4733.259 msecs"

Incanter.matrixを返すdiff:

(time (def y (diff x)))
"Elapsed time: 2599.728 msecs"

二重配列を返すdiff:

(time (def y (diff x)))
"Elapsed time: 1638.548 msecs"

difft:

(time (def y (difft x)))
"Elapsed time: 3683.237 msecs"

機能

(use 'incanter.stats)
(def x (vec (sample-normal 1e7)))

(defn diff [x]
  (let [y (double-array (dec (count x)))
        x (double-array x)] 
   (dotimes [i (dec (count x))]
     (aset y i
       (- (aget x (inc i))
                   (aget x i))))
   (vec y)))


(defn difft [x]
  (let [y (vector (range n))
        y (transient y)
        x (double-array x)]
   (dotimes [i (dec (count x))]
     (assoc! y i
       (- (aget x (inc i))
                   (aget x i))))
   (persistent! y))) 
于 2010-09-29T12:15:44.633 に答える
14

私のシステムでは、R コード (YMMV) よりも高速な Java 配列の実装を次に示します。パフォーマンスを最適化するときに不可欠なリフレクション警告を有効にし、y で型ヒントを繰り返し (def のものは aset には役立たないようです)、すべてをプリミティブな double 値にキャストすることに注意してください (dotimes により、 i はプリミティブ int です)。

(set! *warn-on-reflection* true)
(use 'incanter.stats)
(def ^"[D" x (double-array (sample-normal 1e7)))

(time
 (do
   (def ^"[D" y (double-array (dec (count x))))
   (dotimes [i (dec (count x))]
     (aset ^"[D" y
       i
       (double (- (double (aget x (inc i)))
                  (double (aget x i))))))))
于 2010-09-28T20:29:55.977 に答える
2

Bradford Cross のブログには、これに関する多くの投稿があります (彼は、リンク テキストに取り組んでいるスタートアップにこのようなものを使用しています。一般に、内部ループでのトランジェントの使用、( を介した) タイプ ヒンティング*warn-on-reflection*などはすべて速度向上に適しています。Clojure の喜びには、パフォーマンス チューニングに関する優れたセクションがあります。ぜひお読みください。

于 2010-09-28T19:38:52.770 に答える
1

これはトランジェントを使用したソリューションです-魅力的ですが遅いです。

(use 'incanter.stats)
(set! *warn-on-reflection* true)
(def x (doall (sample-normal 1e7)))

(time
 (def y
      (loop [xs x
             xs+ (rest x)
             result (transient [])]
        (if (empty? xs+)
          (persistent! result)
          (recur (rest xs) (rest xs+)
                 (conj! result (- (double (first xs+))
                                  (double (first xs)))))))))
于 2010-09-29T14:46:31.933 に答える
0

これまでのコメントはすべて、Clojureコードの高速化の経験があまりないように思われる人々によるものです。ClojureコードをJavaと同じように実行したい場合は、そのための機能を利用できます。ただし、ベクトル演算では、ColtやParallelColtなどの成熟したJavaライブラリを使用する方が理にかなっています。絶対的な最高のパフォーマンスの反復のためにJava配列を使用することは理にかなっているかもしれません。

@Shaneのリンクは古くなった情報でいっぱいなので、一見の価値はほとんどありません。また、コードが10倍遅いという@Shaneのコメントは、単に不正確です(そして、サポートされていないhttp://shootout.alioth.debian.org/u32q/compare.php?lang=clojureであり、これらのベンチマークは、 1.2.0または1.3.0-alpha1で可能な最適化の種類。少しの作業で、通常、4X-5XでClojureコードを取得するのは簡単です。それを超えると、通常、Clojureの高速パスについてのより深い知識が必要になります。Clojureはかなり若い言語であるため、何かが広く普及しているわけではありません。

Clojureは十分に高速です。しかし、Clojureは可変操作と可変データ構造を推奨しないため、高速化の方法を学ぶには少しの作業/調査が必要になります。

于 2010-09-28T17:40:26.490 に答える