Skip to content

Instantly share code, notes, and snippets.

@RutledgePaulV
Created July 4, 2020 22:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RutledgePaulV/0161060b01d5aa8fa98aa21a81b0460b to your computer and use it in GitHub Desktop.
Save RutledgePaulV/0161060b01d5aa8fa98aa21a81b0460b to your computer and use it in GitHub Desktop.
A task timer for self-optimizing programs that favors recent timings over older timings.
(ns piped.tools
(:require [amalloy.ring-buffer :as rb]))
(defprotocol Timer
(start [this key] "Given some key, start timing it.")
(stop [this key] "Given some key, stop timing it.")
(avg [this] "Get the weighted average of timings tracked thus far with this timer. Recent timings are weighted more."))
(defn weighted-average
"Calculates the weighted average of numbers. Each subsequent number is weighted one
more than the previous number was weighted. The first number is weighted at 1."
[numbers]
(let [{:keys [sum parts]}
(reduce
(fn [agg [weight time]]
(let [part (inc weight)]
(-> agg
(update :sum + (* part time))
(update :parts + part))))
{:sum 0 :parts 0}
(map-indexed vector numbers))]
(when (pos? parts) (/ sum parts))))
(defn new-timer [capacity]
(let [state (atom {:pending {} :buffer (rb/ring-buffer capacity)})]
(reify Timer
(avg [this] (weighted-average (:buffer @state)))
(start [this key]
(let [time (System/currentTimeMillis)]
(swap! state assoc-in [:pending key] time)
time))
(stop [this key]
(let [time (System/currentTimeMillis)]
(swap! state
(fn [old]
(let [start (get-in old [:pending key])]
(-> old
(update :pending dissoc key)
(update :buffer conj (- time start))))))
time)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment