FAQ: why no user-defined default instances?
John Rose
john.r.rose at oracle.com
Thu Apr 20 01:47:40 UTC 2023
That’s a good point about “hidden indirections”, but it is not
true that we always eagerly “stamp out” the canned pointer to the
default value. In at least one place (`getstatic`), at the same time as
we dynamically indirect the hidden indirection, we *also check for null*
and substitute the default instance. That would work for arrays as
well. In any case, the JVM has the *option* of stamping out zeroes in
all cases. For hidden indirections, the *option* is to either eagerly
stamp out a pointer to the hidden default *or* stamp out zeroes and map
on the fly to the default instance.
On 19 Apr 2023, at 16:57, Dan Smith wrote:
>> On Apr 19, 2023, at 3:51 PM, John Rose <john.r.rose at oracle.com>
>> wrote:
>>
>> “Stamping out” zeroes is fundamentally simpler to do than
>> “stamping out” some other pattern. Especially managed references:
>> If you “stamp out” GC-managed references, you have to have a
>> store-barrier conversation with the GC at each reference, if the GC
>> is designed that way. This factor all by itself will slow array
>> creation down, no matter what other design decisions are taken.
>
>
>> In any case, array creation performance is likely to take some kind
>> of performance hit. The issue is that a every array type will have to
>> store the bit pattern to “stamp out” when creating an array that
>> has an element class with a non-zero default. One might think that
>> this is a pay-as-you go feature, incurring cost only to classes which
>> default non-zero defaults, but that it not completely true. There are
>> places in the JVM and JDK where arrays (and other objects) are
>> created from dynamically selected types. Such places are likely to
>> need a new slow-path branch to handle the possibility that the
>> selected type requires special special handling for a non-zero
>> default. Tests and branches are cheap but it cannot be assumed that
>> they are free.
>
>> As noted above, managed references are a particularly difficult cost
>> to control, whe considering the initialization of non-zero defaults,
>> particularly for arrays. It is somewhat dispiriting to contemplate a
>> flurry of GC activity to create the default state of an array,
>> immediately followed by a copy of non-default values, initiating a
>> second flurry on the same locations. One might avoid this particular
>> problem by allowing only primitive values to be “stamped out”,
>> but that reduces the utility of a user-defined default, and forces
>> users into workarounds anyway.
>
> This discussion about arrays should acknowledge that we *do*, in fact,
> "stamp out" non-zero pointers to default instances when the class has
> been deemed unsuitable for flattening. At an implementation level,
> there are 3 kinds of value classes and !-typed value class arrays:
>
> 1) The happy case that we'll flatten, in which case the array stores a
> bunch of zeros
>
> 2) Value classes that are too big or atomic (or maybe fail some other
> criteria?) and so won't be flattened, in which the array stores a
> repeated non-zero pointer (an alternative strategy would check for
> null on read and swap in the pointer; that's what happens with fields)
>
> 3) Value classes that don't have a default instance, in which case the
> array stores a bunch of nulls and the '!' is erased (an alternative VM
> design would check for null on read and throw)
>
> We can argue that case (1) is expecting the best performance, so in
> that case we don't want to do anything to slow down its array
> allocations, but I think it's fair to say that examples of case (1)
> wouldn't mind the trade-off of moving into case (2) and getting a
> custom default value instead—especially when their alternative
> workaround is to move into case (3) instead.
>
> (I'm not advocating for this outcome, just critiquing the argument.)
>
>> • Also, the all-zero value will be visible in some states, even
>> though we are trying to make it disappear. At least while initial
>> field initializers are executed, and probably at other times as well.
>> That will lead to confusion.
>
> Because of case (2), this should never happen (although we've had to
> do extra work in order to properly hide the null pointer in case (2)).
>
>> • The simplest workaround is not to expose the default value.
>> Allow null to be the default value of your class, just as with all
>> pre-Valhalla classes. The standard workarounds for the null value
>> apply in all cases. Valhalla can succeed even if it doesn’t fully
>> solve all problems with null.
>
> I'd add some acknowledgement of the role '!' has here, and refine the
> analogy—instead of "allow null to be the default value of your
> class", you mean "don't give the class a default instance". Then your
> '!' types operate in case (3) instead of case (1) or (2), and the
> arrays store null. (Your nullable type, meanwhile, continues to have
> '!' as its default value.)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-spec-observers/attachments/20230419/9555b251/attachment-0001.htm>
More information about the valhalla-spec-observers
mailing list