|Functions using locks are slowed even when locks are never taken||Michael Blume||11/1/13 11:34 AM|
|Re: Functions using locks are slowed even when locks are never taken||Michael Blume||11/1/13 11:53 AM|
Since 1.6 alpha is out, I reran the tests with that -- basically the same results.
|Re: Functions using locks are slowed even when locks are never taken||Trenton Strong||11/2/13 12:01 AM|
Verified that I receive the same result patterns as you on my machine.
One other possibility outside of what you have already mentioned would be something JIT-related. Perhaps there is an optimization that can be performed if the locking sections of the code are in another function but otherwise no, but I'm not sure how that dovetails with Clojure' fn compilation.
|Re: Functions using locks are slowed even when locks are never taken||Michael Blume||11/2/13 5:32 PM|
Hmm, it seems like if it were JIT related you'd see the same issue with java code, but I get 5ns across the board.
|Re: Functions using locks are slowed even when locks are never taken||shlomi...@gmail.com||11/3/13 2:47 AM|
same results on my machine as well.
I tried decompiling the jar (with cider-decompile), and the parts that actually run (the "then" clause) seems almost identical. If you'r interested, i can post them later tonight.
|Re: Functions using locks are slowed even when locks are never taken||Michał Marczyk||11/3/13 9:30 AM|
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:
(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
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.
> 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
> For more options, visit this group at
> 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.
|Re: Functions using locks are slowed even when locks are never taken||Michał Marczyk||11/3/13 9:45 AM|
I should perhaps make clear that with direct use of monitor-enter and
monitor-exit with a Var it's possible for monitor-enter and
monitor-exit to operate on different objects even in the absence of
typos, namely if somebody rebinds the Var. To illustrate this with
print at the REPL (a regular Clojure REPL, as opposed to nREPL or
similar, in case there are any issues with reproducing this in
(def x 1)
(future (print x) (Thread/sleep 5000) (print x))
; prints 1 immediately, but then goes to sleep;
; in the meantime, I say
(def x 2)
; and after a moment 2 is printed
NB. this could also happen with alter-var-root or set! to a Var with a
|Re: Functions using locks are slowed even when locks are never taken||Andy Fingerhut||11/3/13 9:46 AM|
Good detective work, Michal.So the extra time for the slower version was because a Var was always being accessed in the generated byte code, even if the source code was only explicitly accessing the Var in a branch that was never executed?
|Re: Functions using locks are slowed even when locks are never taken||Michael Blume||11/3/13 11:09 AM|
foo' is fast, but foo'' is slow. So something about wrapping the locking clause in a let makes it fast. Still no idea why.
|Re: Functions using locks are slowed even when locks are never taken||Michael Blume||11/3/13 11:46 AM|
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?
|Re: Functions using locks are slowed even when locks are never taken||Michał Marczyk||11/3/13 12:10 PM|
Well, that is interesting.
The difference between the compiled versions of
(let [res (locking ois 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...
|Re: Functions using locks are slowed even when locks are never taken||Michael Blume||11/17/13 10:14 AM|
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.
|Re: Functions using locks are slowed even when locks are never taken||Andy Fingerhut||11/17/13 2:11 PM|
I have just created a ticket linking to this discussion, with a copy of one of Michal's earlier messages in the thread as a description of what might be the problem.I would not say that this is the same as movement on this issue. Movement in the form that would lead to a change in the Clojure compiler will require one or more volunteers to carefully diagnose the root cause of the problem, develop one or more patches, respond to comments from screeners and/or Rich Hickey, etc. If people want to raise more attention to this issue, one way to do it is to vote on the ticket in JIRA. If you do not already have an account, you can create one here:
|Re: Functions using locks are slowed even when locks are never taken||Andy Fingerhut||11/17/13 2:12 PM|
Sorry, I neglected to include a link to the ticket: