On constant folding of final field loads
aleksey.shipilev at oracle.com
Mon Jun 29 10:35:20 UTC 2015
On 06/27/2015 04:27 AM, Vladimir Ivanov wrote:
> Current prototype:
> The idea is simple: JIT tracks final field changes and throws away
> nmethods which are affected.
Big picture question: do we actually care about propagating final field
values once the object escaped (and in this sense, available to be
introspected by the compiler)?
Java memory model does not guarantee the final field visibility when the
object had escaped. The very reason why deserialization works is because
the deserialized object had not yet been published.
That is, are we in line with the spec and general expectations by
folding the final values, *and* not deoptimizing on the store?
> Though Unsafe.objectFieldOffset/staticFieldOffset javadoc explicitly
> states that returned value is not guaranteed to be a byte offset ,
> after following that road I don't see how offset encoding scheme can be
Yes. Lots and lots of users rely on *fieldOffset to return the actual
byte offset, even though it is not specified as such. This understanding
is so prevalent, that it leaks into Unsafe.get*Unaligned, etc.
> More realistically, since there are external dependencies on Unsafe API,
> I'd prefer to leave sun.misc.Unsafe as is and switch to VarHandles (when
> they are available in 9) all over JDK. Or temporarily make a private
> copy (finally :-)) of field accessors from Unsafe, switch it to encoded
> offsets, and use it in Reflection & java.lang.invoke API.
Or, introduce Unsafe.invalidateFinalDep(Field/offset/etc), and add the
call to it to Reflection accessors, MethodHandles invoke, VarHandle
handles, etc. When/if Unsafe goes away, so do the unsafe
(non-dependency-firing) final field stores. Raw memory access via Unsafe
already escapes whatever traps you are setting in (oop + offset) path,
so it would be nice to have the option to fire the dependency check for
an arbitrary (?) offset.
> Regarding alternative approaches to track the finality, an offset bitmap
> on per-class basis can be used (containing locations of final fields).
> Possible downsides are: (1) memory footprint (1/8th of instance size per
> class); and (2) more complex checking logic (load a relevant piece of a
> bitmap from a klass, instead of checking locally available offset
> cookie). The advantage is that it is completely transparent to a user:
> it doesn't change offset translation scheme.
I like this one. Paying with slightly larger memory footprint for API
compatibility sounds reasonable to me.
> II. Managing relations between final fields and nmethods
> Another aspect is how expensive dependency checking becomes.
> I took a benchmark from Nashorn/Octane (Box2D), since MethodHandle
> inlining heavily relies on constant folding of instance final fields.
> Before After
> checks (#) 420 12,5K
> nmethods checked(#) 3K 1,5M
> total time: 60ms 2s
> deps total 19K 26K
> Though total number of dependencies in VM didn't change much (+37% =
> 19K->26K), total number of checked dependencies (500x: 3K -> 1,5M) and
> time spent on dependency checking (30x: 60ms -> 2s) dramatically increased.
> The reason is that constant field value dependencies created heavily
> populated contextes which are regularly checked:
> #1 #2 #3/#4
> KlassDep 254 47/2,632
> CallSiteDep 167 46/ 358
> ConstantFieldDep 11,790 0/1,494,112
> KlassDep 286 41/ 2,769
> CallSiteDep 249 58/ 393
> (#1 - dependency kind; #2 - total number of unique dependencies;
> #3/#4 - invalidated nmethods/checked dependencies)
Isn't the underlying problem being the dependencies are searched
linearly? At least in ConstantFieldDep, can we compartmentalize the
dependencies by holder class in some sort of hash table?
-------------- next part --------------
A non-text attachment was scrubbed...
Size: 819 bytes
Desc: OpenPGP digital signature
More information about the hotspot-compiler-dev