ruby – 用Clojure编写的阶乘函数的低性能
发布时间:2020-12-16 22:44:38 所属栏目:百科 来源:网络整理
导读:我是Clojure的新手.在试验它时,我写了一个函数来计算n!.我的Clojure代码如下: (defn factorial [n] (reduce * (biginteger 1) (range 1 (inc n)))) 然后我在repl中运行以下内容. (time (factorial 100)) 这就是结果: "Elapsed time: 0.50832 msecs"933262
我是Clojure的新手.在试验它时,我写了一个函数来计算n!.我的Clojure代码如下:
(defn factorial [n] (reduce * (biginteger 1) (range 1 (inc n)))) 然后我在repl中运行以下内容. (time (factorial 100)) 这就是结果: "Elapsed time: 0.50832 msecs" 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000N 然后我在Ruby中创建了一个类似的解决方案 def factorial(n) start = Time.now.to_f (2..n).inject(1) { |p,f| p * f } finish = Time.now.to_f time_taken = finish - start puts "It took: #{(time_taken * 1000)} msecs" end with irb我跑了阶乘(100) It took: 0.06556510925292969 msecs => 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 Ruby版本的性能似乎要大得多,尽管我见过大多数证据表明Clojure应该具有卓越的性能.有什么我误解的东西或我的Clojure解决方案的某些元素会减慢它的速度吗? 解决方法
Miro-benchmarkinging经常会产生误导,一般而言,很难做到正确.在clojure中获得合理接近的最简单方法(我发现是标准库(感谢Hugo!).如果我从一个简单的循环计算阶乘的丑陋版本开始,我得到大约3 ns.
user> (defn loopy-fact [x] (loop [y x answer-so-far 1] (if (pos? y) (recur (dec y) (*' answer-so-far y)) answer-so-far))) #'user/loopy-fact user> (loopy-fact 100) 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000N 然后让我们对它进行基准测试: user> (criterium.core/bench #(loopy-fact 100)) WARNING: Final GC required 11.10521514596218 % of runtime WARNING: Final GC required 1.069604210579865 % of runtime Evaluation count : 12632130300 in 60 samples of 210535505 calls. Execution time mean : 2.978360 ns Execution time std-deviation : 0.116043 ns Execution time lower quantile : 2.874266 ns ( 2.5%) Execution time upper quantile : 3.243399 ns (97.5%) Overhead used : 1.844334 ns Found 4 outliers in 60 samples (6.6667 %) low-severe 2 (3.3333 %) low-mild 2 (3.3333 %) Variance from outliers : 25.4468 % Variance is moderately inflated by outliers 如果我们通过使用普通的Clojure样式,使用map和reduce,使代码看起来更好,并且没有努力使它快速. user> (defn mapy-fact [x] (reduce *' (range 1 (inc x))) #'user/mapy-fact user> (mapy-fact 100) 933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000N 现在让我们看看它是如何比较的: user> (criterium.core/bench #(mapy-fact 100)) Evaluation count : 8674569060 in 60 samples of 144576151 calls. Execution time mean : 5.208031 ns Execution time std-deviation : 0.265287 ns Execution time lower quantile : 5.032058 ns ( 2.5%) Execution time upper quantile : 5.833466 ns (97.5%) Overhead used : 1.844334 ns Found 4 outliers in 60 samples (6.6667 %) low-severe 1 (1.6667 %) low-mild 3 (5.0000 %) Variance from outliers : 36.8585 % Variance is moderately inflated by outliers 它有点慢,但只慢了两纳秒. 这比你在测试中看起来要好得多,因为标准运行该函数的次数足以让JVM的Hotspot编译器绕过来编译它并内联所有部分.这说明了为什么微基准测试在JVM上会产生误导.你几乎肯定会对这种情况保持标准. PS:*’是“自动提升”乘法运算符,它会根据需要将其类型提升为大整数或大小数 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |