6

この件に関するいくつかのブログ投稿を読んだ後、Clojure で配列を次のように変更することがわかりました。

(defn m [xs ys] 
  (dotimes [i (count xs)] 
    (aset #^ints ys (int i) 
    (int (* (int 3) (int (aget #^ints xs (int i))))))))

どこ(def xs (into-array Integer/TYPE (range 1000000)))(def ys (into-array Integer/TYPE (range 1000000)))

Criterium によると平均 14 ミリ秒かかりましたが、Java は同じことを行いました。

public static int[] m(int[] x, int[] y)
{
  for(int i=0; i<x.length; i++)
    y[i] = 3*x[i];
  return y;
}

平均800usかかります。**

物事を迅速に進めるためにできる限りのことを行っていますか? また、最適化の道筋をたどることができますか?

** Criterium を(report-result (bench (m xs ys )) :verbose)と で使用してこれらのタイミングを計りました(report-result (bench (. Test m xs ys)) :verbose)

4

2 に答える 2

5

Clojure 1.3でこれを試してください:

(set! *unchecked-math* true)

(defn m [^longs xs ^longs ys]
  (dotimes [i (count xs)]
    (aset ys i
      (* 3 (aget xs i)))))
于 2012-04-16T15:39:40.047 に答える
5

速度が必要な場合は、プリミティブの世界に入り、完了するまで離れないようにする必要があります。ボックス化されたものから始めて、Integer i各使用場所でそれをプリミティブに変換しても意味がありません。たぶん、あなたはdotimesintを生成することができます(宣言のタイプヒントi)が、確かではありません. 私が知っているのは、loop-recurループ vars: のプリミティブ初期化子を持つ構成です(loop [i (int 0)] ... (recur (unchecked-inc i))。また、あなたの例では(int 3). let各反復でボックス化解除を繰り返さないように、事前にそれを行う必要があります。

ところで、(int-array (range 1000000))初期化された配列を作成(int-array 1000000)し、空の配列だけを作成するために使用できます。

アップデート

プリミティブのサポートが強化された Clojure 1.3 の時点で、上で書いたことのほとんどは適用されなくなりました。dotimesはすでにプリミティブ演算を使用しているため、完全なパフォーマンスを得るために記述する必要があるのは

(dotimes [i (alength ^ints xs)] 
  (aset ^ints ys i (unchecked-multiply (aget ^ints xs i) 3)

基本的に、intコンストラクタは必要なく、unchecked-multiply.

于 2012-04-16T08:04:45.873 に答える