Skip to content

Commit e0eb607

Browse files
committed
Very WIP plugin for profiling parallel execution.
Internal refers to whether there were delays between running tests in the same thread. It doesn't measure waiting for I/O or many kinds of contention. External refers to how much of the overall time the thread spent. Why do I think this is useful? Well, if you've parallelized your test suite and you end up with a thread that runs from time 0 to time 100 and another that runs from time 1 to time 10, the burden is heavily on the first thread, suggsting the load is not well-balanced. Both may have good internal utilization, but the second has poor external globalization. Part of the reason this is WIP is that there may be better (ideally standard) terms for "internal" and "external". There are some calculation issues and hacks: * Some testables are missing the ::start and ::end for some reason. I don't think this should happen? * I don't think this version counts the number of threads accurately.
1 parent 042b1b8 commit e0eb607

File tree

1 file changed

+90
-0
lines changed

1 file changed

+90
-0
lines changed

Diff for: src/kaocha/plugin/alpha/parallel_profiling.clj

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
(ns kaocha.plugin.alpha.parallel-profiling
2+
(:require [clojure.java.io :as io]
3+
[clojure.string :as str]
4+
[kaocha.plugin :as plugin :refer [defplugin]]
5+
[kaocha.output :as output]
6+
[kaocha.testable :as testable])
7+
(:import java.time.Instant
8+
java.time.temporal.ChronoUnit))
9+
10+
(defn start [testable]
11+
(assoc testable ::start (Instant/now)))
12+
13+
(defn stop [testable]
14+
(cond-> testable
15+
true (assoc ::end (Instant/now))
16+
(::start testable)
17+
(assoc ::duration (.until (::start testable)
18+
(Instant/now)
19+
ChronoUnit/NANOS))))
20+
21+
(defplugin kaocha.plugin.alpha/parallel-profiling
22+
(config [config]
23+
(output/warn "Warning: The kaocha.plugin.alpha/parallel-profiling plugin is in an alpha status, like the parallel feature in general.")
24+
config
25+
)
26+
(pre-run [test-plan]
27+
(start test-plan))
28+
29+
(post-run [test-plan]
30+
(stop test-plan))
31+
32+
(pre-test [testable _]
33+
(start testable))
34+
35+
(post-test [testable _]
36+
(stop testable))
37+
38+
(cli-options [opts]
39+
(conj opts
40+
[nil "--[no-]-parallel-profiling" "Show slowest tests of each type with timing information."]
41+
#_[nil "--profiling-count NUM" "Show this many slow tests of each kind in profile results."
42+
:parse-fn #(Integer/parseInt %)]))
43+
44+
(config [{:kaocha/keys [cli-options] :as config}]
45+
(assoc config
46+
::parallel-profiling? (:parallel-profiling cli-options (::parallel-profiling? config true))
47+
#_#_::count (:profiling-count cli-options (::count config 3))))
48+
49+
(post-summary [result]
50+
(when (::parallel-profiling? result)
51+
(let [tests (->> result
52+
testable/test-seq
53+
(remove ::testable/load-error)
54+
(remove ::testable/skip))
55+
#_#_types (group-by :kaocha.testable/type tests)
56+
threads (group-by #(get-in % [ :kaocha.testable/thread :name]) tests)
57+
total-duration (::duration result)
58+
#_#_limit (::count result)
59+
]
60+
(->> (for [[thread tests] threads
61+
:when (and thread
62+
(some (complement nil?) (map ::start tests))
63+
(some (complement nil?) (map ::end tests))) ;temporary fix until I figure out why these keys are sometimes missing.
64+
:let [start (reduce min (map ::start tests))
65+
end (reduce max (map ::end tests))
66+
span-ns (.until start
67+
end
68+
ChronoUnit/NANOS)
69+
span (cond
70+
(> span-ns 1e8) (format "%.2f s" (/ span-ns 1e9))
71+
(> span-ns 1e5) (format "%.2f ms" (/ span-ns 1e9))
72+
:else (str span-ns " ns"))
73+
utilization (float (* 100 (/ (reduce + (map ::duration tests)) span-ns)))
74+
utilization-external (float (* 100 (/ (reduce + (map ::duration tests)) total-duration)))
75+
]]
76+
77+
(println (format "Thread %s ran from %s to %s (%s), utilizing %.2f%% (internal) and %.2f%% (external)"
78+
thread start end span utilization utilization-external)))
79+
(flatten)
80+
(apply str)
81+
print)
82+
83+
84+
(println (format "\n%d threads ran in %f seconds." (count threads) (float (/ total-duration 1e9))))
85+
(flush)
86+
87+
))
88+
result))
89+
90+
(.until (Instant/now) (Instant/now) ChronoUnit/NANOS)

0 commit comments

Comments
 (0)