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