2

tl;dr : 以下のコードが遅いのはなぜですか?

速度のために次のコードを最適化しようとしています。その目的は、n^2 操作を実行しながら、1 つの配列 (サイズ n=1000) を別の配列 (同じサイズ) に変換することです。変換の詳細は今のところ重要ではありません。

私は可能な限り速度を上げようとしているので、可能な限り Java プリミティブを使用しています。それでも、私が得たのは通常、1 回の「変換」呼び出しあたり約 70 ミリ秒です。Java に書き直すと、平均呼び出しにかかる時間は 2 ミリ秒未満です。

1) うわー、Java は速い

2) うわー、Clojure は遅い

3) どうしてそうなったのか説明してもらえますか? 単純に、Clojure が Java にかなり近いコード バイトコードを生成することを期待していますが、そうでないのはなぜでしょうか?

4) ^ints ヒントの使い方が 100% わからないのですが、間違っているのでしょうか?

(defn transform [^ints src]
  (let [res ^ints (make-array Integer/TYPE 1000)]
    (loop [x (int 0)]
      (if (= 1000 x) res
        (do
          (aset res x (areduce src i ret (int 0) 
            (+ ret (* (mod x 2) (mod i 3) (aget src i)))))
          (recur (inc x)))))))

(let [arr (into-array Integer/TYPE (range 1000))]
  (doseq [_ (range 20)]
      (println (time (transform arr)))
  ))
4

1 に答える 1

15

このようなものはもっと近いはずです:

(set! *warn-on-reflection* true)
(set! *unchecked-math* :warn-on-boxed)

(defn inner ^long [^ints src ^long x]
  (let [len (alength src)]
    (loop [i 0 acc 0]
      (if (< i len)
        (recur (inc i) (+ acc (* (rem x 2) (rem i 3) (aget src i))))
        acc))))

(defn transform [^ints src]
  (let [res ^ints (int-array 1000)]
    (loop [x 0]
      (if (= 1000 x) 
        res
          (do
            (aset res x (inner src x))
            (recur (inc x)))))))

(defn bench []
  (let [arr (int-array (range 1000))]
    (doseq [_ (range 20)]
      (println (time (transform arr))))))

上部の設定は、エラーを検出するのに役立ちます。:warn-on-boxed は Clojure 1.7 で新しく追加されました (現在は beta1 で、まだ完全にはリリースされていません) が、ここでは特に便利です。

私が変更したいくつかの重要なこと:

  • 私はreduceを置き換えました-reduceの問題は、配列のプリミティブ型を認識していないことです。独自の内部ループを作成することで、ヒントを活用できます。それを機能させるためにreduceの本体をほのめかすことは可能かもしれませんが、原始的な数学を行うときは明示的なループを好む傾向があります。
  • Clojure は int ではなくプリミティブな long param/return のみをサポートするため、必要に応じて ^long ヒントを使用しています。必要に応じて、正しいプリミティブ変換が挿入されます。必要に応じて、プリミティブな int オーバーフロー セマンティクスを取得する関数があります。
  • rem は、mod ができないプリミティブ ops に移動できます。ここであなたがしていることのセマンティクスは同じだと思います。これがボクシングのほとんどの源でした。
  • 配列を作成する他の方法ではなく、int-array を使用しています。これがあなたのやっていることにとって最善の方法だと思います。

2 つのループを 1 つに結合して、パフォーマンスをさらに向上させることができます。

于 2015-04-16T19:30:33.870 に答える