Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Megarefs

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

 Megarefs

Slides from my talk at EuroClojure 2013

Avatar for Christophe Grand

Christophe Grand

October 15, 2013
Tweet

More Decks by Christophe Grand

Other Decks in Programming

Transcript

  1. I have a problem • I can’t decide how to

    split the application essential state in discrete «boxes» • Changing the state split later on is not painless. • I just want to put the world in a single box!
  2. Hail the uniform update model! (def a (atom some-big-and-deep-map)) (swap!

    a update-in path f x y z) ; concatenative feeling...
  3. swap-in! v0.0.1 (defn swap-in! [a path f & args] (apply

    swap! a update-in path f args)) ; before (swap! a update-in path f x y z) ; after (swap-in! a path f x y z)
  4. Problems • Unrelated calls to swap-in! conflict • Everything (update-in

    and f) is retried • When unrelated only update-in should retry • f should not retry
  5. swap-in! v0.0.2 ; memoize makes retries cheap! (defn swap-in! [a

    path f & args] (apply swap! a update-in path (memoize f) args))
  6. swap-in! v0.0.3+ ; from here only optimization is better ;

    cheaper caching: 1-slot, identity, ; update-in steps, etc. (defn swap-in! [a path f & args] (swap! a update-in path (memoize #(apply f % args))))
  7. MegaAtom? • swap-in! allows: • bigger values • longer updates

    • Still a regular Atom • Let’s take the next step: multiple paths!
  8. Megalomania! ;; update many paths (defn update-ins [m f &

    paths] (reduce-kv assoc-in m (zipmap paths (apply f (map #(get-in m %) paths))))) (defn swap-ins! [a f & paths] (apply swap! a update-ins (memoize f) paths))
  9. (def accounts (atom {:lucy 100 :ethel 200 :fred 300 :ricky

    400})) (defn transfer [amount] (fn [from to] [(- from amount) (+ to amount)])) ;; No retry of the transfer fn! (swap-ins! accounts (transfer 50) [:ricky] [:lucy]) (swap-ins! accounts (transfer 20) [:ethel] [:fred])
  10. Megalomania!!! • swap-ins! is a poor man’s STM • paths

    as an alternative to discrete identities • intriguing idea • let’s try on the STM proper!
  11. Refs • The memoize trick doesn’t work • There’s no

    single pure function for the whole transaction • Take a page from j.u.concurrent’s book: • Lock striping
  12. Lock striping • Variable-sized mutable collection • Fixed number (N)

    of guards (locks) • To have exclusive access to the item ith • lock the (i mod N)th guard! 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2
  13. Ref striping • Variable-sized immutable collection • Stored in a

    single root ref • Fixed number (N) of guards (refs) • To perform actions (ensure, ref-set/alter, commute) on the item ith • perform dummy actions on the (i mod N)th guard! • deref or commute the root ref!
  14. Why not locks? • Locks don’t compose • Can’t do

    in user code • Hacking the STM itself • I failed/abandoned • Key insight: refs as the building blocks of more complex ref types.
  15. ref-striped alter-nth (defn alter-nth [r i f & args] (let

    [guards (-> r meta ::guards) guard (nth guards (mod i (count guards)))] (ref-set guard nil) (apply commute r update-in [i] f args)))
  16. declining -nth deref-nth ensure-nth alter-nth ref-set-nth commute- nth guard root

    f ø ensure ref-set ø deref deref commute commute get-in get-in update-in assoc-in update-in
  17. From -nth to -in • Maps? (and vectors, records...) •

    Just (mod (hash k) (count guards)) • Nested maps? • Glad you asked!
  18. Paths • You want independent updates for: • [:a :b]

    and [:a :c] • You don’t want independent updates for: • [:a] and [:a :b]
  19. Guards! Guards! • One guard by prefix (or path segment*)

    • (guard [:a]), (guard [:a :b]) etc. • prefix guards are ensured • path guard is ref-set *tends to generate more collisions
  20. declining -in deref-in ensure-in alter-in ref-set-in commute- in path guard

    prefix guard root f ø ensure ref-set ø ø ensure ensure ø deref deref commute commute get-in get-in update-in assoc-in update-in
  21. Subrefs • A mutable view on a part of a

    bigger ref • (subref parent-ref path) • Allow for easy migration of a codebase • from plenty-of-refs to some-megarefs
  22. Ants: ported (1/3) -(ns ants.core) +(ns ants.core + (:refer-clojure :exclude

    [alter commute ref-set ensure]) + (:use [net.cgrand.megaref + :only [alter commute ref-set ensure megaref subref]]))
  23. Ants: ported (2/3) -;world is a 2d vector of refs

    to cells -(def world - (apply vector - (map (fn [_] - (apply vector (map (fn [_] (ref (struct cell 0 0))) - (range dim)))) - (range dim)))) +;world is a megaref to a 2d vector of cells +(def world + (megaref (vec (repeat dim (vec (repeat dim (struct cell 0 0))))))) -(defn place [[x y]] - (-> world (nth x) (nth y))) +(defn place [xy] + (subref world xy))
  24. Ants: ported (3/3) (defn render [g] - (let [v (dosync

    (apply vector (for [x (range dim) y (range dim)] - @(place [x y])))) + (let [w @world img (new BufferedImage (* scale dim) (* scale dim) (. BufferedImage TYPE_INT_ARGB)) bg (. img (getGraphics))] @@ -274,7 +272,7 @@ (.fillRect 0 0 (. img (getWidth)) (. img (getHeight)))) (dorun (for [x (range dim) y (range dim)] - (render-place bg (v (+ (* x dim) y)) x y))) + (render-place bg (get-in w [x y]) x y))) (doto bg (.setColor (. Color blue)) (.drawRect (* scale home-off) (* scale home-off)
  25. Summary • Megaref • No upfront decision • Comparable to

    plenty-of-refs (when not CPU bound) • Cheap snapshot without memory footprint
  26. Further ideas • Allocation profiling • Tuning the path hash

    function • more guards for hot paths • self-tuning? • Applying the same principle to agents
  27. MegaAgents • Relax the serial constraint: • two actions are

    serialized only when their paths are related • [:a :b] blocks [:a :b :c] (and vice-versa) • [:a :b] and [:a :c] and [:c :d] occurs concurrently
  28. Agent striping • Variable-sized immutable collection • Stored in a

    single root atom • Fixed number (N) of guards (agents) • To perform send f on the item ith • send (fn [_] (swap! root assoc-in [i] (f (get-in path @root)))) to the (i mod N)th guard!
  29. From -nth to -in • Breakthrough by Meikel Brandmeyer: •

    chain sends through guards • Use guards’ values to hold kind of a counter and a queue • It even seems to work!
  30. Agent Guards! :a :b :c :d :e :f Not busy

    Busy [2 []] [2 []] [2 [X]] [0 []] [1 []] [0 []] X Pending update to [:a :b :c]