目标
我试图弄清楚为什么我创建的函数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是否是真正的瓶颈,那么似乎还有改进的空间。