Idiom of Local Final Variables for Locks

classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|

Idiom of Local Final Variables for Locks

JSR166 Concurrency mailing list
Good afternoon,

in the java.util.concurrent.* classes, the idiom for using ReentrantLock
seems to be:

public void foo() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
... do something
} finally {
lock.unlock();
}
}

e.g. ArrayBlockingQueue.offer(e)

In other parts of the JDK, we don't see that idiom at all, and instead see:

public void foo() {
lock.lock();
try {
... do something
} finally {
lock.unlock();
}
}

e.g. ServerSocketChannelImpl.accept()

I've always seen this as a style idiom, rather than for pure
performance. Am I correct? I looked at the generated assembly code with
JITWatch, but didn't really see anything that would give a big edge,
especially in comparison to all the other things going on.

Would love to hear your views on this.

Regards

Heinz
--
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java™ Specialists' Newsletter" - www.javaspecialists.eu
Java Champion - www.javachampions.org
JavaOne Rock Star Speaker
Tel: +30 69 75 595 262
Skype: kabutz

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Idiom of Local Final Variables for Locks

JSR166 Concurrency mailing list
This idiom may have originated in a 2003 workaround for a hotspot behavior:

Doug Lea writes to concurrency-jsr list in 2003:

---
I figured out from an exchange with hotspot folks that hotspot does
not reliably cache final fields in the presence of volatile/atomic
operations -- that is, if there are two references to a final field in a
method using volatiles, it loads it twice even though it need not
under JSR133 JMM. This can hurt in some uses of Lock objects, where
I've changed our code to manually cache, as below.  Hopefully hotspot
will start doing this automatically sometime soon so other people
aren't tempted to manually tweak, but for us, and for now, doing this
is worthwhile. (Automation inside a JVM is slightly trickier than it
looks because you need to be careful about aliasing.)

   class X {
      final ReentrantLock lock = new ReentrantLock();
      void f() {
   +    ReentrantLock lock = this.lock; // now added
        lock.lock();
        try { ... }
        finally { lock.unlock(); }
      }
   }   
---

On Tue, Jul 23, 2019 at 6:46 AM Dr Heinz M. Kabutz via Concurrency-interest <[hidden email]> wrote:
Good afternoon,

in the java.util.concurrent.* classes, the idiom for using ReentrantLock
seems to be:

public void foo() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
... do something
} finally {
lock.unlock();
}
}

e.g. ArrayBlockingQueue.offer(e)

In other parts of the JDK, we don't see that idiom at all, and instead see:

public void foo() {
lock.lock();
try {
... do something
} finally {
lock.unlock();
}
}

e.g. ServerSocketChannelImpl.accept()

I've always seen this as a style idiom, rather than for pure
performance. Am I correct? I looked at the generated assembly code with
JITWatch, but didn't really see anything that would give a big edge,
especially in comparison to all the other things going on.

Would love to hear your views on this.

Regards

Heinz
--
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java™ Specialists' Newsletter" - www.javaspecialists.eu
Java Champion - www.javachampions.org
JavaOne Rock Star Speaker
Tel: +30 69 75 595 262
Skype: kabutz

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Idiom of Local Final Variables for Locks

JSR166 Concurrency mailing list
Sent Dec. 23, 2003

On Tue, Jul 23, 2019 at 7:09 AM Joe Bowbeer <[hidden email]> wrote:
This idiom may have originated in a 2003 workaround for a hotspot behavior:

Doug Lea writes to concurrency-jsr list in 2003:

---
I figured out from an exchange with hotspot folks that hotspot does
not reliably cache final fields in the presence of volatile/atomic
operations -- that is, if there are two references to a final field in a
method using volatiles, it loads it twice even though it need not
under JSR133 JMM. This can hurt in some uses of Lock objects, where
I've changed our code to manually cache, as below.  Hopefully hotspot
will start doing this automatically sometime soon so other people
aren't tempted to manually tweak, but for us, and for now, doing this
is worthwhile. (Automation inside a JVM is slightly trickier than it
looks because you need to be careful about aliasing.)

   class X {
      final ReentrantLock lock = new ReentrantLock();
      void f() {
   +    ReentrantLock lock = this.lock; // now added
        lock.lock();
        try { ... }
        finally { lock.unlock(); }
      }
   }   
