;;; https://aphyr.com/posts/306-clojure-from-the-ground-up-state
;;; http://stackoverflow.com/questions/34663718/alter-vs-commute-in-clojure-what-am-i-doing-wrong
;;; setup
(defn- sum [start end] (reduce + (range start end)))
;;; ex1
(let [x (delay (sum 0 1e7))]
(time @x)) ;; => 49999995000000
;; Elapsed time: 175.930652 msecs
;;; ex2
(defmacro ^:private my-future [& body]
`(.start (Thread. (fn [] ~@body))))
(time
(let [x (promise)]
#_(future
(deliver x (sum 0 5e7)))
(my-future
(deliver x (sum 0 1e7)))
@x)) ;;=> 49999995000000
;; Elapsed time: 173.683918 msecs
;;; ex3
(time (+ (deref (future (sum 0 (/ 1e7 2))))
(deref (future (sum (/ 1e7 2) 1e7))))) ;=> 4.9999995E13
;; Elapsed time: 199.625634 msecs
;;; utilize cores more?
(let [a (agent nil)
b (agent nil)]
(time
(do
(send a (constantly (sum 0 (/ 1e7 2))))
(send b (constantly (sum (/ 1e7 2) 1e7)))
(await a) (await b)
(+ @a @b)))) ;;=> 4.9999995E13
;; Elapsed time: 124.770032 msecs
;;; ex4
(let [x (atom 0)
mid (/ 1e7 2)]
(time
(let [f1 (future
(loop [n 0]
(let [m (inc n)]
(when (<= m mid)
(swap! x + m)
(recur m)))))
f2 (future
(loop [n 1e7]
(let [m (dec n)]
(when (< mid m)
(swap! x + m)
(recur m)))))]
@f1 @f2
@x))) ;; => 4.9999995E13
;; Elapsed time: 988.096735 msecs
;;; ex5
(let [work (ref (apply list (range 1e5)))
sum (ref 0)
transaction #(dosync
(when-first [x @work]
(alter sum + x)
(alter work rest)
true))]
(time
(let [f1 (future (while (transaction)))
f2 (future (while (transaction)))]
@f1 @f2
@sum))) ;;=> 4999950000
;; Elapsed time: 158.79588 msecs
(let [work (ref (apply list (range 1e5)))
sum (ref 0)
transaction #(dosync
(when-first [x @work]
(commute sum + x)
(commute work rest)
true))]
(time
(let [f1 (future (while (transaction)))
f2 (future (while (transaction)))]
@f1 @f2
@sum))) ;;=> 4999935198 .... 5000026273
;; Elapsed time: 156.817954 msecs
;;; use `ensure` instead of `deref`
(let [work (ref (apply list (range 1e3))) ;<- lower than the before
sum (ref 0)
transaction #(dosync
(when-first [x (ensure work)] ;<- instead of deref
(alter sum + x)
(alter work rest)
true))]
(time
(let [f1 (future (while (transaction)))
f2 (future (while (transaction)))]
@f1 @f2
@sum))) ;; => 499500
;; Elapsed time: 16132.614599 msecs
;; slowwww!!!
(let [work (ref (apply list (range 1e5))) ;<- go back to the common value.
sum (ref 0)
transaction #(dosync
(when-first [x (ensure work)]
(commute sum + x)
(commute work rest)
true))]
(time
(let [f1 (future (while (transaction)))
f2 (future (while (transaction)))]
@f1 @f2
@sum))) ;; => 4999950000
;; Elapsed time: 394.146033 msecs
;; takes 2 times but correct.
;;; There are same numbers in the commute version.
(let [work (ref (apply list (range 1e5)))
acc (ref [])
transaction #(dosync
(when-first [x @work]
(alter acc conj x)
(alter work rest)
true))]
(let [f1 (future (while (transaction)))
f2 (future (while (transaction)))]
@f1 @f2
[(count @acc)
(- (count @acc) (count (distinct @acc)))])) ;; => [100000 0]
(let [work (ref (apply list (range 1e5)))
acc (ref [])
transaction #(dosync
(when-first [x @work]
(commute acc conj x)
(commute work rest)
true))]
(let [f1 (future (while (transaction)))
f2 (future (while (transaction)))]
@f1 @f2
[(count @acc)
(- (count @acc) (count (distinct @acc)))])) ;; => [100000 14190]
;; with ensure
(let [work (ref (apply list (range 1e5)))
acc (ref [])
transaction #(dosync
(when-first [x (ensure work)]
(commute acc conj x)
(commute work rest)
true))]
(let [f1 (future (while (transaction)))
f2 (future (while (transaction)))]
@f1 @f2
[(count @acc)
(- (count @acc) (count (distinct @acc)))])) ;; => [100000 0]