[concurrency-interest] JDK 9's compareAndSet vs compareAndExchange

Vitaly Davidovich vitalyd at gmail.com
Thu Sep 22 11:06:13 EDT 2016


On Thu, Sep 22, 2016 at 10:20 AM, Aleksey Shipilev <shade at redhat.com> wrote:

> Hi,
>
> Somewhat counter-intuitively, reusing compareAndExchange return value as
> the basis for retry is slower than re-reading actual value for
> compareAndSet. Because under contention, the overall progress depends on
> the width on the "collision window" for your RMW operation:
>  https://bugs.openjdk.java.net/browse/JDK-8141640

Interesting result.  What CPU was this test done on, if you recall? The #
of cycles in the "collision window" in that benchmark is fairly small (just
a bunch of cheap ALU instructions and register-to-register movs that ought
to be renamed).  Did you look at any PMU counters that would back up the
theory?

Thanks

>
>
> compareAndExchange is useful when you *have* to have the "witness value"
> against which you failed. Choosing compareAndExchange over compareAndSet
> within the loop is futile. Choosing compareAndExchange *instead* of
> compareAndSet + re-reads is sane, e.g. in
> https://github.com/boundary/high-scale-lib/blob/master/
> src/main/java/org/cliffc/high_scale_lib/NonBlockingHashMap.java#L583
>
> Thanks,
> -Aleksey
>
> On 09/22/2016 03:35 PM, Dávid Karnok wrote:
> > JDK 9's VarHandle API (and AtomicXXX classes) specify the new
> >  compareAndExchange() method. Traditionally, I wrote CAS loops like this:
> >
> > public static long getAddAndCap(AtomicLong requested, long n) {
> >     for (;;) {
> >         long current = requested.get();
> >
> >         if (current == Long.MAX_VALUE) {
> >            return Long.MAX_VALUE;
> >         }
> >         long next = current + n;
> >         if (next < 0L) {
> >             next = Long.MAX_VALUE;
> >         }
> >         if (requested.compareAndSet(current, next)) {
> >             return current;
> >         }
> >     }
> > }
> >
> > Now I can write this:
> >
> > public static long getAddAndCap(AtomicLong requested, long n) {
> >     long current = requested.get();
> >     for (;;) {
> >
> >         if (current == Long.MAX_VALUE) {
> >            return Long.MAX_VALUE;
> >         }
> >         long next = current + n;
> >         if (next < 0L) {
> >             next = Long.MAX_VALUE;
> >         }
> >         long actual = requested.compareAndExchange(current, next);
> >         if (actual == current) {
> >            return current;
> >         }
> >         current = actual;
> >     }
> > }
> >
> > I'm not sure I could JMH benchmark these under JDK 9 now so my question
> > is whether the latter pattern has lower overhead (on x86) since it
> > reuses the value returned by the underlying LOCK CMPXCHG instead of
> > re-reading the target field again (or the JIT was always smart enough to
> > detect the unnecessary re-read with compareAndSet; or the hardware is
> > smart enough by doing speculative reads in this case to hide its latency
> ?).
> >
> > --
> > Best regards,
> > David Karnok
> >
> >
> > _______________________________________________
> > Concurrency-interest mailing list
> > Concurrency-interest at cs.oswego.edu
> > http://cs.oswego.edu/mailman/listinfo/concurrency-interest
> >
>
>
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20160922/6e6e4820/attachment.html>


More information about the Concurrency-interest mailing list