RFR: 8202184: Reduce time blocking the ClassSpecializer cache creating SpeciesData

2018-04-24 Thread Claes Redestad

Hi,

the current implementation of ClassSpecializer.findSpecies may cause
excessive blocking due to a potentially expensive computeIfAbsent, and
we have reason to believe this might have been cause for a few very
rare bootstrap issues in tests that attach debuggers to VM in the middle
of this.

Breaking this apart so that code generation and linking is done outside
of the computeIfAbsent helps minimize the risk that a thread gets
interrupted by a debugger at a critical point in time, while also removing
a few limitations on what we can do from a findSpecies, e.g., look up other
SpeciesData.

Bug: https://bugs.openjdk.java.net/browse/JDK-8202184
Webrev: http://cr.openjdk.java.net/~redestad/8202184/open.00/

This implementation inserts a placeholder, unresolved SpeciesData,
and then replaces it with another instance that is linked together
fully before publishing it, which ensure safe publication. There might
be alternatives involving volatiles and/or careful fencing, but I've not
been able to measure any added startup cost from this anyway.

Thanks!

/Claes


Re: RFR: 8202184: Reduce time blocking the ClassSpecializer cache creating SpeciesData

2018-04-24 Thread Paul Sandoz
Hi,

This looks good. Took me a while to understand the interactions: you need to 
replace not update otherwise there is a race on isResolved (which currently 
queries multiple state, there is no singular guard here). We could push 
isResolved into the synchronized block and simplify but every findSpecies call 
may take a small hit (or there are potentially other ways looking more closely 
at how ClassSpecializer.Factory initializes state, i.e. a fenced static field 
write, but we go further down the rabbit hole) 

I think this might fix some rare and intermittent recursive exceptions from 
ConcurrentHashMap cache we have been observing, where a collision occurs on 
keys for recursive updates (rather than for the same key).

Paul.

> On Apr 24, 2018, at 9:57 AM, Claes Redestad  wrote:
> 
> Hi,
> 
> the current implementation of ClassSpecializer.findSpecies may cause
> excessive blocking due to a potentially expensive computeIfAbsent, and
> we have reason to believe this might have been cause for a few very
> rare bootstrap issues in tests that attach debuggers to VM in the middle
> of this.
> 
> Breaking this apart so that code generation and linking is done outside
> of the computeIfAbsent helps minimize the risk that a thread gets
> interrupted by a debugger at a critical point in time, while also removing
> a few limitations on what we can do from a findSpecies, e.g., look up other
> SpeciesData.
> 
> Bug: https://bugs.openjdk.java.net/browse/JDK-8202184
> Webrev: http://cr.openjdk.java.net/~redestad/8202184/open.00/
> 
> This implementation inserts a placeholder, unresolved SpeciesData,
> and then replaces it with another instance that is linked together
> fully before publishing it, which ensure safe publication. There might
> be alternatives involving volatiles and/or careful fencing, but I've not
> been able to measure any added startup cost from this anyway.
> 
> Thanks!
> 
> /Claes



Re: RFR: 8202184: Reduce time blocking the ClassSpecializer cache creating SpeciesData

2018-04-24 Thread Peter Levart

Hi Claes,

Nice play with CHM and safe publication.

If findSpecies() is on a hot concurrent path, you might want to wrap the 
preface:


 176 S speciesData = cache.computeIfAbsent(key, new Function<>() {
 177 @Override
 178 public S apply(K key1) {
 179 return newSpeciesData(key1);
 180 }
 181 });

with a simple:

S speciesData = cache.get(key);
if (speciesData == null) {
    speciesData = cache.computeIfAbsent();
}

or even use putIfAbsent instead if redundant concurrent newSpeciesData 
is OK.


This way you can avoid hot-path synchronization with locks that CHM 
always performs in computeIfAbsent.


Regards, Peter

On 04/24/18 18:57, Claes Redestad wrote:

Hi,

the current implementation of ClassSpecializer.findSpecies may cause
excessive blocking due to a potentially expensive computeIfAbsent, and
we have reason to believe this might have been cause for a few very
rare bootstrap issues in tests that attach debuggers to VM in the middle
of this.

Breaking this apart so that code generation and linking is done outside
of the computeIfAbsent helps minimize the risk that a thread gets
interrupted by a debugger at a critical point in time, while also 
removing
a few limitations on what we can do from a findSpecies, e.g., look up 
other

