FAQ: why no user-defined default instances?

Dan Smith daniel.smith at oracle.com
Wed Apr 19 23:57:54 UTC 2023



> 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.)



More information about the valhalla-spec-experts mailing list