ryoakg
5/2/2016 - 8:20 AM

state.clj

;;; 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]