SpeciesData.

Bug: https://bugs.openjdk.java.net/browse/JDK-8202184
Webrev: http://cr.openjdk.java.net/~redestad/8202184/open.00/

This implementation inserts a placeholder, unresolved SpeciesData,
and then replaces it with another instance that is linked together
fully before publishing it, which ensure safe publication. There might
be alternatives involving volatiles and/or careful fencing, but I've not
been able to measure any added startup cost from this anyway.

Thanks!

/Claes




Re: RFR: 8202184: Reduce time blocking the ClassSpecializer cache creating SpeciesData

2018-04-25 Thread Claes Redestad

Hi Peter,


On 2018-04-25 08:36, Peter Levart wrote:

Hi Claes,

Nice play with CHM and safe publication.


thanks, I was curious how you'd react to this. :-)



If findSpecies() is on a hot concurrent path, [...]


It'd be surprising if it was: findSpecies is typically called once for a 
specific SpeciesData,
and sometimes every now and then during setup of certain method handles 
(in particular
the static speciesData_L* methods in jli.BoundMethodHandle are begging 
to be turned

into lazy constants).

Besides, CHM.computeIfAbsent has a non-synchronizing fast-path for when 
the key exists,

lines 1731-1734:

    else if (fh == h    // check first node without acquiring lock
 && ((fk = f.key) == key || (fk != null && 
key.equals(fk)))

 && (fv = f.val) != null)
    return fv;

.. so I'm not sure we'd gain much from wrapping the preface with a get 
even if it was

hot and contended.

/Claes


Re: RFR: 8202184: Reduce time blocking the ClassSpecializer cache creating SpeciesData

2018-04-25 Thread Claes Redestad

Hi Paul,

On 2018-04-24 21:01, Paul Sandoz wrote:

Hi,

This looks good.


thanks!


Took me a while to understand the interactions: you need to replace not update 
otherwise there is a race on isResolved (which currently queries multiple 
state, there is no singular guard here). We could push isResolved into the 
synchronized block and simplify but every findSpecies call may take a small hit 
(or there are potentially other ways looking more closely at how 
ClassSpecializer.Factory initializes state, i.e. a fenced static field write, 
but we go further down the rabbit hole)


Right, I've been down that hole unsuccessfully and came up with this 
'play' (a much nicer word than 'hack'; thanks Peter!) as a means to 
escape it. :-)




I think this might fix some rare and intermittent recursive exceptions from 
ConcurrentHashMap cache we have been observing, where a collision occurs on 
keys for recursive updates (rather than for the same key).


I think so, too, but as I've not been able to reproduce any of these 
rare intermittent issues locally I've no real evidence to that effect.


/Claes


Re: RFR: 8202184: Reduce time blocking the ClassSpecializer cache creating SpeciesData

2018-04-25 Thread Peter Levart



On 04/25/2018 10:06 AM, Claes Redestad wrote:
Besides, CHM.computeIfAbsent has a non-synchronizing fast-path for 
when the key exists,

lines 1731-1734:

    else if (fh == h    // check first node without acquiring 
lock
 && ((fk = f.key) == key || (fk != null && 
key.equals(fk)))

 && (fv = f.val) != null)
    return fv; 


Sorry, you're (almost) right! I confused it with CHM.compute()...

The almost part is that lock is avoided only when the match is found in 
the 1st linked node of the bucket. If there is a hash collision (very 
unlikely) and the entry is in a 2nd or subsequent node in the list, the 
lock is still used. So there's almost no locks used... And if there's no 
hot contention going on, there's no need for prefacing with .get().


Regards, Peter



Re: RFR: 8202184: Reduce time blocking the ClassSpecializer cache creating SpeciesData

2018-04-25 Thread Peter Levart

Hi Claes,

Your patch is OK from logical point of view, but something bothers me a 
little. For each species data that gets cached, at least 3 objects are 
created:


- the Function passed to computeIfAbsent
- the unresolved SpeciesData object that serves as a placeholder
- the 2nd resolved SpeciesData object that replaces the 1st

