Skip to content

Commit c099142

Browse files
Faster promesa.exec/wrap-bindings
83x faster than current implementation ``` (run-benchmark (fn [] (testing "`promesa.exec.csp` implementation" ;; 4.956172 µs — this is due to `promesa.exec/wrap-bindings`'s way of forwarding ;; bindings, which is 83x slower than `clojure.core/binding-conveyor-fn` (criterium/bench (csp/go)) ;; 4.889489 ms (criterium/bench (dotimes [i 1000] (csp/go)))) (testing "Bare implementation" ;; 59.033042 ns (criterium/bench (-> (Thread/ofVirtual) (.unstarted (fn [])) (.start)))) (testing "With bindings forwarding" ;; 58.851215 ns (criterium/bench (-> (Thread/ofVirtual) (.unstarted (binding-conveyor (^{:once true} fn* [] ~@Body))) (.start)))))) ```
1 parent be4ea19 commit c099142

File tree

1 file changed

+48
-46
lines changed

1 file changed

+48
-46
lines changed

src/promesa/exec.cljc

+48-46
Original file line numberDiff line numberDiff line change
@@ -179,56 +179,58 @@
179179
@default-scheduler
180180
(pu/maybe-deref scheduler))))
181181

182+
#?(:clj
183+
(defn- binding-conveyor-inner
184+
[f]
185+
(let [frame (clojure.lang.Var/cloneThreadBindingFrame)]
186+
(fn
187+
([]
188+
(clojure.lang.Var/resetThreadBindingFrame frame)
189+
(cond
190+
(instance? clojure.lang.IFn f)
191+
(.invoke ^clojure.lang.IFn f)
192+
193+
(instance? java.lang.Runnable f)
194+
(.run ^java.lang.Runnable f)
195+
196+
(instance? java.util.concurrent.Callable f)
197+
(.call ^java.util.concurrent.Callable f)
198+
199+
:else
200+
(throw (ex-info "Unsupported function type" {:f f :type (type f)}))))
201+
([x]
202+
(clojure.lang.Var/resetThreadBindingFrame frame)
203+
(cond
204+
(instance? clojure.lang.IFn f)
205+
(.invoke ^clojure.lang.IFn f x)
206+
207+
(instance? java.util.function.Function f)
208+
(.apply ^java.util.function.Function f x)
209+
210+
:else
211+
(throw (ex-info "Unsupported function type" {:f f :type (type f)}))))
212+
([x y]
213+
(clojure.lang.Var/resetThreadBindingFrame frame)
214+
(f x y))
215+
([x y z]
216+
(clojure.lang.Var/resetThreadBindingFrame frame)
217+
(f x y z))
218+
([x y z & args]
219+
(clojure.lang.Var/resetThreadBindingFrame frame)
220+
(apply f x y z args))))))
221+
182222
(defn wrap-bindings
183223
{:no-doc true}
224+
;; Passes on local bindings from one thread to another. Compatible with `clojure.lang.IFn`,
225+
;; `java.lang.Runnable`, `java.util.concurrent.Callable`, and `java.util.function.Function`.
226+
;; Adapted from `clojure.core/binding-conveyor-fn`."
184227
[f]
185228
#?(:cljs f
186-
:clj
187-
(let [bindings (get-thread-bindings)]
188-
(fn
189-
([]
190-
(push-thread-bindings bindings)
191-
(try
192-
(f)
193-
(finally
194-
(pop-thread-bindings))))
195-
([a]
196-
(push-thread-bindings bindings)
197-
(try
198-
(f a)
199-
(finally
200-
(pop-thread-bindings))))
201-
([a b]
202-
(push-thread-bindings bindings)
203-
(try
204-
(f a b)
205-
(finally
206-
(pop-thread-bindings))))
207-
([a b c]
208-
(push-thread-bindings bindings)
209-
(try
210-
(f a b c)
211-
(finally
212-
(pop-thread-bindings))))
213-
([a b c d]
214-
(push-thread-bindings bindings)
215-
(try
216-
(f a b c d)
217-
(finally
218-
(pop-thread-bindings))))
219-
([a b c d e]
220-
(push-thread-bindings bindings)
221-
(try
222-
(f a b c d e)
223-
(finally
224-
(pop-thread-bindings))))
225-
([a b c d e & args]
226-
(push-thread-bindings bindings)
227-
(try
228-
(apply f a b c d e args)
229-
(finally
230-
(pop-thread-bindings))))))))
231-
229+
:clj (reify
230+
clojure.lang.IFn
231+
(invoke [_ f] (binding-conveyor-inner f))
232+
java.util.function.Function
233+
(apply [_ f] (binding-conveyor-inner f)))))
232234

233235
#?(:clj
234236
(defn thread-factory?

0 commit comments

Comments
 (0)