[concurrency-interest] LongAdder with custom behavior?

Carl Mastrangelo notcarl at google.com
Fri Dec 1 14:12:52 EST 2017


responses inline:

On Fri, Dec 1, 2017 at 12:07 AM, Peter Levart <peter.levart at gmail.com>

> Hi Carl,
> On 12/01/2017 08:18 AM, Carl Mastrangelo via Concurrency-interest wrote:
> While looking at LongAdder as a possible candidate for a concurrent
> counter, I noticed three things that made it seem surprising.  I am
> wondering what the rationale is for these since they don't make sense to
> me:
> 1.  LongAdder.sumThenReset seems to not be thread-safe.  If concurrent
> writes happen between reading and resetting of each Cell, then the write
> could get dropped.    This matches the behavior described in reset(), but
> makes the class less useful.   For instance, I would like sumThenReset to
> tally up all the mutations, and reset the counter back to zero without
> dropping mutations.  This would make it so I call sumThenReset later, and
> pick up any mutations I missed.
> 2.  Following up on the first point, the implementation of sumThenReset
> does two volatile operations on each Cell.value.  First it reads, followed
> by setting it to zero.  Why doesn't it use getAndSet on the value?  On the
> surface it would appear to be fewer synchronization points.   Also, it
> would nicely solve the first item.
> You can achieve similar observable effect in the following way:
>     public static long sumThenResetAtomic(LongAdder adder) {
>         long sum = adder.sum();
>         adder.add(-sum);
>         return sum;
>     }
> This has better performance, because it does N reads and potentially only
> a single CAS which presents less disturbance to concurrent ongoing
> additions than N getAndSet(s) would.

This approach would work, but why wouldn't sumThenReset implement it that
way?     It isn't clear to me that two volatile accesses is faster than a
single CAS.

> 3.   In the case that the LongAdder only ever increases in value (no
> resets), it would be possible to provide a means to see if it's equal to
> zero.  I believe this would be cheaper to implement than summing across all
> Cells.  The list of cells could be walked until there was a single nonzero
> cell, at which point it could return early.  This would be similar to the
> isEmpty() method on ConcurrentLinkedQueue, which doesn't need to walk the
> whole list.
> if you are talking about adder.isEmpty() being a cheaper variant of
> (adder.sum() == 0L), then all cells have to be summed in any case. Imagine
> an adder with 2 cells:
> cell[0] == 42L
> cell[1] == -42L

Right.  I tried to clarify that the value only ever goes up (by throwing on
increment() and negative arguments to add().  If positive numbers only ever
go in, and there is no overflow, then this would be a safe assumption.   I
realize LongAdder can't make this assumption generally, but it isn't even
possible for me to extend the class to enforce it myself.

> Regards, Peter
> The reason I bring this up is that although the LongAdder class is
> non-final, there is no way to provide an isEmpty() myself.  All the access
> to the Cells and value are package private.  If I did implement this, I
> would have to give up the nice contention features.
> Is LongAdder reusable or should I try making my own counter?  Also, were
> the issues I bring up now brought up during the design?
> Carl
> _______________________________________________
> Concurrency-interest mailing listConcurrency-interest at cs.oswego.eduhttp://cs.oswego.edu/mailman/listinfo/concurrency-interest
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20171201/4323dd9e/attachment.html>

More information about the Concurrency-interest mailing list