Defer performance

650 views
Skip to first unread message

manxi...@gmail.com

unread,
Aug 31, 2015, 10:58:54 AM8/31/15
to golang-nuts

Hi,

I'm profiling a Go program and I've noticed that "defer" instruction CPU usage is the 18% of the total CPU program usage. Is this normal?

My code is like this:
    c.mutex.RLock()
    defer c.mutex.RUnlock()
    ...

Thanks,
David

Ian Lance Taylor

unread,
Aug 31, 2015, 11:22:50 AM8/31/15
to manxi...@gmail.com, golang-nuts
On Mon, Aug 31, 2015 at 3:44 AM, <manxi...@gmail.com> wrote:

I'm profiling a Go program and I've noticed that "defer" instruction CPU usage is the 18% of the total CPU program usage. Is this normal?

My code is like this:
    c.mutex.RLock()
    defer c.mutex.RUnlock()
    ...

If that's your whole program, then, yes, it is normal....

The defer statement is fairly fast, but it's true that it is not as fast as it could be.  There is some discussion of making defer run faster in https://golang.org/issue/6980 .  I also think it might be possible to speed up the fairly common case of a zero-argument defer by skipping the argument handling.

Ian 

Paul Borman

unread,
Aug 31, 2015, 1:22:39 PM8/31/15
to Ian Lance Taylor, manxi...@gmail.com, golang-nuts
I have taken the practice to doing the defer prior to the lock:

defer mu.Unlock()
mu.Lock()

defer does take time, as you noticed, so if you are going to use defer, doing it outside of the lock makes the critical section just a smidgen shorter.  Of course, even more efficient is not do a defer at all and just call Unlock after the region (and every place you can exit).

This re-ordering was suggested by Dmitry Vyukov sometime back.

    -Paul

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

manxi...@gmail.com

unread,
Sep 1, 2015, 7:34:11 AM9/1/15
to golang-nuts, ia...@golang.org, manxi...@gmail.com

Changing the order to that, will reduce thread contention, but I don't think it will make the defer instruction faster. Am I right?

Paul Borman

unread,
Sep 1, 2015, 10:42:01 AM9/1/15
to manxi...@gmail.com, golang-nuts, Ian Lance Taylor
Correct, it does not make defer faster, it only reduces lock contention (by a small amount).  It more acknowledges your point that defer is not free, which is why deferring the unlock before the lock makes sense.

    -Paul

On Tue, Sep 1, 2015 at 4:33 AM, <manxi...@gmail.com> wrote:

Changing the order to that, will reduce thread contention, but I don't think it will make the defer instruction faster. Am I right?

--

Tim K

unread,
Sep 1, 2015, 8:46:06 PM9/1/15
to golang-nuts, ia...@golang.org, manxi...@gmail.com
That approach could cause a problem. If Lock() fails (not sure how) then the deferred Unlock() will panic or worse it may actually Unlock() the mutex that another thread may have locked in the mean time introducing an ugly bug. I'm not sure it's safe to do so, unless I'm missing something.

You should probably have a very strong reason for such an optimization otherwise it's probably best to live with it and wait for the runtime to improve the performance of defer.

Caleb Spare

unread,
Sep 1, 2015, 8:57:42 PM9/1/15
to Tim K, golang-nuts, Ian Lance Taylor, manxi...@gmail.com
> That approach could cause a problem. If Lock() fails (not sure how) then the
> deferred Unlock() will panic or worse it may actually Unlock() the mutex
> that another thread may have locked in the mean time introducing an ugly
> bug. I'm not sure it's safe to do so, unless I'm missing something.
>
> You should probably have a very strong reason for such an optimization
> otherwise it's probably best to live with it and wait for the runtime to
> improve the performance of defer.

There is no failure mode that ordering the Lock/Unlock in the
"correct" way would handle but the "incorrect" ordering wouldn't
handle. Lock doesn't return an error, for instance. So I don't agree
with this.

-Caleb

Tim K

