优化Clojure中的数组变异

Hen*_*gon 6 arrays optimization clojure

阅读了一些关于这个主题的博客文章,我发现在Clojure中改变一个数组是这样的:

(defn m [xs ys] 
  (dotimes [i (count xs)] 
    (aset #^ints ys (int i) 
    (int (* (int 3) (int (aget #^ints xs (int i))))))))
Run Code Online (Sandbox Code Playgroud)

在哪里(def xs (into-array Integer/TYPE (range 1000000)))(def ys (into-array Integer/TYPE (range 1000000)))

根据Criterium,平均花费了14ms,而Java也是如此,

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

平均需要800us.**

我是否竭尽所能让事情变得更快,还有什么可以进入优化路径吗?

**我使用(report-result (bench (m xs ys )) :verbose)和时间使用Criterium计时(report-result (bench (. Test m xs ys)) :verbose)

Mar*_*nik 5

如果你想要速度,你需要进入原始世界而不是离开它直到你完成.从盒装开始Integer i然后在每个使用站点将其转换为基元是没有用的.也许你可以制作dotimes产品ints(类型提示声明i),但不确定.我所知道的是一个loop-recur带有循环变量的原始初始化器的构造:(loop [i (int 0)] ... (recur (unchecked-inc i)).另外,在你的例子中,你有(int 3).您需要let提前做到这一点,以免在每次迭代中重复拆箱.

顺便说一句,你可以(int-array (range 1000000))用来创建你的初始化数组,只(int-array 1000000)用于空数组.

UPDATE

从Clojure 1.3开始,随着对原语的增强支持,我上面写的大部分内容都不再适用了.dotimes已经使用原始算术,所以你需要编写才能获得完整的性能

(dotimes [i (alength ^ints xs)] 
  (aset ^ints ys i (unchecked-multiply (aget ^ints xs i) 3)
Run Code Online (Sandbox Code Playgroud)

基本上,没有int必要的构造函数,并使用unchecked-multiply.


dno*_*len 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)))))
Run Code Online (Sandbox Code Playgroud)