From 10c58a0a8173e4f0b3d7947180e4c35228469e39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imre=20K=C3=B3sz=C3=B3?= Date: Sun, 31 Jul 2022 12:10:52 +0200 Subject: [PATCH] Fix x/into perf problem when from is a small map --- src/net/cgrand/xforms.cljc | 45 ++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/net/cgrand/xforms.cljc b/src/net/cgrand/xforms.cljc index 3740693..2fad080 100644 --- a/src/net/cgrand/xforms.cljc +++ b/src/net/cgrand/xforms.cljc @@ -7,9 +7,10 @@ :clj (:require [net.cgrand.macrovich :as macros])) (:refer-clojure :exclude [some reduce reductions into count for partition str last keys vals min max drop-last take-last - sort sort-by time]) + sort sort-by time satisfies?]) (:require [#?(:clj clojure.core :cljs cljs.core) :as core] - [net.cgrand.xforms.rfs :as rf]) + [net.cgrand.xforms.rfs :as rf] + #?(:clj [clojure.core.protocols])) #?(:cljs (:import [goog.structs Queue]))) (macros/deftime @@ -124,8 +125,44 @@ (macros/usetime -(defn kvreducible? [coll] - (satisfies? #?(:clj clojure.core.protocols/IKVReduce :cljs IKVReduce) coll)) +;; Workaround clojure.core/satisfies? being slow in Clojure +;; see https://ask.clojure.org/index.php/3304/make-satisfies-as-fast-as-a-protocol-method-call +#?(:cljs + (def satisfies? core/satisfies?) + + :bb + (def satisfies? core/satisfies?) + + :clj + (defn fast-satisfies?-fn + "Ported from https://github.com/clj-commons/manifold/blob/37658e91f836047a630586a909a2e22debfbbfc6/src/manifold/utils.clj#L77-L89" + [protocol-var] + (let [^java.util.concurrent.ConcurrentHashMap classes + (java.util.concurrent.ConcurrentHashMap.)] + (add-watch protocol-var ::memoization (fn [& _] (.clear classes))) + (fn [x] + (let [cls (class x) + val (.get classes cls)] + (if (nil? val) + (let [val (core/satisfies? @protocol-var x)] + (.put classes cls val) + val) + val)))))) + + +#?(:cljs + (defn kvreducible? [coll] + (satisfies? core/IKVReduce coll)) + + :clj + (let [satisfies-ikvreduce? #?(:bb #(satisfies? clojure.core.protocols/IKVReduce %) + :default (fast-satisfies?-fn #'clojure.core.protocols/IKVReduce))] + (if (satisfies-ikvreduce? (Object.)) + (defn kvreducible? + "Clojure 1.11 makes everything satisfy IKVReduce, so we can short-circuit" + [_] true) + (defn kvreducible? [coll] (satisfies-ikvreduce? coll))))) + (extend-protocol KvRfable #?(:clj Object :cljs default) (some-kvrf [_] nil)