unread,
Sep 1, 2015, 9:05:17 PM9/1/15
to golang-nuts, tim....@gmail.com, ia...@golang.org, manxi...@gmail.com
There isn't one today but what if in the future Lock() can panic? I don't know if that would be acceptable behavior under the 1.x compatibility rules, but even if it does in 2.x it will make it hard to catch such bugs, the code will compile just fine but the logic will be wrong. 

Caleb Spare

unread,
Sep 1, 2015, 9:11:16 PM9/1/15
to Tim K, golang-nuts, Ian Lance Taylor, manxi...@gmail.com
Yes that would be a behavior change that wouldn't be allowed under the
compatibility rules. Also if Lock could panic, then this code:

mu.Lock()
defer mu.Unlock()

would still crash in that case because it doesn't catch the panic.

Tim K

unread,
Sep 1, 2015, 9:13:19 PM9/1/15
to golang-nuts, tim....@gmail.com, ia...@golang.org, manxi...@gmail.com
Right, but it would not end up unlocking the mutex that may have been locked by some other goroutine.

Paul Borman

unread,
Sep 1, 2015, 9:55:39 PM9/1/15
to Tim K, golang-nuts, Ian Lance Taylor, David M.
If Lock panics then the defer is the least of your concerns.

Fortunately, lock does not panic, it does not return an error.  Lock does what it says it does.  I have no interest in coding around a hypothetical Lock implementation that we don't use.

You are free to order those two statements how ever you want.  For an uncontested lock there is no difference in time.  For a contested lock then doing the defer first will shorten the critical section (not by much, but by an amount you can measure).  You are free to do them in either order, but both are safe (unless your program has already lost memory safety, at which point it does not matter) and functionally equivalent.

Will Newton

unread,
Sep 2, 2015, 4:04:21 AM9/2/15
to Paul Borman, Tim K, golang-nuts, Ian Lance Taylor, David M.
On 2 September 2015 at 02:55, 'Paul Borman' via golang-nuts
<golan...@googlegroups.com> wrote:
> If Lock panics then the defer is the least of your concerns.
>
> Fortunately, lock does not panic, it does not return an error. Lock does
> what it says it does. I have no interest in coding around a hypothetical
> Lock implementation that we don't use.
>
> You are free to order those two statements how ever you want. For an
> uncontested lock there is no difference in time. For a contested lock then
> doing the defer first will shorten the critical section (not by much, but by
> an amount you can measure). You are free to do them in either order, but
> both are safe (unless your program has already lost memory safety, at which
> point it does not matter) and functionally equivalent.

One measurable cost of these types of micro-optimization is that it
looks odd, so programmers who have not seen the idiom before may
change the statements back to the "right" order unless you add
comments explaining the idiom to every call site. In any case I would
expect truly performance critical critical sections to not use defer
at all.

My advice would be: do it in the natural order unless it measurably
improves performance to do otherwise, and then comment it.

--
Will Newton
Software - Cocoon (http://cocoon.life)

Paul Borman

unread,
Sep 2, 2015, 10:32:56 AM9/2/15
to Will Newton, Tim K, golang-nuts, Ian Lance Taylor, David M.
Everything looks odd the first time you see it.

a, b := b, a

t := t

Both of those look odd to a programmer unfamiliar with either concept, the can probably reason out the first one, the second is a little more difficult, but the effort of learning why they work is well worth it.

As for lok order, if you don't want to put the defer first, then don't.  If a programmer cannot figure out that

defer mu.Unlock()
mu.Lock()

is functionally equivalent to 

mu.Lock()
defer mu.Unlock()

then I don't think the main problem is the order.  There is no need to add extra comments (just like the first two examples given above).  Also, if you don't know why someone would use this order, and you stop and think about it, hopefully you will come to the conclusion that it shortens the critical section, also prompting you to think about your critical sections and making sure they are short.  Anyhow, use it or don't.  My comment was I do use it because defers are not free, which supported the OP's point.  Ian has said defers will become less expensive in the future.  Great!  I will probably continue to use this order, I like what it implies, but you can use what order you like.  It is one of the great things about freewill ;-)
Reply all
Reply to author
Forward
0 new messages