---

On Tue, Jul 23, 2019 at 6:46 AM Dr Heinz M. Kabutz via Concurrency-interest <[hidden email]> wrote:
Good afternoon,

in the java.util.concurrent.* classes, the idiom for using ReentrantLock
seems to be:

public void foo() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
... do something
} finally {
lock.unlock();
}
}

e.g. ArrayBlockingQueue.offer(e)

In other parts of the JDK, we don't see that idiom at all, and instead see:

public void foo() {
lock.lock();
try {
... do something
} finally {
lock.unlock();
}
}

e.g. ServerSocketChannelImpl.accept()

I've always seen this as a style idiom, rather than for pure
performance. Am I correct? I looked at the generated assembly code with
JITWatch, but didn't really see anything that would give a big edge,
especially in comparison to all the other things going on.

Would love to hear your views on this.

Regards

Heinz
--
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java™ Specialists' Newsletter" - www.javaspecialists.eu
Java Champion - www.javachampions.org
JavaOne Rock Star Speaker
Tel: +30 69 75 595 262
Skype: kabutz

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Idiom of Local Final Variables for Locks

JSR166 Concurrency mailing list
In reply to this post by JSR166 Concurrency mailing list
Thanks so much Joe.  So there was a performance gain at some point, but hopefully HotSpot fixed that, I guess :-)
Regards

Heinz
-- 
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java™ Specialists' Newsletter" - www.javaspecialists.eu
Java Champion - www.javachampions.org
JavaOne Rock Star Speaker
Tel: +30 69 75 595 262
Skype: kabutz


Joe Bowbeer wrote:
This idiom may have originated in a 2003 workaround for a hotspot behavior:

Doug Lea writes to concurrency-jsr list in 2003:

---
I figured out from an exchange with hotspot folks that hotspot does
not reliably cache final fields in the presence of volatile/atomic
operations -- that is, if there are two references to a final field in a
method using volatiles, it loads it twice even though it need not
under JSR133 JMM. This can hurt in some uses of Lock objects, where
I've changed our code to manually cache, as below.  Hopefully hotspot
will start doing this automatically sometime soon so other people
aren't tempted to manually tweak, but for us, and for now, doing this
is worthwhile. (Automation inside a JVM is slightly trickier than it
looks because you need to be careful about aliasing.)

   class X {
      final ReentrantLock lock = new ReentrantLock();
      void f() {
   +    ReentrantLock lock = this.lock; // now added
        lock.lock();
        try { ... }
        finally { lock.unlock(); }
      }
   }   
---

On Tue, Jul 23, 2019 at 6:46 AM Dr Heinz M. Kabutz via Concurrency-interest <[hidden email]> wrote:
Good afternoon,

in the java.util.concurrent.* classes, the idiom for using ReentrantLock
seems to be:

public void foo() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
... do something
} finally {
lock.unlock();
}
}

e.g. ArrayBlockingQueue.offer(e)

In other parts of the JDK, we don't see that idiom at all, and instead see:

public void foo() {
lock.lock();
try {
... do something
} finally {
lock.unlock();
}
}

e.g. ServerSocketChannelImpl.accept()

I've always seen this as a style idiom, rather than for pure
performance. Am I correct? I looked at the generated assembly code with
JITWatch, but didn't really see anything that would give a big edge,
especially in comparison to all the other things going on.

Would love to hear your views on this.

Regards

Heinz
--
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java™ Specialists' Newsletter" - www.javaspecialists.eu
Java Champion - www.javachampions.org
JavaOne Rock Star Speaker
Tel: +30 69 75 595 262
Skype: kabutz

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Idiom of Local Final Variables for Locks

JSR166 Concurrency mailing list