The 1st SpeciesData object serves just as placeholder, so it could be 
lighter-weight. An java.lang.Object perhaps? Creation of Function object 
could be eliminated by using putIfAbsent instead of computeIfAbsent 
then. For the price of some unsafe casts that are contained to a single 
method that is the sole user of CHM cache.


For example, like this:

http://cr.openjdk.java.net/~plevart/jdk-dev/8202184_ClassSpecializer/webrev.01/

I won't oppose to your version if you find it easier to understand. I 
just hat to try to do it without redundant creation of placeholder 
SpeciesData object.


What do you think?

Regards, Peter

On 04/24/2018 06:57 PM, Claes Redestad wrote:

Hi,

the current implementation of ClassSpecializer.findSpecies may cause
excessive blocking due to a potentially expensive computeIfAbsent, and
we have reason to believe this might have been cause for a few very
rare bootstrap issues in tests that attach debuggers to VM in the middle
of this.

Breaking this apart so that code generation and linking is done outside
of the computeIfAbsent helps minimize the risk that a thread gets
interrupted by a debugger at a critical point in time, while also 
removing
a few limitations on what we can do from a findSpecies, e.g., look up 
other

SpeciesData.

Bug: https://bugs.openjdk.java.net/browse/JDK-8202184
Webrev: http://cr.openjdk.java.net/~redestad/8202184/open.00/

This implementation inserts a placeholder, unresolved SpeciesData,
and then replaces it with another instance that is linked together
fully before publishing it, which ensure safe publication. There might
be alternatives involving volatiles and/or careful fencing, but I've not
been able to measure any added startup cost from this anyway.

Thanks!

/Claes




Re: RFR: 8202184: Reduce time blocking the ClassSpecializer cache creating SpeciesData

2018-04-25 Thread Claes Redestad

Hi Peter,

too late! :-)

Good observation about us creating a new Function every time we look up 
an item. If we go with an Object as a reservation marker we could get 
away with a singleton static Function and still use computeIfAbsent 
(which I think is clearer and avoids creating a reservation Object on 
every lookup). I'd consider this a reasonable follow-up RFE:


http://cr.openjdk.java.net/~redestad/scratch/ClassSpec_followup.00/

Its value as an optimization might be somewhat dubious, but it does help 
future-proof the mechanism (say if we wanted to add forms that are even 
more heavyweight or if we needed to side-effect something when calling 
newSpeciesData...).


/Claes


On 2018-04-25 18:45, Peter Levart wrote:

Hi Claes,

Your patch is OK from logical point of view, but something bothers me 
a little. For each species data that gets cached, at least 3 objects 
are created:


- the Function passed to computeIfAbsent
- the unresolved SpeciesData object that serves as a placeholder
- the 2nd resolved SpeciesData object that replaces the 1st

The 1st SpeciesData object serves just as placeholder, so it could be 
lighter-weight. An java.lang.Object perhaps? Creation of Function 
object could be eliminated by using putIfAbsent instead of 
computeIfAbsent then. For the price of some unsafe casts that are 
contained to a single method that is the sole user of CHM cache.


For example, like this:

http://cr.openjdk.java.net/~plevart/jdk-dev/8202184_ClassSpecializer/webrev.01/ 



I won't oppose to your version if you find it easier to understand. I 
just hat to try to do it without redundant creation of placeholder 
SpeciesData object.


What do you think?

Regards, Peter

On 04/24/2018 06:57 PM, Claes Redestad wrote:

Hi,

the current implementation of ClassSpecializer.findSpecies may cause
excessive blocking due to a potentially expensive computeIfAbsent, and
we have reason to believe this might have been cause for a few very
rare bootstrap issues in tests that attach debuggers to VM in the middle
of this.

Breaking this apart so that code generation and linking is done outside
of the computeIfAbsent helps minimize the risk that a thread gets
interrupted by a debugger at a critical point in time, while also 
removing
a few limitations on what we can do from a findSpecies, e.g., look up 
other

SpeciesData.

Bug: https://bugs.openjdk.java.net/browse/JDK-8202184
Webrev: http://cr.openjdk.java.net/~redestad/8202184/open.00/

This implementation inserts a placeholder, unresolved SpeciesData,
and then replaces it with another instance that is linked together
fully before publishing it, which ensure safe publication. There might
be alternatives involving volatiles and/or careful fencing, but I've not
been able to measure any added startup cost from this anyway.

Thanks!

/Claes