Hi Cecil,

On Apr 12, 2014, at 3:18 PM, Cecil Westerhof <cldwester...@gmail.com> wrote:

> 
> I just started playing with Clojure a few days ago, so I am a tabula rasa. I 
> attached what I have until now. If it can be improved, I like to know it.

I had a look at your code and it's not clear to me what you are trying to 
experiment with. Possibly benchmark using futures to run the do-sequential 
function concurrently. I'll assume that for the moment and make a couple of 
observations...

You are using atoms to store two values (max-diff and max-factor) that are used 
and changed in main-function. Did you really intend to share these across all 
the calculations? The code you've provided will have concurrency errors as a 
result. You might consider writing this as something like (the *completely* 
untested and so only good as a sketch):

(def max-diff (ref 0.0))
(def max-factor (ref 0.0))

(defn init [type]
  (give-message (format "Start %s" type))
  (dosync
    (ref-set max-diff   0.0)
    (ref-set max-factor 0.0)))
   
(defn main-function [i diff]
  (dosync
    (ensure max-diff)
    (ensure max-factor)
    (when (> diff @max-diff)
       (give-message (format "Different for %12d (%e, %e)" i diff (/ diff i)))
       (alter max-diff max diff)
       (when (> (/ diff i) @max-factor)
         (alter max-factor max (/ diff i)))))

The 'ensures' are there because max-diff and max-factor aren't really 
independent and some interaction between concurrent threads in the outer when 
could lead to some strange situations (like testing for max-diff (or 
max-factor) then finding that some other thread ran and changed max-diff (or 
max-factor) out from under you.

Your concurrency will likely be restricted to (some subset) of your cores since 
there's no IO in there (you're dumping the actual logging off to an agent). 
This isn't a bug, just mentioning it, but there's something to think about 
below.

There's a problem in your calculation of diff... it's always 0.0 which means 
that there's nothing being done in main-function. I changed it to something 
arbitrary (and *completely* untested and so only good as a sketch):

  (let [v (int (Math/sqrt i))
        diff (Math/abs (- (Math/pow v 2) i))]

No idea if that's reasonable, but at least main-function does something now.

With the hacked up calculation of diff, I was able to demonstrate the 
concurrency problem in main-function by simply running (the *completely* 
untested and so only good as a sketch):

(defn main-function [i diff]
  (swap! iterations0 inc)
  (when (> diff @max-diff)
    (swap! iterations1 inc)
    #_(give-message (format "Different for %12d (%e, %e)" i diff (/ diff i)))
    (swap! max-diff max diff)
    (when (> (/ diff i) @max-factor)
      (swap! iterations2 inc)
      (swap! max-factor max (/ diff i)))))

and printing the values of iterations* -- with your swap algorithm you get 
different values for iterations1 and sometimes iterations2 for successive run.

If you didn't intend to share max-diff and max-factor, then you'd likely be 
better off if do-sequential passed local values into main-function, something 
like (the *completely* untested and so only good as a sketch):

(defn main-function [[max-diff max-factor] i]
  (let [v (int (Math/sqrt i))
        diff (Math/abs (- (Math/pow v 2) i))]
    (if (> diff max-diff)
      (do
        #_(give-message (format "Different for %12d (%e, %e)" i diff (/ diff 
i)))
        [(max max-diff diff) (max max-factor (/ diff i))])
      [max-diff max-factor])))

(defn do-sequential [start stop step]
  (reduce main-function [0.0 0.0] (range start stop step)))


You've decided to use futures for concurrency. Maybe that's the whole point, 
but if you're just trying to get a feel for concurrency in Clojure then there 
are some options. I find I directly use futures and promises, at most, rarely 
(I can't remember ever using them directly, maybe in the very early days of 
Clojure). What I find handy for this sort of thing is something like pmap, and 
if you really want this asynchronous then have a look at core.async. Anyway, 
with pmap (the *completely* untested and so only good as a sketch):

(defn check-concurrent [number]
  (doall
    (pmap #(do-sequential % check-until number) (range 1 (inc number)))))

The shutdown-agents call is likely not what you want, you don't need it.

You're using agents for the logging. This gets the IO out of your code being 
tested, but there's still a lot of formatting work happening there (not much of 
a problem). But you don't know the impact of the agent thread pool on your 
benchmark. It's possible that it acts as a throttle. You can avoid this easily 
by not logging anything in main-function (I've commented it out in the example 
above).

Benchmarking is tricky to do. It's a lot easier to use something like 
criterium, kind of like this (I added the *verbose* dynamic variable for fun to 
the *completely* untested and so only good as a sketch):

(def ^:dynamic *verbose* true)
(defn give-message [message]
  (when *verbose*
    (send logger
          (fn [_ & [msg]] (println (format "%s: %s"
                                           (. time-format format (new 
java.util.Date (now))) msg)))
          message)))

(defn run-bench [n]
  (println "\n\n\nBench:: threads:" n)
  (binding [*verbose* false]
    (bench (check-concurrent n bench-until))))

bench-until should be much smaller than your check-until. All you need is for a 
real test to be run and criterium will do the rest (so maybe 1001 will work, 
but if it's too small and you'll benchmark the wrong thing and introduce 
variations in timing you don't want)

Criterium will warm up the JVM, call check-concurrent as many times as 
necessary to satisfy some criteria it decides while pre-running the benchmark 
(pre-running isn't what they call it... but anyway, if a run takes too long, 
criterium will take a long time -- as it is, it takes a minute or two for each 
benchmark). When done Criterium will report mean execution time, standard 
deviation and lower & upper quintiles. It's a little slow, but you'll not be 
wasting your time dealing with strange JVM behaviour, and your benchmarks have 
a good chance of being representative.

Anyway, have fun!

Cheers,
Bob


-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to