performance increases

memoize construction of ranges, and use transient/mutable
Java collections for internal collection state
This commit is contained in:
2026-01-13 21:01:46 -06:00
parent df24a84908
commit 9ffa5d1b97
3 changed files with 42 additions and 37 deletions

View File

@@ -24,13 +24,13 @@
(union [this other] (union [this other]
(when-not (.isConnected this other) (when-not (.isConnected this other)
(throw (ex-info "Cannot union non-connecting ranges" {}))) (throw (ex-info "Cannot union non-connecting ranges" {})))
(.union this other)) (.union this other)))
(before [^LocalDateRange this ^LocalDate x]
(.isBefore this x)) (def ^:private closed-local-date-range
(after [this ^LocalDate x] (memoize (fn [start end]
(.isAfter (.getEndInclusive this) x))) (LocalDateRange/ofClosed start end))))
(defmethod range/->discrete-value-range :local-date-range [_ start end] (defmethod range/->discrete-value-range :local-date-range [_ start end]
(LocalDateRange/ofClosed start end)) (closed-local-date-range start end))
(def difference range/difference) (def difference range/difference)

View File

@@ -26,7 +26,7 @@
that before/after values against other values that the range that before/after values against other values that the range
is over. is over.
" "
(abuts [this ^DiscreteValueRange other]) (abuts [this other])
(value-before [this]) (value-before [this])
(value-after [this]) (value-after [this])
(start [this]) (start [this])
@@ -34,6 +34,18 @@
(range-type [this]) (range-type [this])
(union [this other])) (union [this other]))
(defn ->range-boundaries* [range]
[{:value (start range)
:boundary-type :start
:type (range-type range)
:prev-value (value-before range)}
{:value (end range)
:boundary-type :end
:type (range-type range)
:next-value (value-after range)}])
(def ->range-boundaries (memoize ->range-boundaries*))
(defn- ordered-range-values (defn- ordered-range-values
"Builds an ordered list or 'fenceposts' for the start and end "Builds an ordered list or 'fenceposts' for the start and end
of all given ranges, to prepare to produces a consolidated of all given ranges, to prepare to produces a consolidated
@@ -48,15 +60,7 @@
any intermediate matching starts/stops." any intermediate matching starts/stops."
[ranges] [ranges]
(->> ranges (->> ranges
(mapcat (fn [range] (mapcat ->range-boundaries)
[{:value (start range)
:boundary-type :start
:type (range-type range)
:prev-value (value-before range)}
{:value (end range)
:boundary-type :end
:type (range-type range)
:next-value (value-after range)}]))
(sort-by (juxt :value (comp {:start 0 :end 1} :boundary-type))))) (sort-by (juxt :value (comp {:start 0 :end 1} :boundary-type)))))
(defmulti ->discrete-value-range (fn [range-type _start _end] (defmulti ->discrete-value-range (fn [range-type _start _end]
@@ -77,25 +81,25 @@
end pairs encountered between the new start and end values." end pairs encountered between the new start and end values."
[] []
(fn [xf] (fn [xf]
(let [stack (volatile! []) (let [stack (java.util.Stack.)
start (volatile! nil)] start (volatile! nil)]
(fn (fn
([] (xf)) ([] (xf))
([result] (xf result)) ([result] (xf result))
([result input] ([result {:keys [boundary-type value type] :as input}]
(if (reduced? input) (if (reduced? input)
result result
(case (:boundary-type input) (case boundary-type
:start (do :start (do
(when-not (seq @stack) (when (.empty stack)
(vreset! start (:value input))) (vreset! start value))
(vswap! stack (fnil conj []) input) (.push stack input)
result) result)
:end (do :end (do
(vswap! stack pop) (.pop stack)
(if (seq @stack) (if (not (.empty stack))
result result
(xf result (->discrete-value-range (:type input) @start (:value input)))))))))))) (xf result (->discrete-value-range type @start value)))))))))))
(defn- combine-abutting-ranges (defn- combine-abutting-ranges
"transducer to join ranges where the start and end of two ranges are consective "transducer to join ranges where the start and end of two ranges are consective
@@ -138,18 +142,17 @@
(defn walk-range-boundaries [range-boundary-items] (defn walk-range-boundaries [range-boundary-items]
(let [close-working-range (fn [range-type start end ranges] (let [close-working-range (fn [range-type start end ranges]
(conj ranges (->discrete-value-range range-type (conj! ranges (->discrete-value-range range-type
start start
end)))] end)))]
(loop [[boundary-item & boundary-items] range-boundary-items (loop [[boundary-item & boundary-items] range-boundary-items
in-filter-range nil in-filter-range nil
in-source-range nil in-source-range nil
ranges #{} ranges (transient #{})
close-fn nil] close-fn nil]
(match [boundary-item] (match [boundary-item]
[nil] [nil]
ranges (persistent! ranges)
[{:boundary-type :start [{:boundary-type :start
:range-source-type :source-range :range-source-type :source-range

View File

@@ -31,10 +31,12 @@
(toString [_this] (toString [_this]
(str start ".." end))) (str start ".." end)))
(defn int-range-inclusive [start end] (defn- int-range-inclusive* [start end]
(assert (<= start end) (str "start : " start "; end: " end)) (assert (<= start end) (str "start : " start "; end: " end))
(->IntInclusiveDiscreteValueRange start end)) (->IntInclusiveDiscreteValueRange start end))
(def int-range-inclusive (memoize int-range-inclusive*))
(defmethod range/->discrete-value-range :int-range-inclusive [_ start end] (defmethod range/->discrete-value-range :int-range-inclusive [_ start end]
(int-range-inclusive start end)) (int-range-inclusive start end))