如何使用Tufte分析多线程的clojure结果

时间:2016-11-16 21:58:32

标签: clojure profiling

Tutfe是一个分析库,非常适合理解运行时的组合,尤其是在复杂的调用堆栈中。

当我在我的项目中运行它时,我期望性能昂贵的一段代码没有在结果中注册成为高犯罪者,这导致发现Tufte(合理地)只捕获了线程 - 本地结果。

文档表明它使用{:dynamic? true}选项在其他线程上内置了对仪器累积时间的约束,但似乎需要更多来捕获在其他线程上累积的时间,例如那些由pmap

发起

这是原始的Tufte演示,引入了一些多线程:

(require '[taoensso.tufte :as tufte :refer (defnp p profiled profile)])

;; We'll request to send `profile` stats to `println`:
(tufte/add-basic-println-handler! {})

;;; Let's define a couple dummy fns to simulate doing some expensive work
(defn get-x [] (Thread/sleep 500)             "x val")
(defn get-y []
  (pmap
    #(do
        (Thread/sleep 500)
        (print %))
    (range 10)))

;; How do these fns perform? Let's check:

(profile ; Profile any `p` forms called during body execution
  {:dynamic? true} ; Profiling options; we'll use the defaults for now
  (dotimes [_ 5]
    (p :get-x (get-x))
    (p :get-y (get-y))))

输出表明println语句强制线程进行评估。然而,结果中没有显示得到的累积时间:

2187306594=> nil
=> #{:basic-println}
=> #'user/get-x
=> #'user/get-y
8904365271703695842110783956243415760829=> nil

       pId      nCalls       Min        Max       MAD      Mean   Time% Time
    :get-x           5  500.52ms   504.84ms    1.44ms  502.44ms     100 2.51s
    :get-y           5   90.67μs   581.91μs   162.2μs  269.29μs       0 1.35ms
Clock Time                                                          100 2.51s
Accounted Time                                                      100 2.51s

1 个答案:

答案 0 :(得分:3)

答案:懒惰的初始化。虽然数据正在打印到屏幕上,但是(profile...)形式的结尾是在get-y之外的间谍形式之外。

这会产生同样的效果:

(profile ; Profile any `p` forms called during body execution
  {:dynamic? true} ; Profiling options; we'll use the defaults for now
  (dotimes [_ 5]
    (p :get-x (get-x))
    (doall (p :get-y (get-y)))))

30167894521045768392530798241651268940371023657894=> nil

           pId      nCalls       Min        Max       MAD      Mean   Time% Time
        :get-x           5  500.07ms   504.58ms    1.41ms  503.08ms      50 2.52s
        :get-y           5   80.25μs   126.18μs   15.98μs  104.84μs       0 524.18μs
    Clock Time                                                          100 5.03s
Accounted Time                                                           50 2.52s

然而,这个实现了get-y配置文件中的延迟序列:

(profile ; Profile any `p` forms called during body execution
  {:dynamic? true} ; Profiling options; we'll use the defaults for now
  (dotimes [_ 5]
    (p :get-x (get-x))
    (p :get-y (doall (get-y)))))

12037645987105892436354169872031089546721058729634=> nil

           pId      nCalls       Min        Max       MAD      Mean   Time% Time
        :get-x           5  502.54ms   504.71ms   705.6μs   503.5ms      50 2.52s
        :get-y           5  501.69ms   505.68ms    1.05ms  503.06ms      50 2.52s
    Clock Time                                                          100 5.03s
Accounted Time                                                          100 5.03s

了解剖析经验非常重要,因为在处理惰性序列时,您会意识到它们的使用方式,而不是序列本身:

(profile ; Profile any `p` forms called during body execution
  {:dynamic? true} ; Profiling options; we'll use the defaults for now
  (dotimes [_ 5]
    (p :get-x (get-x))
    (p ::realize-y
       (doall (p :get-y (get-y))))))

06538947123410695278450678913223071958645126380479=> nil

            pId      nCalls       Min        Max       MAD      Mean   Time% Time
:user/realize-y           5  503.29ms   504.86ms  458.12μs  504.37ms      50 2.52s
         :get-x           5  500.13ms   505.06ms     1.4ms  503.64ms      50 2.52s
         :get-y           5    86.0μs     1.15ms  331.81μs  322.94μs       0 1.61ms
     Clock Time                                                          100 5.04s
 Accounted Time                                                          100 5.04s