Java Clojure的平均亮度非常慢
作为Clojure的新手,我想计算(很多)jpg图像的平均亮度。为此,我使用Java中的Java Clojure的平均亮度非常慢,java,image,clojure,Java,Image,Clojure,作为Clojure的新手,我想计算(很多)jpg图像的平均亮度。为此,我使用Java中的ImageIO/read将图像加载到内存中,提取其后面的字节缓冲区并应用平均值 (defn brightness "Computes the average brightness of an image." [^File file] (-> file ImageIO/read .getRaster .getDataBuffer .getData byt
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)
)
虽然这确实给出了正确的结果,但速度非常慢。2000万像素的图像大约需要10到20秒。磁盘访问不是问题所在。从玩弄时间
,罪魁祸首似乎是字节点
转换。只需将这个bytepoint
映射到字节数组就会消耗8GB的内存,并且不会在REPL中终止
为什么会这样?人们能做些什么呢
PS:我知道可以使用其他编程语言、库、多线程或更改算法。我的观点是,上面的Clojure代码应该快得多,我想了解它为什么不快。如果您想在java中快速完成,那么您可以使用这些选项(最好使用所有选项):
int rgb = somePixelColor;
int b = rgb & 0xFF;
int g = (rgb>>8) & 0xFF;
int r = (rgb>>16) & 0xFF;
int sillyBrightness = (r + g + b)/3; // because each color should have a weight for calculating brightness, there are some models of that.
您基本上是在一个非常紧密的循环中运行大量管道,例如装箱、转换、使用chuncked惰性序列等。。您从现代CPU中获得的许多好处都会立即消失;例如预加载缓存线、分支预测等 这种循环(计算和)在更直接的计算形式上实现得更好,例如clojure
loop
construct,形式如下:
(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)))))
(定义获取和[^bytes数据]
(设[m(长度数据)]
(循环[idx 0和0]
(如果(
这是未经测试的,因此您可能需要对其进行调整,但它显示了以下几点:
您还可以使用其他形式,这些形式的性能可能会更好,例如具有内部可变状态的
dotimes
(例如大小为1的长向量),如果您确实需要压缩性能,那么您最好用java编写一个小方法;) 除了@shlomi的回答之外:
您还可以使用areduce
函数使其更简洁(可能更快):
(defn get-sum-2 [^bytes data]
(/ (areduce data i res 0
(unchecked-add res (bit-and (aget data i) 0xff)))
(alength data)))
除了上述良好信息外,您可能还对HipHip库感兴趣,该库设计用于操作Clojure中的基元值数组: 以下是自述文件中关于计算基元数组的平均值和标准偏差的示例:
(defn std-dev [xs]
(let [mean (dbl/amean xs)
square-diff-sum (dbl/asum [x xs] (Math/pow (- x mean) 2))]
(/ square-diff-sum (dbl/alength xs))))
(defn covariance [xs ys]
(let [ys-mean (dbl/amean ys)
xs-mean (dbl/amean xs)
diff-sum (dbl/asum [x xs y ys] (* (- x xs-mean) (- y ys-mean)))]
(/ diff-sum (dec (dbl/alength xs)))))
(defn correlation [xs ys std-dev1 std-dev2]
(/ (covariance xs ys) (* std-dev1 std-dev2)))
不,我想在Clojure做。(1) ImageIO占用了大约1%的计算时间,因此将速度提高30倍(该库无法通过SATA限制实现)也无济于事。(2) 显而易见,但在Clojure中实现起来似乎并不那么容易。也应该是不必要的。(3) 多线程并没有使任何事情更快,只是更分散。为什么要避免浮点计算?否则,Clojure将使用更慢、更不实用的(此处)理性。原始问题并没有提到在Clojure中必须这样做。