我最近一直在尝试使用Clojure。我尝试编写自己的map函数(实际上是两个)并将它们与内置函数计时。但是,我的地图功能比内置功能慢得多。我想知道如何更快地实现我的实现。它应该给我一些关于我编写的性能调优Clojure算法的见解。第一个函数(my-map)使用recur进行递归。第二个版本(my-map-loop)使用loop / recur,它比简单地使用recur快得多。
(defn my-map
([func lst] (my-map func lst []))
([func lst acc]
(if (empty? lst)
acc
(recur func (rest lst) (conj acc (func (first lst)))))))
(defn my-map-loop
([func lst]
(loop [acc []
inner-lst lst]
(if (empty? inner-lst)
acc
(recur (conj acc (func (first inner-lst))) (rest inner-lst))
))))
(let [rng (range 1 10000)]
(time (map #(* % %) rng))
(time (my-map #(* % %) rng))
(time (my-map-loop #(* % %) rng)))
这些是我得到的结果 -
"Elapsed time: 0.084496 msecs"
"Elapsed time: 14.132217 msecs"
"Elapsed time: 7.324682 mess"
更新
在resueman指出我错误地计时后,我将功能改为:
(let [rng (range 1 10000)]
(time (doall (map #(* % %) rng)))
(time (doall (my-map #(* % %) rng)))
(time (doall (my-map-loop #(* % %) rng)))
nil)
这些是新结果:
"Elapsed time: 9.563343 msecs"
"Elapsed time: 12.320779 msecs"
"Elapsed time: 5.608647 mess"
"Elapsed time: 11.103316 msecs"
"Elapsed time: 18.307635 msecs"
"Elapsed time: 5.86644 mess"
"Elapsed time: 10.276658 msecs"
"Elapsed time: 10.288517 msecs"
"Elapsed time: 6.19183 mess"
"Elapsed time: 9.277224 msecs"
"Elapsed time: 13.070076 msecs"
"Elapsed time: 6.830464 mess"
看起来我的第二个实现是最快的。无论如何,我仍然想知道是否有进一步优化它的方法。
答案 0 :(得分:3)
您的实现和内置的clojure实现之间存在一个主要的根本区别:内置版本是懒惰的。实际上,您需要比较简单地将包装器放在集合周围的时间,以及转换每个项目并将它们放入新集合的时间。基本上,无论你的代码有多快,你都没有机会赢得那场比赛。
如果您将比较更改为:
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir_path),
RecursiveIteratorIterator::SELF_FIRST);
foreach($iterator as $file) {
if($file->isDir()) {
echo $file->getRealpath();
}
}
时代更加接近。在我用它做的运行中,你的第二个实现在任何地方做了〜两倍长,再到一半再快一点。可能有很多优化可以做些改进,但主要问题是你的测试,而不是你的功能。
如果您真的对与clojure / core竞争感兴趣,最好看的地方是the source
(let [rng (range 1 10000)] (time (doall (map #(* % %) rng))) (time (doall (my-map #(* % %) rng))) (time (doall (my-map-loop #(* % %) rng))) nil)
^已编辑删除除了您要与之比较的代码之外的所有内容。
答案 1 :(得分:0)
有很多东西可以利用来获得更快的地图:瞬态(对于你的累加器),chunked seqs(对于源但只在你需要延迟输出时才有意义),可简化的集合(对于源再次)并且更熟悉核心功能(有一个mapv)。
如果仅检查您的JVM优化是否受限(这是time
的默认值),您还应该考虑使用Criterium而不是lein
。
=> (let [rng (range 1 10000)]
(quick-bench (my-map-loop #(* % %) rng))
(quick-bench (into [] (map #(* % %)) rng)) ; leveraging reducible collections and transients
(quick-bench (mapv #(* % %) rng))) ; existing core fn
(output elided to keep only the means)
Execution time mean : 776,364755 µs
Execution time mean : 409,737852 µs
Execution time mean : 456,071295 µs
有趣的是,mapv
并不比(into [] (map #(* % %)) rng)
快,这是优化这些类型计算的通用方法。