gpt4 book ai didi

java - Clojure 的平均亮度非常慢

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:35:30 24 4
gpt4 key购买 nike

作为 Clojure 的新手,我想计算(很多)jpg 图像的平均亮度。为此,我使用 Java 中的 ImageIO/read 将图像加载到内存中,提取其后面的字节缓冲区并应用平均值。

(defn brightness
"Computes the average brightness of an image."
[^File file]
(-> file
ImageIO/read
.getRaster
.getDataBuffer
.getData
byteaverage))

这里是平均值

(defn byteaverage
[numbers]
(/ (float
(->> numbers
(map bytetoint)
(apply +)))
(count numbers))
)

需要考虑到字节在 Java 中是有符号的,需要先转换为足够大的整数。

(defn bytetoint
[b]
(bit-and b 0xFF)
)

虽然这确实给出了正确的结果,但速度非常慢。 20 兆像素的图像大约需要 10 到 20 秒。磁盘访问不是问题。从玩弄 time 来看,罪魁祸首似乎是 bytetoint 转换。仅将此 bytetoint 映射到字节数组会占用 8 GB 内存,并且不会在 REPL 中终止。

为什么会这样,我们可以做些什么?

PS:我知道可以使用其他编程语言、库、多线程或更改算法。我的观点是上面的 Clojure 代码应该快得多,我想知道为什么不是这样。

最佳答案

你基本上是在一个非常紧凑的循环中运行大量的管道,比如装箱、转换、使用分块的惰性序列等。你从现代 cpus 中获得的很多好处就飞到窗外了;例如预加载缓存行,分支预测等。

这种循环(计算总和)可以通过更直接的计算形式更好地实现,例如 clojure loop 构造,形式如下:

(defn get-sum [^bytes data]
(let [m (alength data)]
(loop [idx 0 sum 0]
(if (< idx m)
(recur (inc idx) (unchecked-add sum (bit-and (aget data idx) 0xff)))
(/ sum m)))))

这是未经测试的,因此您可能需要对其进行调整,但它表明了一些事情:

  1. 使用类型提示数组访问
  2. 使用非常高效的直接循环
  3. 在实际循环中使用“整数”(长)数学运算,并且只在最后除法
  4. 使用未经检查的数学,这大大提高了“紧密循环”中的性能

编辑

您也可以使用其他形式,这可能会表现得更好,例如 dotimes 具有内部可变状态(比如大小为 1 的长 vector ),如果您确实需要提高性能, 但到那时,你还不如用 java 写一个小方法 ;)

关于java - Clojure 的平均亮度非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43535776/

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