gpt4 book ai didi

java - 为什么这个 Clojure 代码与 Java 中的替代代码相比如此慢?

转载 作者:行者123 更新时间:2023-12-02 11:00:59 25 4
gpt4 key购买 nike

tl;dr:为什么下面的代码这么慢?

我尝试优化以下代码以提高速度;它的目的是在执行 n^2 操作时将一个数组(大小 n=1000)转换为另一个数组(相同大小),转换的细节现在并不重要。

由于我试图获得尽可能快的速度,因此我尽可能地使用 Java 原语;尽管如此,我得到的结果通常是每次“转换”调用大约 70 毫秒。当重写为 Java 时,平均调用时间 < 2 毫秒。

1) 哇,Java 很快

2) 哇,Clojure 很慢

3)你能向我解释一下为什么会这样吗?天真地,我希望 Clojure 生成一个代码字节码,应该非常接近 Java 的字节码,为什么事实并非如此?

4) 我不是 100% 确定如何使用这些 ^ints 提示,也许我弄错了?

(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)))
))

最佳答案

类似这样的事情应该更接近:

(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 版本,尚未完全推出),但在这里特别有用。

我改变的一些重要的事情:

  • 我替换了 areduce - areduce 的问题是它不知道数组的原始类型。通过编写自己的内部循环,您可以利用这些提示。也许可以暗示 areduce 的主体来使其工作,但在进行原始数学时我倾向于更喜欢显式循环。
  • 我在需要时使用 ^long 提示,因为 Clojure 仅支持原始长参数/返回值,而不支持 int。将根据需要插入正确的原始转换。如果需要的话,有一些函数可以获取原始 int 溢出语义。
  • rem 可以进行 mod 不能进行的原始操作。我认为这里的语义与你正在做的事情是相同的。这是大部分拳击的来源。
  • 我使用 int-array 而不是您制作数组的其他方式。我认为这对于您正在做的事情来说是最好的方式。

您可以将两个循环合并为一个循环,进一步提高性能。

关于java - 为什么这个 Clojure 代码与 Java 中的替代代码相比如此慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29683923/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com