[concurrency-interest] LongAdder with custom behavior?

Carl Mastrangelo notcarl at google.com
Mon Dec 4 14:58:30 EST 2017


Inline responses

On Sat, Dec 2, 2017 at 4:00 AM, Peter Levart <peter.levart at gmail.com> wrote:

> Hi Carl,
>
> Carl Mastrangelo je 01. 12. 2017 ob 20:12 napisal:
>
>
>>
>> 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.
>
>
> The name I gave - 'sumThenResetAtomic' is not correct. There's nothing
> atomic about the method. If one wanted to use it as is in a concurrent
> environment it should at least be synchronized so that  concurrent threads
> calling it do the right thing - to support your use case.
>

> So my answer is speculative: probably because it would require
> synchronization and LongAdder was designed to not use any locks. If one
> wants to do it that way, (s)he can. LongAdder is just a low-level primitive
> with which you can build your concurrent logic.
>

Adding locks would be tantamount to just not using LongAdder at all.


>
> I'm wondering why you need a fail-fast isEmpty() method though. Do you
> need to call it frequently? Why?
>

Well, I would like to call it frequently.  I'm playing around with SPSC and
MPSC queues, and I need an approximate size of the queue.  In the MPSC
queue case (so called Vyukov queue) its possible for the queue to appear
empty briefly while being updated, so having a size counter would be nice.
There may be better ways of doing what I want, but it seemed reasonably
fast enough to use LongAdder.

As for speed, I would like to call sumThenReset as much as 10 million times
per second, so it should be not so expensive.


>
> Regards, Peter
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20171204/28c2c58d/attachment.html>


More information about the Concurrency-interest mailing list