On Tue, Jul 23, 2019 at 8:10 AM Dr Heinz M. Kabutz via Concurrency-interest <[hidden email]> wrote:
Thanks so much Joe.  So there was a performance gain at some point, but hopefully HotSpot fixed that, I guess :-)

Sorry, no.


But even if we could totally trust hotspot, it's such a good practice (for correctness and performance) to copy fields into locals in concurrency libraries that we would probably keep on doing it.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Idiom of Local Final Variables for Locks

JSR166 Concurrency mailing list
Furthermore, the JLS 17.5.3 said
"In some cases, such as deserialization, the system will need to change the final fields of an object after construction. final fields can be changed via reflection and other implementation-dependent means."

so if a JIT want to optimize a subsequent read of a final field, it has to prove that there is not access to reflection, JNI, Unsafe, MethodHandle, in between.
Given that Reentrant.lock() can call park() which is a C call, i doubt Hotspot will be ever be able to optimize that code apart if park() is artificially marked has "this is a pure function".

regards,
Rémi


De: "concurrency-interest" <[hidden email]>
À: "Dr Heinz M. Kabutz" <[hidden email]>
Cc: "Joe Bowbeer" <[hidden email]>, "concurrency-interest" <[hidden email]>
Envoyé: Jeudi 25 Juillet 2019 01:42:46
Objet: Re: [concurrency-interest] Idiom of Local Final Variables for Locks


On Tue, Jul 23, 2019 at 8:10 AM Dr Heinz M. Kabutz via Concurrency-interest <[hidden email]> wrote:
Thanks so much Joe.  So there was a performance gain at some point, but hopefully HotSpot fixed that, I guess :-)

Sorry, no.


But even if we could totally trust hotspot, it's such a good practice (for correctness and performance) to copy fields into locals in concurrency libraries that we would probably keep on doing it.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Idiom of Local Final Variables for Locks

JSR166 Concurrency mailing list

On Jul 24, 2019, at 5:24 PM, Remi Forax via Concurrency-interest <[hidden email]> wrote:

Furthermore, the JLS 17.5.3 said
"In some cases, such as deserialization, the system will need to change the final fields of an object after construction. final fields can be changed via reflection and other implementation-dependent means."

so if a JIT want to optimize a subsequent read of a final field, it has to prove that there is not access to reflection, JNI, Unsafe, MethodHandle, in between.
Given that Reentrant.lock() can call park() which is a C call, i doubt Hotspot will be ever be able to optimize that code apart if park() is artificially marked has "this is a pure function".

Actually, this is optimize-able. And doing so turns out to be a win in some surprisingly common code idioms

We optimize instance-final fields in Zing (with Falcon) with our "Truly Final" speculative optimization. I.e. we speculate that instance fields declared final are actually and truly final, and deal with the exceptional cases where they are not. The JIT speculatively assumes that instance final fields will not be modified, and optimizes based the (registered) assumption (much like unguarded inlining of monomorphic non-final methods works). The JVM enforces the assumption by intercepting any changes to instance final fields by e.g. reflection, Unsafe, JNI, or MethodHandles, and will de-optimize assumption-dependent methods before any changes to the fields that they assume are final can take hold.

My favorite example of this (surprisingly) taking hold comes from code that looks like this, and is quite common in the wild:

class FastDoof {
    private final long[] buf = new int[MAX_BUFLEN];
    private long residual;
    …

    public long doSomethingFast(int val) {
      for (int i = 0; i < buf.length; i++) {
        computeResidual(buf[i], residual);
      }
      return residual;
    }
    …
}

It turns how that (without the Truly Final optimization) the following code is measurably faster on HotSpot:

    public long doSomethingFast(int val) {
      final localBuf = buf;
      for (int val : buf) {
        computeResidual(val, residual);
      }
      return residual;
    }

That is because the loop above is actually the semantic equivalent of this code:

    public long doSomethingFast(int val) {
      final localBuf = buf;
      for (int i = 0; i < localBuf.length; i++) {
        computeResidual(localBuf[i], residual);
      }
      return residual;
    }

