-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add :parallel-threads option to set thread pool size #274
base: parallelize
Are you sure you want to change the base?
Changes from all commits
00f2135
b45731b
f434921
b7f95ed
93405e5
8069a9a
249e657
df07326
c9c4b95
d52eb38
0990efc
3445607
c1615a4
0c87a16
0d82ed2
56c1dc3
47627b7
3c68292
ae0863b
2502632
152f6f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
|
||
(ns benchmark | ||
(:require [criterium.core :as c]) | ||
(:import [java.util.concurrent Executors ]) | ||
) | ||
|
||
(def thread-pool (Executors/newFixedThreadPool 10)) | ||
|
||
(defn math-direct [] | ||
(+ 1 1)) | ||
|
||
(defn math-future [] | ||
(deref | ||
(future (+ 1 1)))) | ||
|
||
(defn math-thread [] | ||
(let [result (atom nil)] | ||
(doto (Thread. (fn [] (reset! result (+ 1 1)))) | ||
(.start) | ||
(.join)) | ||
@result)) | ||
|
||
(defn math-threadpool [] | ||
(let [result (atom nil)] | ||
(.get (.submit thread-pool (fn [] (reset! result (+ 1 1))) )) | ||
@result)) | ||
|
||
(defn math-threadpool-no-atom [] | ||
(.get (.submit thread-pool (fn [] (+ 1 1)) ))) | ||
|
||
|
||
(c/bench (math-direct) ) | ||
; (out) Evaluation count : 6215391600 in 60 samples of 103589860 calls. | ||
; (out) Execution time mean : 2,015262 ns | ||
; (out) Execution time std-deviation : 0,497743 ns | ||
; (out) Execution time lower quantile : 1,442374 ns ( 2,5%) | ||
; (out) Execution time upper quantile : 3,392990 ns (97,5%) | ||
; (out) Overhead used : 7,915626 ns | ||
; (out) | ||
; (out) Found 5 outliers in 60 samples (8,3333 %) | ||
; (out) low-severe 3 (5,0000 %) | ||
; (out) low-mild 2 (3,3333 %) | ||
; (out) Variance from outliers : 94,6147 % Variance is severely inflated by outliers | ||
|
||
(c/bench (math-future) ) | ||
; (out) Evaluation count : 3735420 in 60 samples of 62257 calls. | ||
; (out) Execution time mean : 16,635809 µs | ||
; (out) Execution time std-deviation : 1,104338 µs | ||
; (out) Execution time lower quantile : 15,397518 µs ( 2,5%) | ||
; (out) Execution time upper quantile : 19,751883 µs (97,5%) | ||
; (out) Overhead used : 7,915626 ns | ||
; (out) | ||
; (out) Found 6 outliers in 60 samples (10,0000 %) | ||
; (out) low-severe 3 (5,0000 %) | ||
; (out) low-mild 3 (5,0000 %) | ||
; (out) Variance from outliers : 50,0892 % Variance is severely inflated by outliers | ||
|
||
(c/bench (math-thread)) | ||
|
||
; (out) Evaluation count : 774420 in 60 samples of 12907 calls. | ||
; (out) Execution time mean : 82,513236 µs | ||
; (out) Execution time std-deviation : 5,706987 µs | ||
; (out) Execution time lower quantile : 75,772237 µs ( 2,5%) | ||
; (out) Execution time upper quantile : 91,971212 µs (97,5%) | ||
; (out) Overhead used : 7,915626 ns | ||
; (out) | ||
; (out) Found 1 outliers in 60 samples (1,6667 %) | ||
; (out) low-severe 1 (1,6667 %) | ||
; (out) Variance from outliers : 51,7849 % Variance is severely inflated by outliers | ||
|
||
(c/bench (math-threadpool)) | ||
; (out) Evaluation count : 3815100 in 60 samples of 63585 calls. | ||
; (out) Execution time mean : 16,910124 µs | ||
; (out) Execution time std-deviation : 2,443261 µs | ||
; (out) Execution time lower quantile : 14,670118 µs ( 2,5%) | ||
; (out) Execution time upper quantile : 23,743868 µs (97,5%) | ||
; (out) Overhead used : 7,915626 ns | ||
; (out) | ||
; (out) Found 3 outliers in 60 samples (5,0000 %) | ||
; (out) low-severe 2 (3,3333 %) | ||
; (out) low-mild 1 (1,6667 %) | ||
; (out) Variance from outliers : 82,4670 % Variance is severely inflated by outliers | ||
|
||
|
||
(c/bench (math-threadpool-no-atom)) | ||
|
||
; (out) Evaluation count : 3794940 in 60 samples of 63249 calls. | ||
; (out) Execution time mean : 16,182655 µs | ||
; (out) Execution time std-deviation : 1,215451 µs | ||
; (out) Execution time lower quantile : 14,729393 µs ( 2,5%) | ||
; (out) Execution time upper quantile : 18,549902 µs (97,5%) | ||
; (out) Overhead used : 7,915626 ns | ||
; (out) | ||
; (out) Found 3 outliers in 60 samples (5,0000 %) | ||
; (out) low-severe 2 (3,3333 %) | ||
; (out) low-mild 1 (1,6667 %) | ||
; (out) Variance from outliers : 56,7625 % Variance is severely inflated by outliers |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,17 @@ | ||
(ns kaocha.testable | ||
(:refer-clojure :exclude [load]) | ||
(:require [clojure.java.io :as io] | ||
[clojure.pprint :as pprint] | ||
[clojure.spec.alpha :as s] | ||
[clojure.test :as t] | ||
[com.climate.claypoole :as cp] | ||
[kaocha.classpath :as classpath] | ||
[kaocha.hierarchy :as hierarchy] | ||
[kaocha.history :as history] | ||
[kaocha.output :as output] | ||
[kaocha.plugin :as plugin] | ||
[kaocha.result :as result] | ||
[kaocha.specs :refer [assert-spec]] | ||
[kaocha.util :as util]) | ||
(:import [clojure.lang Compiler$CompilerException])) | ||
[kaocha.util :as util])) | ||
|
||
(def ^:dynamic *fail-fast?* | ||
"Should testing terminate immediately upon failure or error?" | ||
|
@@ -28,13 +27,16 @@ | |
and `:line`." | ||
nil) | ||
|
||
(def REQUIRE_LOCK (Object.)) | ||
|
||
(defn add-desc [testable description] | ||
(assoc testable ::desc | ||
(str (name (::id testable)) " (" description ")"))) | ||
|
||
(defn- try-require [n] | ||
(try | ||
(require n) | ||
(locking REQUIRE_LOCK | ||
(require n)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
true | ||
(catch java.io.FileNotFoundException e | ||
false))) | ||
|
@@ -45,6 +47,12 @@ | |
(try-require (symbol (namespace type)))) | ||
(try-require (symbol (name type))))) | ||
|
||
(defn- retry-assert-spec [type testable n] | ||
(let [result (try (assert-spec type testable) (catch Exception _e false))] | ||
(if (or result (<= n 1)) result | ||
(retry-assert-spec type testable (dec n))) ;otherwise, retry | ||
)) | ||
|
||
(defn- load-type+validate | ||
"Try to load a testable type, and validate it both to be a valid generic testable, and a valid instance given the type. | ||
|
||
|
@@ -57,7 +65,10 @@ | |
(assert-spec :kaocha/testable testable) | ||
(let [type (::type testable)] | ||
(try-load-third-party-lib type) | ||
(assert-spec type testable))) | ||
(try | ||
(assert-spec type testable) | ||
(catch Exception e | ||
(output/warn (format "Could not load %s. This is a known bug in parallelization.\n%s" type e)))))) | ||
|
||
(defmulti -load | ||
"Given a testable, load the specified tests, producing a test-plan." | ||
|
@@ -100,8 +111,8 @@ | |
(throw t))))) | ||
|
||
(s/fdef load | ||
:args (s/cat :testable :kaocha/testable) | ||
:ret :kaocha.test-plan/testable) | ||
:args (s/cat :testable :kaocha/testable) | ||
:ret :kaocha.test-plan/testable) | ||
|
||
(defmulti -run | ||
"Given a test-plan, perform the tests, returning the test results." | ||
|
@@ -163,6 +174,7 @@ | |
[file line] (util/compiler-exception-file-and-line error) | ||
file (::load-error-file test file) | ||
line (::load-error-line test line) | ||
thread (.getName (Thread/currentThread)) | ||
m (if-let [message (::load-error-message test)] | ||
{:type :error | ||
:message message | ||
|
@@ -174,7 +186,8 @@ | |
:kaocha/testable test}) | ||
m (cond-> m | ||
file (assoc :file file) | ||
line (assoc :line line))] | ||
line (assoc :line line) | ||
thread (assoc :thread thread))] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might want to namespace this? things like type/actual/expected/line/file are standard clojure.test things, if we shove extra data into reporter events we generally namespace them. Not sure how consistent we are in this but generally a good idea. |
||
(t/do-report (assoc m :type :kaocha/begin-suite)) | ||
(binding [*fail-fast?* false] | ||
(t/do-report m)) | ||
|
@@ -211,11 +224,22 @@ | |
(run % test-plan) | ||
(plugin/run-hook :kaocha.hooks/post-test % test-plan))))) | ||
|
||
(defn run-testables | ||
(defn try-run-testable [test test-plan n] | ||
(let [result (try (run-testable test test-plan) (catch Exception _e false))] | ||
(if (or result (> n 1)) result ;success or last try, return | ||
(try-run-testable test test-plan (dec n))) ;otherwise retry | ||
)) | ||
|
||
|
||
(defn run-testables-serial | ||
"Run a collection of testables, returning a result collection." | ||
[testables test-plan] | ||
(doall testables) | ||
#_(print "run-testables got a collection of size" (count testables) | ||
" the first of which is " | ||
(:kaocha.testable/type (first testables))) | ||
(let [load-error? (some ::load-error testables)] | ||
(loop [result [] | ||
(loop [result [] | ||
[test & testables] testables] | ||
(if test | ||
(let [test (cond-> test | ||
|
@@ -227,6 +251,33 @@ | |
(recur (conj result r) testables))) | ||
result)))) | ||
|
||
(defn run-testables-parallel | ||
"Run a collection of testables, returning a result collection." | ||
[testables test-plan] | ||
(let [num-threads (or (:parallel-threads *config*) (* 2 (inc (cp/ncpus)))) | ||
pred #(= :kaocha.type/ns (:kaocha.testable/type %)) | ||
nses (seq (filter pred testables)) | ||
others (seq (remove pred testables))] | ||
(concat | ||
(when others (run-testables-serial others test-plan)) | ||
(when nses | ||
(cp/with-shutdown! [pool (cp/threadpool num-threads :name "kaocha-test-runner")] | ||
(doall | ||
(cp/pmap | ||
pool | ||
#(binding [*config* | ||
(-> *config* | ||
(dissoc :parallel) | ||
(update :levels (fnil inc 0)))] | ||
(run-testable % test-plan)) | ||
nses))))))) | ||
|
||
(defn run-testables | ||
[testables test-plan] | ||
(if (:parallel *config*) | ||
(run-testables-parallel testables test-plan) | ||
(run-testables-serial testables test-plan))) | ||
|
||
(defn test-seq [testable] | ||
(cond->> (mapcat test-seq (remove ::skip (or (:kaocha/tests testable) | ||
(:kaocha.test-plan/tests testable) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also shutdown agents if
-main*
exits normally?