Sorry to be a bother, but any movement on this? This looks like a real
performance bug and I don't yet have the internals knowledge to chase it
down myself.


On Sun, Nov 3, 2013 at 12:10 PM, Michał Marczyk <michal.marc...@gmail.com>wrote:

> Well, that is interesting.
>
> The difference between the compiled versions of
>
> (defn foo [x]
>   (if (> x 0)
>     (inc x)
>     (locking o
>       (dec x))))
>
> and
>
> (defn bar [x]
>   (if (> x 0)
>     (inc x)
>     (let [res (locking o
>                 (dec x))]
>       res)))
>
> is quite significant. foo gets compiled to a single class, with
> invocations handled by a single invoke method; bar gets compiled to a
> class for bar + an extra class for an inner function which handles the
> (locking o (dec x)) part -- probably very similar to the output for
> the version with the hand-coded locking-part (although I haven't
> really looked at that yet). The inner function is a closure, so
> calling it involves an allocation of a closure object; its ctor
> receives the closed-over locals as arguments and stores them in two
> fields (lockee and x). Then they get loaded from the fields in the
> body of the closure's invoke method etc.
>
> I guess I'll have to play around with Java equivalents too...
>
> Cheers,
> Michał
>
>
> On 3 November 2013 20:46, Michael Blume <blume.m...@gmail.com> wrote:
> > I mean, I'm probably being naive, but this suggests that one could write
> >
> > (defmacro locking' [& forms]
> >   `(let [res# (locking ~@forms)] res#))
> >
> > and use locking' in place of locking for improved performance. Is this
> > wrong? If it's right, does that suggest the macro in clojure.core should
> be
> > changed?
> >
> >
> > On Sun, Nov 3, 2013 at 11:09 AM, Michael Blume <blume.m...@gmail.com>
> wrote:
> >>
> >> Huh, interesting.
> >>
> >> I have:
> >>
> >> (defn foo' [x]
> >>   (if (> x 0)
> >>     (inc x)
> >>     (let [res (locking o (dec x))] res)))
> >>
> >> (defn foo'' [x]
> >>   (if (> x 0)
> >>     (inc x)
> >>     (locking o
> >>       (dec x))))
> >>
> >> foo' is fast, but foo'' is slow. So something about wrapping the locking
> >> clause in a let makes it fast. Still no idea why.
> >>
> >> On Sunday, November 3, 2013 9:30:45 AM UTC-8, Michał Marczyk wrote:
> >>>
> >>> You have a typo in foo -- monitor-exit's argument is 0 (zero) rather
> >>> than o (the sentinel object).
> >>>
> >>> Besides that, in foo both monitor-enter and monitor-exit get their
> >>> arguments from a Var. Rewriting to use locking, which first puts the
> >>> object whose monitor will be used in a local (that is, (let [lockee o]
> >>> ...), where ... performs the locking using the newly introduced
> >>> local), gives timings identical to those of bar and baz:
> >>>
> >>> (defn foo' [x]
> >>>   (if (> x 0)
> >>>     (inc x)
> >>>     (let [res (locking o (dec x))] res)))
> >>>
> >>> So this is one reason not to use monitor-enter and monitor-exit
> >>> directly. Another reason is that locking guarantees that the monitor
> >>> will be released (by using try / finally, and of course by preventing
> >>> situations where the matching monitor-enter & monitor-exit operate on
> >>> different objects).
> >>>
> >>> In fact, both monitor-enter and monitor-exit carry docstrings which
> >>> explicitly say that they should not be used in user code and point to
> >>> locking as the user-facing equivalent to Java's synchronized.
> >>>
> >>> Cheers,
> >>> Michał
> >>>
> >>>
> >>> On 1 November 2013 19:34, Michael Blume <blume...@gmail.com> wrote:
> >>> > https://github.com/MichaelBlume/perf-test
> >>> >
> >>> > (ns perf-test
> >>> >   (:use (criterium core))
> >>> >   (:gen-class))
> >>> >
> >>> > (def o (Object.))
> >>> >
> >>> > (defn foo [x]
> >>> >   (if (> x 0)
> >>> >     (inc x)
> >>> >     (do
> >>> >       (monitor-enter o)
> >>> >       (let [res (dec x)]
> >>> >         (monitor-exit 0)
> >>> >         res))))
> >>> >
> >>> > (defn bar [x]
> >>> >   (if (> x 0)
> >>> >     (inc x)
> >>> >     (dec x)))
> >>> >
> >>> > (defn locking-part [x l]
> >>> >   (monitor-enter l)
> >>> >   (let [res (dec x)]
> >>> >     (monitor-exit l)
> >>> >     res))
> >>> >
> >>> > (defn baz [x]
> >>> >   (if (> x 0)
> >>> >     (inc x)
> >>> >     (locking-part x o)))
> >>> >
> >>> > (defn -main []
> >>> >   (println "benching foo")
> >>> >   (bench (foo 5) :verbose)
> >>> >   (println "benching bar")
> >>> >   (bench (bar 5) :verbose)
> >>> >   (println "benching baz")
> >>> >   (bench (baz 5) :verbose)
> >>> >   (println "done benching"))
> >>> >
> >>> >
> >>> >
> >>> > I'm only ever calling these functions with positive values, so the
> >>> > monitor-enter branch should never be entered. Nevertheless, the
> >>> > performance
> >>> > of foo is much worse than bar or baz.
> >>> >
> >>> > The best guess I've got is that the fact that lock-taking is involved
> >>> > somehow changes how the function is compiled, somehow making the
> >>> > function
> >>> > slower. If the practical upshot is that I shouldn't write functions
> >>> > that
> >>> > only sometimes lock -- that the locking part of a function should
> >>> > always be
> >>> > its own function -- then I can do that, but I'm curious why.
> >>> >
> >>> > $ lein uberjar
> >>> > Compiling perf-test
> >>> > Created /Users/mike/perf-test/target/perf-test-0.1.0-SNAPSHOT.jar
> >>> > Created
> >>> > /Users/mike/perf-test/target/perf-test-0.1.0-SNAPSHOT-standalone.jar
> >>> > $ java -jar -server target/perf-test-0.1.0-SNAPSHOT-standalone.jar
> >>> > benching foo
> >>> > WARNING: Final GC required 1.5974571326266802 % of runtime
> >>> > x86_64 Mac OS X 10.8.3 4 cpu(s)
> >>> > Java HotSpot(TM) 64-Bit Server VM 24.0-b28
> >>> > Runtime arguments:
> >>> > Evaluation count : 391582560 in 60 samples of 6526376 calls.
> >>> >       Execution time sample mean : 167.426696 ns
> >>> >              Execution time mean : 167.459429 ns
> >>> > Execution time sample std-deviation : 4.079466 ns
> >>> >     Execution time std-deviation : 4.097819 ns
> >>> >    Execution time lower quantile : 160.742869 ns ( 2.5%)
> >>> >    Execution time upper quantile : 175.453376 ns (97.5%)
> >>> >                    Overhead used : 1.634996 ns
> >>> >
> >>> > Found 2 outliers in 60 samples (3.3333 %)
> >>> > low-severe 2 (3.3333 %)
> >>> >  Variance from outliers : 12.5602 % Variance is moderately inflated
> by
> >>> > outliers
> >>> > benching bar
> >>> > x86_64 Mac OS X 10.8.3 4 cpu(s)
> >>> > Java HotSpot(TM) 64-Bit Server VM 24.0-b28
> >>> > Runtime arguments:
> >>> > Evaluation count : 2174037300 in 60 samples of 36233955 calls.
> >>> >       Execution time sample mean : 26.068923 ns
> >>> >              Execution time mean : 26.066422 ns
> >>> > Execution time sample std-deviation : 0.887937 ns
> >>> >     Execution time std-deviation : 0.916861 ns
> >>> >    Execution time lower quantile : 23.996763 ns ( 2.5%)
> >>> >    Execution time upper quantile : 27.911936 ns (97.5%)
> >>> >                    Overhead used : 1.634996 ns
> >>> >
> >>> > Found 3 outliers in 60 samples (5.0000 %)
> >>> > low-severe 1 (1.6667 %)
> >>> > low-mild 1 (1.6667 %)
> >>> > high-mild 1 (1.6667 %)
> >>> >  Variance from outliers : 22.1874 % Variance is moderately inflated
> by
> >>> > outliers
> >>> > benching baz
> >>> > x86_64 Mac OS X 10.8.3 4 cpu(s)
> >>> > Java HotSpot(TM) 64-Bit Server VM 24.0-b28
> >>> > Runtime arguments:
> >>> > Evaluation count : 2270676660 in 60 samples of 37844611 calls.
> >>> >       Execution time sample mean : 25.834142 ns
> >>> >              Execution time mean : 25.837429 ns
> >>> > Execution time sample std-deviation : 0.718382 ns
> >>> >     Execution time std-deviation : 0.729431 ns
> >>> >    Execution time lower quantile : 24.837925 ns ( 2.5%)
> >>> >    Execution time upper quantile : 27.595781 ns (97.5%)
> >>> >                    Overhead used : 1.634996 ns
> >>> >
> >>> > Found 4 outliers in 60 samples (6.6667 %)
> >>> > low-severe 2 (3.3333 %)
> >>> > low-mild 2 (3.3333 %)
> >>> >  Variance from outliers : 15.7591 % Variance is moderately inflated
> by
> >>> > outliers
> >>> > done benching
> >>> >
> >>> > --
> >>> > --
> >>> > You received this message because you are subscribed to the Google
> >>> > Groups "Clojure" group.
> >>> > To post to this group, send email to clo...@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+u...@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+u...@googlegroups.com.
> >>> > For more options, visit https://groups.google.com/groups/opt_out.
> >>
> >> --
> >> --
> >> 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 a topic in the
> >> Google Groups "Clojure" group.
> >> To unsubscribe from this topic, visit
> >> https://groups.google.com/d/topic/clojure/x86VygZYf4Y/unsubscribe.
> >> To unsubscribe from this group and all its topics, send an email to
> >> clojure+unsubscr...@googlegroups.com.
> >>
> >> For more options, visit https://groups.google.com/groups/opt_out.
> >
> >
> > --
> > --
> > 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/groups/opt_out.
>
> --
> --
> 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 a topic in the
> Google Groups "Clojure" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/clojure/x86VygZYf4Y/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> clojure+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>

-- 
-- 
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/groups/opt_out.

Reply via email to