Which allows the hoisting of the array range check out of the loop (since localBuf is actually known to remain final during the loop execution).

With the Truly Final optimizations (which Zing does right now), all the loop versions above end up doing the same thing...

You can see more on this in Nistan Wakart's blog entry here: http://psy-lob-saw.blogspot.com/2014/02/when-i-say-final-i-mean-final.html

But, with all that said, I agree that the idiom itself is "much better" for the writing of lock code in methods, because it insolates the locking code's correctness from the potential of the lock field itself being (mistakenly declared non-final, or made practically-non-final for any reason...



regards,
Rémi


De: "concurrency-interest" <[hidden email]>
À: "Dr Heinz M. Kabutz" <[hidden email]>
Cc: "Joe Bowbeer" <[hidden email]>, "concurrency-interest" <[hidden email]>
Envoyé: Jeudi 25 Juillet 2019 01:42:46
Objet: Re: [concurrency-interest] Idiom of Local Final Variables for Locks


On Tue, Jul 23, 2019 at 8:10 AM Dr Heinz M. Kabutz via Concurrency-interest <[hidden email]> wrote:
Thanks so much Joe.  So there was a performance gain at some point, but hopefully HotSpot fixed that, I guess :-)

Sorry, no.


But even if we could totally trust hotspot, it's such a good practice (for correctness and performance) to copy fields into locals in concurrency libraries that we would probably keep on doing it.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

signature.asc (849 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Idiom of Local Final Variables for Locks

JSR166 Concurrency mailing list
This is a very good idea. I even wonder if there can be bug-free code that modifies final fields after first use at all. That is, breaking the assumption of immutability can be allowed to be expensive. For example I cannot see how a loop with the buffer can remain correct, if the final buffer were allowed to change during the loop. Or how the mutual exclusion be guaranteed, if the lock instance were allowed to change while any thread has access to a different instance (even if it is saved in a method local).

Alex

On Thu, 25 Jul 2019, 02:22 Gil Tene via Concurrency-interest, <[hidden email]> wrote:

On Jul 24, 2019, at 5:24 PM, Remi Forax via Concurrency-interest <[hidden email]> wrote:

Furthermore, the JLS 17.5.3 said
"In some cases, such as deserialization, the system will need to change the final fields of an object after construction. final fields can be changed via reflection and other implementation-dependent means."

so if a JIT want to optimize a subsequent read of a final field, it has to prove that there is not access to reflection, JNI, Unsafe, MethodHandle, in between.
Given that Reentrant.lock() can call park() which is a C call, i doubt Hotspot will be ever be able to optimize that code apart if park() is artificially marked has "this is a pure function".

Actually, this is optimize-able. And doing so turns out to be a win in some surprisingly common code idioms

We optimize instance-final fields in Zing (with Falcon) with our "Truly Final" speculative optimization. I.e. we speculate that instance fields declared final are actually and truly final, and deal with the exceptional cases where they are not. The JIT speculatively assumes that instance final fields will not be modified, and optimizes based the (registered) assumption (much like unguarded inlining of monomorphic non-final methods works). The JVM enforces the assumption by intercepting any changes to instance final fields by e.g. reflection, Unsafe, JNI, or MethodHandles, and will de-optimize assumption-dependent methods before any changes to the fields that they assume are final can take hold.

My favorite example of this (surprisingly) taking hold comes from code that looks like this, and is quite common in the wild:

class FastDoof {
    private final long[] buf = new int[MAX_BUFLEN];
    private long residual;
    …

    public long doSomethingFast(int val) {
      for (int i = 0; i < buf.length; i++) {
        computeResidual(buf[i], residual);
      }
      return residual;
    }
    …
}

It turns how that (without the Truly Final optimization) the following code is measurably faster on HotSpot:

    public long doSomethingFast(int val) {
      final localBuf = buf;
      for (int val : buf) {
        computeResidual(val, residual);
      }
      return residual;
    }

That is because the loop above is actually the semantic equivalent of this code:

    public long doSomethingFast(int val) {
      final localBuf = buf;
      for (int i = 0; i < localBuf.length; i++) {
        computeResidual(localBuf[i], residual);
      }
      return residual;
    }

Which allows the hoisting of the array range check out of the loop (since localBuf is actually known to remain final during the loop execution).

With the Truly Final optimizations (which Zing does right now), all the loop versions above end up doing the same thing...

You can see more on this in Nistan Wakart's blog entry here: http://psy-lob-saw.blogspot.com/2014/02/when-i-say-final-i-mean-final.html

But, with all that said, I agree that the idiom itself is "much better" for the writing of lock code in methods, because it insolates the locking code's correctness from the potential of the lock field itself being (mistakenly declared non-final, or made practically-non-final for any reason...



regards,
Rémi


De: "concurrency-interest" <[hidden email]>
À: "Dr Heinz M. Kabutz" <[hidden email]>
Cc: "Joe Bowbeer" <[hidden email]>, "concurrency-interest" <[hidden email]>
Envoyé: Jeudi 25 Juillet 2019 01:42:46
Objet: Re: [concurrency-interest] Idiom of Local Final Variables for Locks


On Tue, Jul 23, 2019 at 8:10 AM Dr Heinz M. Kabutz via Concurrency-interest <[hidden email]> wrote:
Thanks so much Joe.  So there was a performance gain at some point, but hopefully HotSpot fixed that, I guess :-)

