RFR(L) 8195099: Low latency hashtable for read-mostly scenarios
David Holmes
david.holmes at oracle.com
Thu May 3 09:08:34 UTC 2018
On 3/05/2018 6:35 PM, Robbin Ehn wrote:
> On 05/03/2018 12:17 AM, David Holmes wrote:
>> On 2/05/2018 11:56 PM, Robbin Ehn wrote:
>>> On 2018-05-02 14:40, David Holmes wrote:
>>>> Not a review, one comment ...
>>>>
>>>> On 2/05/2018 6:31 PM, Robbin Ehn wrote:
>>>>> On 2018-04-30 21:23, Gerard Ziemski wrote:
>>>>>> #3 How come "_first" doesn't need to be volatile to be used in
>>>>>> Atomic::cmpxchg ?
>>>>>>
>>>>>> Node* _first;
>>>>>> ...
>>>>>> if (Atomic::cmpxchg(node, &_first, expect) == expect) {
>>>>>
>>>>> An implicit cast to the same compatible type which is
>>>>> volatile-qualified is okey.
>>>>
>>>> Any variable used in a lock-free manner should be declared volatile
>>>> as general good practice IMHO.
>>>
>>> Generally I agree with you. But in this case I didn't find the volatile
>>> qualifier helpful, it just clobbered the code. I didn't see any
>>> readability
>>> improvements from having essential all lines with Node* also having
>>> volatile in them.
>>
>> I'm not concerned with readability I'm concerned with doing the
>> minimum to try to ensure that a variable that can be concurrently
>> mutated and accessed in a lock-free manner is at least flagged to the
>> compiler as something it should be careful about dealing with!
>
> The minimum is la/sr. Mutating it as a volatile is a bug. Hiding bugs
> with volatile is not something I like to do. E.g. if code crashes and
> volatile makes the crash go away, you have not fixed the bug.
> So yes this is _only_ readability and consistency.
So there are never any raw accesses to these fields only load_acquire,
store_release, and atomic operations? In that case okay. And my
apologies as I did not realize that.
David
> It seem most people favor the consistency of hotspot-style, therefore I
> will follow the hotspot-style and make those volatile (_first/_next).
>
> I'll send new version on RFR thread.
>
> Thanks, Robbin
>
>>
>>> What I would like is a type that forces the use of the correct
>>> ordering, e.g.
>>> Atomic<Node*, memory_order_acq_rel> _next;
>>
>> Meanwhile
>>
>> Node * volatile _next;
>>
>> is the closest thing we have.
>>
>> David
>> -----
>>
>>> /Robbin
>>>
>>>>
>>>> David
>>>> -----
>>>>
>>>>>>
>>>>>>
>>>>>> #4 Inconsistent parameter names - in:
>>>>>>
>>>>>> template <typename LOOKUP_FUNC, typename VALUE_FUNC>
>>>>>> bool get_insert_lazy(Thread* thread, LOOKUP_FUNC& lookup,
>>>>>> VALUE_FUNC& val_f,
>>>>>> bool* grow_hint = NULL) {
>>>>>> return get_insert_lazy(thread, lookup, val_f, noOp, grow_hint);
>>>>>> }
>>>>>>
>>>>>> We use "val_f" with "_f” suffix, so we should have "lookup_f", not
>>>>>> "lookup" (many other instances) and "found_f", not "foundf" in
>>>>>>
>>>>>> template <typename LOOKUP_FUNC, typename FOUND_FUNC>
>>>>>> bool get(Thread* thread, LOOKUP_FUNC& lookup, FOUND_FUNC& foundf,
>>>>>> bool* grow_hint = NULL);
>>>>>>
>>>>>
>>>>> Fixed.
>>>>>
>>>>>>
>>>>>> #5 I wasn't familiar with CRTP, so I read up on it, but I still
>>>>>> don't see the CRTP in BaseConfig - which is the base class and
>>>>>> which is derived? In particular I don't see "class Derived :
>>>>>> public Base<Derived>" pattern here?
>>>>>
>>>>> It's the supplied config that extends the BaseConfig which is the
>>>>> derived.
>>>>>
>>>>> In gtest (test_concurrentHashtable.cpp) you will see this:
>>>>>
>>>>> // Simplest working CRPT implementation for the hash-table.
>>>>> struct Pointer : public SimpleTestTable::BaseConfig {
>>>>>
>>>>>>
>>>>>> More to come…
>>>>>>
>>>>>>
>>>>>> btw. I edited the subject of the email slightly by adding the bug
>>>>>> number to it, so it’s easy to search for.
>>>>>>
>>>>>
>>>>> Thanks, I'll send the update to the original mail.
>>>>>
>>>>> /Robbin
>>>>>
>>>>>
>>>>>
>>>>>>
>>>>>> cheers
>>>>>>
>>>>>>
>>>>>>> On Apr 26, 2018, at 2:38 AM, Robbin Ehn <robbin.ehn at oracle.com>
>>>>>>> wrote:
>>>>>>>
>>>>>>>
>>>>>>> Hi all, please review.
>>>>>>>
>>>>>>> The lower latency of the new gcs have higher demands on runtime
>>>>>>> data-structure
>>>>>>> in terms of latency. This is a concurrent hashtable using the
>>>>>>> global-counter
>>>>>>> (8195099).
>>>>>>>
>>>>>>> Webrev:
>>>>>>> http://cr.openjdk.java.net/~rehn/8195098/v0/webrev/
>>>>>>>
>>>>>>> Bug:
>>>>>>> https://bugs.openjdk.java.net/browse/JDK-8195098
>>>>>>>
>>>>>>> * Readers never blocks or spins.
>>>>>>> * Inserts do CAS from a read-side, need to re-CAS during
>>>>>>> re-size/deletion in targeted bucket or insert collision. (inserts
>>>>>>> are conceptually a reader)
>>>>>>> * Deletes locks the targeted bucket, all other buckets are free
>>>>>>> for operations.
>>>>>>> * Re-size locks one bucket at the time, all other buckets are
>>>>>>> free for operations.
>>>>>>>
>>>>>>> It does concurrent re-size by doubling size and use one more bit
>>>>>>> from hash.
>>>>>>> That means a bucket in the old table contains nodes to either one
>>>>>>> of two buckets
>>>>>>> in the new table. Each node in the chain is sorted into one of
>>>>>>> the two buckets
>>>>>>> with concurrent readers. Shrinking just take two node chains and
>>>>>>> put them
>>>>>>> together in the corresponding bucket. To keep track of what is
>>>>>>> visible to the
>>>>>>> concurrent readers it's uses a global-counter, needed during
>>>>>>> re-size and for
>>>>>>> deletes.
>>>>>>>
>>>>>>> A gtest is provided which passes on our platforms, we also have a
>>>>>>> prototype of the stringtable using this which passes tier 1-5 on
>>>>>>> our platforms.
>>>>>>>
>>>>>>> Various people have pre-reviewed various parts, thanks! And a
>>>>>>> special thanks to
>>>>>>> Coleen for a lot of reviewing!
>>>>>>>
>>>>>>> Thanks, Robbin
>>>>>>
More information about the hotspot-dev
mailing list