该功能的蒸发时间交替为85纳米秒和10秒(!?)



目标

我试图弄清楚为什么我创建的函数items-staged-f的求值时间如此之长和如此之短。

你说奇怪吗

我说"奇怪"是因为:

  • (time (items-staged-f))产生1.313 msecs
  • (time (items-staged-f))第二次生成0.035 msecs(这并不奇怪,因为结果是一个惰性序列,并且它一定已被存储(
  • Criterium基准测试系统报告它需要85.149767 ns(这并不奇怪(

然而。。。

  • 在REPL中实际评估(items-staged-f)所需的时间约为10秒。这甚至在它打印任何东西之前。我最初认为它需要这么长时间,可能是因为它正在准备打印到REPL,因为它是一个漫长而复杂的数据结构(以惰性序列嵌套的映射和向量(,但奇怪的是,直到10秒后,结果才开始打印出来,而这(据说(需要85纳秒。可能是它在预先计算如何打印数据结构吗
  • (time (last (items-staged-f)))产生10498.16 msecs(尽管这可能在20秒左右变化(,原因可能与上述相同

现在对于代码

函数items-staged-f的目标是可视化需要做什么,以便对会计数据库中的库存项目进行一些必要的更改。

items-staged-f中引用的不熟悉的函数如下所示。

(defn items-staged-f []
  (let [items-0 (lazy-seq (items-staged :items))
        both-types? #(in? % (group+line-items))
        items-from-group #(get items-0 %)
        replace-subgroups
          (fn [[g-item l-items :as group]]
            (let [items-in-both
                    (->> l-items
                         (map :item)
                         (filter both-types?))]
              (->> (concat
                        (remove #(in? (:item %)  items-in-both) l-items)
                        (mapcat items-from-group items-in-both))
                   (into [])
                   (assoc group 1))))
        replaced (map replace-subgroups items-0)]
   replaced))

items-staged是输出items-staged-f操作的原始数据的函数。(items-staged :items)输出具有字符串关键字(组项目(的映射,该字符串关键字的值是映射的矢量(子项目列表(:

{"786M" ; this is a group item
 ; below are the sub-items of the above group item
 [{:description "Signature Collection Item", :item "4X1"}
  {:description "Cookies, Inc. Paper Wrapped", :item "65G7"}
  {:description "MyChocolate 5 oz.", :item "21F"}]}

请注意,items-staged-f的输出在结构上与items-staged的输出几乎相同,只是它是一个向量的惰性序列,而不是具有哈希映射条目的哈希映射,正如在哈希映射上调用map函数所期望的那样

in?是一个谓词,用于检查对象是否在给定集合中。例如,(in? 1 [1 2 3])的计算结果为true

group+line-items是一个函数,它输出我希望消除的某些重复项的惰性序列。例如,(group+line-items)评估为:("428X" "41SF" "6998" "75D22")

备注

VisualVM 1.3.8表示clojure.lang.Reflector.getMethods((的时钟为28700毫秒(51.3%(,clojure.lang.LineNumberingPushbackReader.read(((这是因为REPL中的输出吗?(为9000毫秒(16.2%(,clojure.lang.RT.nthhFrom((为7800毫秒(13.9%(。

但是,当我在REPL中单独评估懒惰序列(nth items-staged-f n)的每个元素时,只有clojure.lang.LineNumberingPushbackReader.read((会上升。调用以32为增量递增,这是惰性seq分块大小。其他方法/函数所花费的时间可以忽略不计。

另一个考虑因素是items-staged是一个最终从Excel文件(通过Apache POI读取(中提取数据的函数。然而,Excel文件中的原始数据存储为var,所以这不应该是一个问题,因为它在被存储之前只会计算一次(我认为(。


感谢您的帮助

附录

一旦我使用doall强制实现懒惰序列(我认为它正在实现(,Criterium现在说函数需要11.370356 sec来评估,不幸的是,这是有道理的。一旦我重构,我就会重新发布。

根据定义,Lazy序列仅在需要时计算其元素。打印到REPL或请求last元素都强制实现。对生成延迟序列的函数调用进行计时不会。

(defn slow-and-lazy [] (map #(do (Thread/sleep 1000) (inc %)) (range 10)))
user=> (time (slow-and-lazy))
"Elapsed time: 0.837002 msecs"
(1 2 3 4 5 6 7 8 9 10) ; printed 10 seconds later
user=> (time (doall (slow-and-lazy)))
"Elapsed time: 10000.205709 msecs"
(1 2 3 4 5 6 7 8 9 10)

(time (slow-and-lazy))的情况下,slow-and-lazy快速返回未实现的延迟序列,time完成,打印经过的时间,并将这种情况下的未实现结果传递给REPL。然后,REPL尝试打印序列。为了做到这一点,它必须实现序列。


话虽如此,10秒对计算机来说是永恒的,因此这确实需要检查/分析。我建议将代码重构为更小的自包含函数。特别是,数据应该作为参数传入。一旦你确定了瓶颈(用doall强制实现的时间!(,然后考虑发布一个新问题。如果不能确切地说出这个代码发生了什么,或者items-staged中的IO是否是真正的瓶颈,那么似乎还有改进的空间。

相关内容

  • 没有找到相关文章

最新更新