Sorry, no.


But even if we could totally trust hotspot, it's such a good practice (for correctness and performance) to copy fields into locals in concurrency libraries that we would probably keep on doing it.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Idiom of Local Final Variables for Locks

JSR166 Concurrency mailing list
We've been doing this in RxJava for years. However, there is another end to the optimization spectrum we found: loading up too many fields into local variables. At this point, some spill onto the stack, sometimes the most frequently needed ones, so we are back at square one.

Alex Otenko via Concurrency-interest <[hidden email]> ezt írta (időpont: 2019. júl. 25., Cs, 8:00):
This is a very good idea. I even wonder if there can be bug-free code that modifies final fields after first use at all. That is, breaking the assumption of immutability can be allowed to be expensive. For example I cannot see how a loop with the buffer can remain correct, if the final buffer were allowed to change during the loop. Or how the mutual exclusion be guaranteed, if the lock instance were allowed to change while any thread has access to a different instance (even if it is saved in a method local).

Alex

On Thu, 25 Jul 2019, 02:22 Gil Tene via Concurrency-interest, <[hidden email]> wrote:

On Jul 24, 2019, at 5:24 PM, Remi Forax via Concurrency-interest <[hidden email]> wrote:

Furthermore, the JLS 17.5.3 said
"In some cases, such as deserialization, the system will need to change the final fields of an object after construction. final fields can be changed via reflection and other implementation-dependent means."

so if a JIT want to optimize a subsequent read of a final field, it has to prove that there is not access to reflection, JNI, Unsafe, MethodHandle, in between.
Given that Reentrant.lock() can call park() which is a C call, i doubt Hotspot will be ever be able to optimize that code apart if park() is artificially marked has "this is a pure function".

Actually, this is optimize-able. And doing so turns out to be a win in some surprisingly common code idioms

We optimize instance-final fields in Zing (with Falcon) with our "Truly Final" speculative optimization. I.e. we speculate that instance fields declared final are actually and truly final, and deal with the exceptional cases where they are not. The JIT speculatively assumes that instance final fields will not be modified, and optimizes based the (registered) assumption (much like unguarded inlining of monomorphic non-final methods works). The JVM enforces the assumption by intercepting any changes to instance final fields by e.g. reflection, Unsafe, JNI, or MethodHandles, and will de-optimize assumption-dependent methods before any changes to the fields that they assume are final can take hold.

My favorite example of this (surprisingly) taking hold comes from code that looks like this, and is quite common in the wild:

class FastDoof {
    private final long[] buf = new int[MAX_BUFLEN];
    private long residual;
    …

    public long doSomethingFast(int val) {
      for (int i = 0; i < buf.length; i++) {
        computeResidual(buf[i], residual);
      }
      return residual;
    }
    …
}

It turns how that (without the Truly Final optimization) the following code is measurably faster on HotSpot:

    public long doSomethingFast(int val) {
      final localBuf = buf;
      for (int val : buf) {
        computeResidual(val, residual);
      }
      return residual;
    }

That is because the loop above is actually the semantic equivalent of this code:

    public long doSomethingFast(int val) {
      final localBuf = buf;
      for (int i = 0; i < localBuf.length; i++) {
        computeResidual(localBuf[i], residual);
      }
      return residual;
    }

Which allows the hoisting of the array range check out of the loop (since localBuf is actually known to remain final during the loop execution).

With the Truly Final optimizations (which Zing does right now), all the loop versions above end up doing the same thing...

You can see more on this in Nistan Wakart's blog entry here: http://psy-lob-saw.blogspot.com/2014/02/when-i-say-final-i-mean-final.html

But, with all that said, I agree that the idiom itself is "much better" for the writing of lock code in methods, because it insolates the locking code's correctness from the potential of the lock field itself being (mistakenly declared non-final, or made practically-non-final for any reason...



regards,
Rémi


De: "concurrency-interest" <[hidden email]>
À: "Dr Heinz M. Kabutz" <[hidden email]>
Cc: "Joe Bowbeer" <[hidden email]>, "concurrency-interest" <[hidden email]>
Envoyé: Jeudi 25 Juillet 2019 01:42:46
Objet: Re: [concurrency-interest] Idiom of Local Final Variables for Locks


On Tue, Jul 23, 2019 at 8:10 AM Dr Heinz M. Kabutz via Concurrency-interest <[hidden email]> wrote:
Thanks so much Joe.  So there was a performance gain at some point, but hopefully HotSpot fixed that, I guess :-)

Sorry, no.


But even if we could totally trust hotspot, it's such a good practice (for correctness and performance) to copy fields into locals in concurrency libraries that we would probably keep on doing it.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


--
Best regards,
David Karnok

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Idiom of Local Final Variables for Locks

JSR166 Concurrency mailing list
In reply to this post by JSR166 Concurrency mailing list


On Wed, Jul 24, 2019 at 10:58 PM Alex Otenko <[hidden email]> wrote:
This is a very good idea. I even wonder if there can be bug-free code that modifies final fields after first use at all. That is, breaking the assumption of immutability can be allowed to be expensive. For example I cannot see how a loop with the buffer can remain correct, if the final buffer were allowed to change during the loop. Or how the mutual exclusion be guaranteed, if the lock instance were allowed to change while any thread has access to a different instance (even if it is saved in a method local).

Whether a field is __declared__ final can be mostly irrelevant to the JIT.  Effective finality can often be deduced from the code.

In practice, final fields are often modified in pseudo-constructors, e.g. via CopyOnWriteArrayList.resetLock, while the object is still thread-confined.  Java's object construction design bug.

---

Our own ArrayDeque is full of
        final Object[] es = elements;
so that we can access the backing array more efficiently.  Here it would be tougher for Zing to optimize because "elements" is not final and is in fact often modified.  BUT ArrayDeque is not thread-safe and if another thread modifies "elements" while an ArrayDeque method executes, that's a user's race bug!  

Zing could speculatively optimize an arbitrary loop over any array field, but it would have to deoptimize on concurrent modification of the field, and detecting concurrent access (reliably!) seems expensive.
 

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Idiom of Local Final Variables for Locks

JSR166 Concurrency mailing list


Sent from my iPad

On Jul 25, 2019, at 6:55 AM, Martin Buchholz <[hidden email]> wrote:



On Wed, Jul 24, 2019 at 10:58 PM Alex Otenko <[hidden email]> wrote:
This is a very good idea. I even wonder if there can be bug-free code that modifies final fields after first use at all. That is, breaking the assumption of immutability can be allowed to be expensive. For example I cannot see how a loop with the buffer can remain correct, if the final buffer were allowed to change during the loop. Or how the mutual exclusion be guaranteed, if the lock instance were allowed to change while any thread has access to a different instance (even if it is saved in a method local).

Whether a field is __declared__ final can be mostly irrelevant to the JIT.  Effective finality can often be deduced from the code.

The “Truly Final” optimization deals [only] with declare-as-final fields. The fact that the language will prevent java code from doing any modifications (to a decayed final field) outside of the specific mechanisms noted before (reflection, methodhandles, unsafe and JNI) makes it easy both for analysis and intercepts. And truly final picks up optimizations on idiomatic truly final uses (which are quite common) where, without it, other optimizations are prevented when e.g. volatile access or method calls occur in loops. The range check elimination on a private instance-final buffer is a common example of this.

Zing also has an “Effectively Final” optimization coming out, which applies the same benefits to fields that are not declared final, but can be proven to be [currently] following the same behavior. We make no attempt to speculate about lack of concurrent modification for this, and instead apply it only to fields for which code analysis and other speculations can together prove “effective finality”. The scope of cases for which we can actually prove this is growing over time. Where started simply with declared-private fields that are modified exactly once, and only in constructors. This could then be expand with more code analysis, e.g. to modified-once but outside a constructor (a common case found in idiomatic code when multiple constructors call a common init method that is only called from constructors, and only once from each constructor), or to multiple possible modifications of private fields  but speculating that hey don’t happen (e.g. a setter method does exist, but was never actually called). It can be expanded to protected fields (requires analysis across all current subclasses, and re-analysis on class loading), and even to public fields (requires analysis across all classes and upon each class load). But with each scope extension (beyond private fields), the amount of additional common idiomatic code that the optimization would provide benefits to tend to drops, as the likelihood of finding a public field that is effectively final in modern, idiomatic java code that is also “hot” enough to cate about seems to be pretty low. [they do exist, but they are generally more rare that private fields that are effectively final].


In practice, final fields are often modified in pseudo-constructors, e.g. via CopyOnWriteArrayList.resetLock, while the object is still thread-confined.  Java's object construction design bug.

---

Our own ArrayDeque is full of
        final Object[] es = elements;
so that we can access the backing array more efficiently.  Here it would be tougher for Zing to optimize because "elements" is not final and is in fact often modified.  BUT ArrayDeque is not thread-safe and if another thread modifies "elements" while an ArrayDeque method executes, that's a user's race bug!  

Yup. Zing would not attempt this sort of thing (where the elements field is actually often modified). At least not with the current Truly Final or Effectively Final optimizations. They are aimed at other fish in the sea, and there are plenty of those in common code...

Code like the above, which samples an actually modifiable (and in practice modified)  field into a local variable for speed is solving a real problem (and taking responsibility for correctness when concurrent of the field exists). When the same pattern is used purely to get speed for truly final or effectively final fields (compensating fur the compiler not recognizing those situations), the Truly Final and Effectively Final optimizations make it unnneccesary.


Zing could speculatively optimize an arbitrary loop over any array field, but it would have to deoptimize on concurrent modification of the field, and detecting concurrent access (reliably!) seems expensive.

Correct. Detecting concurrent modification when it cannot be disproved based on other assumptions is “hard”, and we don’t try to do that.

But making some (speculative) assumptions on other things and provably deducing no concurrent modification from those assumptions (as long as the assumptions hold) works. Truly final optimizations prove that there is no concurrent modification is possible as long as the field remains truly final. The same goes for Effectively Final. There is plenty of code out there where people haven’t identified the speed deficiencies and compensated for them by caching into a local variable, and even more code where such caching would probably be incorrect (e.g. wrong to do for some external use cases of the same code), but the speculative assumptions can prove it to be “correct right now” in a lasting way for specific executions or applications.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest