From forax at univ-mlv.fr Tue Sep 6 08:32:13 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 6 Sep 2022 10:32:13 +0200 (CEST) Subject: What we have lost ? Message-ID: <740286845.4338662.1662453133776.JavaMail.zimbra@u-pem.fr> Hi everybody, it seems to me that the current design has reached a kind of local maximum, which is nice and not nice at the same time, so i would like to take the time to reflect on what we have sacrificed when moving to the current design. What is missing/not supported by the current model is value classes that should not be used by reference, either because it will cause performance issues or because the user will not get the semantics he think he will get. Here is a list of such value types: - unit types, value types like by example Nothing (which mean that a method never returns) with no fields. Because creating a ref on it creates something :) - wrappers/monads that modify the semantics, by example a generic value class Atomic that plays the same role as an AtomicReference, AtomicInteger, etc the problem here is that the default semantics is not the semantics the user want. - SIMD vectors, if those are nullable, the VM/JIT will insert implicit null checks which are not usually a problem apart in thigh loop like users write with SIMD vectors. - existing value classes in Scala or Kotlin, those are not nullable by default but in the current design, getClass() will happily reflect them with a nullable class making Scala/Kotlin second class citizens of the Java platform. Those are my 4 tent poles, they are maybe others, but currently we fail to provide a good answer for those cases. regards, R?mi From daniel.smith at oracle.com Tue Sep 6 22:08:57 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Tue, 6 Sep 2022 22:08:57 +0000 Subject: What we have lost ? In-Reply-To: <740286845.4338662.1662453133776.JavaMail.zimbra@u-pem.fr> References: <740286845.4338662.1662453133776.JavaMail.zimbra@u-pem.fr> Message-ID: <08E4021C-C5A1-4E33-8E9B-F54DEE260A64@oracle.com> > On Sep 6, 2022, at 1:32 AM, Remi Forax wrote: > > What is missing/not supported by the current model is value classes that should not be used by reference, > either because it will cause performance issues or because the user will not get the semantics he think he will get. This is a useful question to explore, so thanks for bringing it up: can we think of use cases in which a class should, in the informed opinion of its author, always be referred to via its value type? If so, a reference-by-default approach is definitely problematic, because one of our starting assumptions was that most uses of value classes would be totally fine using a reference type. > Here is a list of such value types: > - unit types, value types like by example Nothing (which mean that a method never returns) with no fields. > Because creating a ref on it creates something :) If you truly mean for such a class to have no instances, that's not something a class with a value type can assert?the default instance always exists. I can see how it would be nice, for example, to have a type like Void that is also non-nullable, but value types are not the feature to accomplish that. > - wrappers/monads that modify the semantics, by example a generic value class Atomic that plays the same role as an AtomicReference, AtomicInteger, etc > the problem here is that the default semantics is not the semantics the user want. Okay, so say we have value class Atomic, and we're in a future where this gets specialized. I think you're saying it will be important to say 'Atomic.val' at all uses rather than 'Atomic'. But I'm not clear on the argument for why that would be. Can you elaborate? > - SIMD vectors, if those are nullable, the VM/JIT will insert implicit null checks which are not usually a problem apart in thigh loop like users write with SIMD vectors. The *storage* should definitely use a value type, so in this sort of application we'd encourage value-typed array allocations (and value-typed type arguments for wrapping data structures). In a loop over a flat array, I would expect it to be okay to talk about the reference type in source, and have the JIT generate optimal code to work with the underlying flat storage, without any allocations or null checks. My sense is that we suspect that this can work reliably, but it could use more targeted performance testing to confirm. > - existing value classes in Scala or Kotlin, those are not nullable by default but in the current design, getClass() will happily reflect them with a nullable class making Scala/Kotlin second class citizens of the Java platform. Is the problem here that Scala/Kotlin will want reference-default interpretation of names in Java source? (If so, , if you want a good user experience with Kotlin types, write Kotlin source.) Or is the problem that they will want the reflection API to behave differently, making Foo.val.class the "primary" class object, not a secondary one? (If so, again, the Java reflection API is Java-oriented, interop is not a major factor in its design.) Maybe I'm missing your point on this one? From daniel.smith at oracle.com Wed Sep 7 00:19:42 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Wed, 7 Sep 2022 00:19:42 +0000 Subject: EG meeting, 2022-09-07 Message-ID: EG Zoom meeting September 7 at 4pm UTC (9am PDT, 12pm EDT). Recent topics that may merit further discussion: "What have we lost?": R?mi raised concerns about some use cases in which a value-by-default approach to the class name would be better than a reference-by-default approach "JLS updates": I posted a (minor) update to the JLS & JVMS change documents And here's this from a few weeks ago, if anybody would like to spend more time on any of these older threads: > Lots of recent threads that could be further discussed: > > - "Question about universal type variables": Kevin started a discussion about how type variable types should be modeled, and what changes when they become universal > > - "Updated SoV, take 3": Brian revised the State of Valhalla document to reflect recent design ideas > > - "object sameness, Lebniz's Law, ...": John elaborated on SoV review comments regarding value object equality/substitutability > > - "The storage hint model": Remi shared thoughts about using a storage attribute, rather than a value type, to encode flatness > > - "The problem with encapsulating C.val + autoboxing": Remi discussed the treatment of access-restricted value types in generics > > - "where are all the objects?": John and Kevin discussed usages of the terms "object" and "instance" > > - "one class, two types, many bikesheds": John discussed how we model classes vs. types, the relationship of ref and val types, and how syntax like .ref and .val might be used > > - "Value type companions, encapsulated": John shared a document describing how access restrictions could be enforced on value types > > - "races on flat values": John discussed how the memory model needs to be updated to describe concurrent accesses of flat variables From forax at univ-mlv.fr Wed Sep 7 07:56:55 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Wed, 7 Sep 2022 09:56:55 +0200 (CEST) Subject: What we have lost ? In-Reply-To: <08E4021C-C5A1-4E33-8E9B-F54DEE260A64@oracle.com> References: <740286845.4338662.1662453133776.JavaMail.zimbra@u-pem.fr> <08E4021C-C5A1-4E33-8E9B-F54DEE260A64@oracle.com> Message-ID: <1053890450.176406.1662537415380.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "daniel smith" > To: "Remi Forax" > Cc: "valhalla-spec-experts" > Sent: Wednesday, September 7, 2022 12:08:57 AM > Subject: Re: What we have lost ? >> On Sep 6, 2022, at 1:32 AM, Remi Forax wrote: >> >> What is missing/not supported by the current model is value classes that should >> not be used by reference, >> either because it will cause performance issues or because the user will not get >> the semantics he think he will get. > > This is a useful question to explore, so thanks for bringing it up: can we think > of use cases in which a class should, in the informed opinion of its author, > always be referred to via its value type? If so, a reference-by-default > approach is definitely problematic, because one of our starting assumptions was > that most uses of value classes would be totally fine using a reference type. > >> Here is a list of such value types: >> - unit types, value types like by example Nothing (which mean that a method >> never returns) with no fields. >> Because creating a ref on it creates something :) > > If you truly mean for such a class to have no instances, that's not something a > class with a value type can assert?the default instance always exists. I can > see how it would be nice, for example, to have a type like Void that is also > non-nullable, but value types are not the feature to accomplish that. no, such classes have instances but they are empty. By example, an array of Nothing stores just its length in memory. Accessing a cell return the default value, Nothing.default which is equivalent to new Nothing(). And it can be used in generics too, a HashMap is more or less a set of ints. > >> - wrappers/monads that modify the semantics, by example a generic value class >> Atomic that plays the same role as an AtomicReference, AtomicInteger, etc >> the problem here is that the default semantics is not the semantics the user >> want. > > Okay, so say we have value class Atomic, and we're in a future where this > gets specialized. I think you're saying it will be important to say > 'Atomic.val' at all uses rather than 'Atomic'. But I'm not clear on > the argument for why that would be. Can you elaborate? If Atomic is declared as a ref-by-default value class, Atomic behave as a ref an not as a Foo + some atomic magic. So it is always wrong to use it. As the author of Atomic, i don't want my users to have a way to shoot themselves into the foot. Here, Atomic should be flat-by-default and also the ref variant should not exist, so there is no way to misuse the Atomic API. > >> - SIMD vectors, if those are nullable, the VM/JIT will insert implicit null >> checks which are not usually a problem apart in thigh loop like users write >> with SIMD vectors. > > The *storage* should definitely use a value type, so in this sort of application > we'd encourage value-typed array allocations (and value-typed type arguments > for wrapping data structures). > > In a loop over a flat array, I would expect it to be okay to talk about the > reference type in source, and have the JIT generate optimal code to work with > the underlying flat storage, without any allocations or null checks. My sense > is that we suspect that this can work reliably, but it could use more targeted > performance testing to confirm. Sorry for not being clear, i'm more worry about people using IntVector instead of IntVector.val on stack and the VM having to emit a nullcheck inside a loop, by example if there is a call to a method that returns an IntVector is not inlined. For me, the vector API represents operation so a ref-by-default IntVector is a kind of useless, worst it will work but performance will suffer with no simple way to fix that apart taking a look to the generated assembly code. > >> - existing value classes in Scala or Kotlin, those are not nullable by default >> but in the current design, getClass() will happily reflect them with a nullable >> class making Scala/Kotlin second class citizens of the Java platform. > > Is the problem here that Scala/Kotlin will want reference-default interpretation > of names in Java source? (If so, , if you want a good user experience > with Kotlin types, write Kotlin source.) > I don't know the answer to that question but i disagree with your conclusion, interrop with Java, so with the Java ecosystem is important. > Or is the problem that they will want the reflection API to behave differently, > making Foo.val.class the "primary" class object, not a secondary one? (If so, > again, the Java reflection API is Java-oriented, interop is not a major > factor in its design.) > > Maybe I'm missing your point on this one? The problem is that we are forcing the companion class design to other languages than Java. C# has done the same mistake with their generics being too specific to C# so other languages on the .Net platform all suffer. Other languages than Java can not makes Foo.val.class their primary class object because the companion class design is bolt into the VM. R?mi From heidinga at redhat.com Wed Sep 7 12:59:09 2022 From: heidinga at redhat.com (Dan Heidinga) Date: Wed, 7 Sep 2022 08:59:09 -0400 Subject: JVMS updates as well In-Reply-To: <6C3EAD97-8B4E-47CC-ACD1-25B52987C94A@oracle.com> References: <0342FD93-3506-41D3-A7D4-D7C53727D316@oracle.com> <6C3EAD97-8B4E-47CC-ACD1-25B52987C94A@oracle.com> Message-ID: Some additional comments based on my read through. 4.4.8 The CONSTANT_MethodHandle_info Structure > Design discussion: the withfield and aconst_init instructions may justify new forms of CONSTANT_MethodHandle, but for now no changes are made to reference_kind. Both instructions need to be incorporated in a method and carry nest-restrictions. I don't think we want to reify these instructions as new MH types. Better to rely on the programmers intent and have programmers opt to expose this functionality through methods. 4.4.10 The CONSTANT_Dynamic_info and CONSTANT_InvokeDynamic_info Structures > We could allow here, but the precedent seems to be to avoid any special method names when using invokedynamic. Agreed on not allowing special names in invokedynamic. 4.6 Methods > A method of an abstract class, an identity class, or an interface must not have the name . and > If the name of the method is , then the descriptor must denote a return type that is the type of the current class or interface. We had previously discussed this restriction and I thought we mostly agreed on dropping it as each previous discussion didn't result in a clear answer on the value of the restriction. The intention to allow reflection to use the return type to treat methods as j.l.r.Constructor objects would violate the invariant that a constructor returns an instance of the declaring class. Here we don't have to do that (and can also return null) so it would weaken the guarantees provided by Constructor and lead to programmer errors as they aren't used to validating Constructors previous invariants (which vnew can violate). I'm also unsure how to read that last clause "... type of the current class or interface." I'm interpreting it as "current class or *an* interface" as isn't allowed in interfaces so it can't be "current interface". Is the intention that the VM loads any class named as the return type in a method descriptor to enforce it is an interface? Preload -- need definition of when loading occurs. This should be added to section 5.3.5 to outline when the Preloaded classes are made available. Because this is observable behaviour, we should mandate when it occurs. Or at least it will occur before stage X. 6.5 Instructions > aconst_init > The referenced value class is initialized if it has not already been initialized (5.5). I think we have some missing cases around value class initialization that I think we've talked about previously but I don't see here. Basically, any Array creation would need to ensure the class is initalized as well as would static fields of value classes. Since we're talking about having two classes - the reference class and the value companion - we should be clear in the spec on how class loading & initalization for this works. Given the companion class is derived from the ValueClass attribute in the reference's classfile, the spec should indicate that the reference projection is loaded first, then the value companion. And that the value companion, not being a true class, doesn't get its own ? Another option here is to hide this distinction in the translation layer so you can't ask "Class.forName("Point.val")" and need to instead do a "Class.forName("Point").valueClass()". I think we've under-specified this interaction so far and need to add more text to cover how the two j.l.Class objects relate and how that hooks into VM-lifecycle events like class loading and initialization. Also the "ValueClass" attribute proposed in John's "Value type companions, encapsulated" email from July 3, 2022 seems to be missing from this draft. --Dan On Wed, Aug 31, 2022 at 11:49 AM John Rose wrote: > http://cr.openjdk.java.net/~dlsmith/jep8277163/latest > > I edited the URL to find the corresponding JVMS updates: > > > http://cr.openjdk.java.net/~dlsmith/jep8277163/jep8277163-20220830/specs/value-objects-jvms.html > > I have a few unsystematic comments on the special subject of special > methods. > > In that draft JVMS, we say in 2.9.1 > > An instance initialization method may not be declared in a value class or > an interface (4.6). > > and in 2.9.4 > > A value class instance creation method may not be declared in an identity > class, an abstract class, or an interface (4.6). > > Then in 4.6 we reaffirm these restrictions on and , along > with many similar pre-existing restrictions. These are restrictions on > *declaration* of special methods. > > Then in 4.9.1 we have similar static constraints on *uses* of special > methods, by bytecodes. Such constraints also appear on constants in the > constant pool, in 4.4.8. > > Eventually in 5.3.5 we discover when and what we must throw if any of > these conditions fail: > > If the purported representation is not a ClassFile structure (?4.1, ?4.8), > loading throws an instance of ClassFormatError. > > IIRC somewhere there is a place in chapter 4 that tells us that all of the > many requirements will be enforced. I don?t remember where that promise is > made. (Remind me?) > > The phrases ?instance initialization method ? and ?value class instance > creation method? are accompanied by cross references to 2.9.1 and 2.9.4. > I?m starting to think that the cross references should *also* mention the > special method names and . That would be a new thing for the > JVMS but makes more are more sense as we add more and more special method > names. Now we are at three such names; maybe it?s time to mention the names > along with the cross references. I think I should be able to grep the JVMS > for for mentions of this feature, not just the phrase ?value class > instance creation method? or the section number 2.9.4. For example: > > Only the invokestatic instruction is allowed to invoke a value class > instance creation method (2.9.4 ``). > > At some time we will want to change JVMS language that says things like > ?not , , or ? to ?not a special method name?. Is that > time now yet? Places that allow some but not all would have to say ?not a > special method name other than ? or something of that nature. I > think that would expose some irregularities in our treatment of > ?surprising? names, but that?s all to the good, even if embarrassing. > > You have this commentary: > > As an alternative naming scheme, we could re-use but give the > method a non-void return type. > > You could also say something like: > > An alternative implementation technique could use regular static methods > (presumably marked with a Synthetic attribute) of a standard name to > implement constructors of value classes. This would be convenient for some > tools in that they could treat these factory methods like all other factory > methods, without learning the new rules for symbols, but would be > inconvenient for other tools, including the JVM, which need to make a clear > distinction between constructors and other methods. > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From heidinga at redhat.com Wed Sep 7 14:15:59 2022 From: heidinga at redhat.com (Dan Heidinga) Date: Wed, 7 Sep 2022 10:15:59 -0400 Subject: JLS updates In-Reply-To: References: <0342FD93-3506-41D3-A7D4-D7C53727D316@oracle.com> Message-ID: Comments based on my latest read through: 8.1.1.5 identity and value Classes > The class is neither abstract not the class Object Typo? I think that should be "nor" rather than "not" I don't recall seeing these restrictions - non-static fields, synchronized methods, etc - in the JVMS update. Maybe I missed them? Otherwise, I think we need to add them to Ch.4 in the static constraints section as checks against value classes as well. 9.8 Functional Interfaces I'm not sure I follow the restriction on functional interfaces not being "sealed, identity, or value". Sealed makes sense as it is about limiting who can implement the interface. Is this an implementation concern? 15.8.3 this > Use of this in a constructor of a value class is restricted. It is a compile-time error if a this expression occurs in the constructor of a value class unless one of the following is true: > The this expression appears at a point where all blank final instance fields of the class have been definitely assigned (16) I'm unclear on the rationale for this restriction. If I wanted to expose a value constructor that returned the default (all-zeros) instance, it would need to explicitly zero each field? Seems like we're trying to solve the sins of the past here (like we often do when making things null-hostile) when it doesn't apply in the same way. We're not leaking an uninitialized value - we always start from a completely (zero) initialized value. --Dan On Wed, Aug 31, 2022 at 6:15 PM Dan Smith wrote: > On Aug 31, 2022, at 11:54 AM, Dan Smith wrote: > > On Aug 30, 2022, at 12:42 PM, Dan Smith wrote: > > On Aug 30, 2022, at 12:18 PM, Dan Smith wrote: > > FYI, I've made some minor fixes to the JLS change document supporting > Value Objects, published here: > > > http://cr.openjdk.java.net/~dlsmith/jep8277163/latest > > > One change I'll need to make: an interface is not a functional interface > if it is `identity` or `value`; but this should also be the case if any > *superinterface* is `identity` or `value`. (Ultimately, the implementation > should be free to generate identity objects or value objects to encode > lambdas and method references.) > > > And another (just want to memorialize these so I can fix them later): rule > in 8.1.1 about 'final' conflicting with 'sealed' also applies to the > implicit 'final' of a concrete value class. (More generally, anywhere > 'final' is mentioned as a class modifier, need to be sure it's clear that > this includes the implicit 'final'.) > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Sep 7 18:38:46 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 7 Sep 2022 14:38:46 -0400 Subject: What we have lost ? In-Reply-To: <740286845.4338662.1662453133776.JavaMail.zimbra@u-pem.fr> References: <740286845.4338662.1662453133776.JavaMail.zimbra@u-pem.fr> Message-ID: <8954f7d5-bfe4-d189-6cc9-e659bb353e21@oracle.com> I would summarize this mail as "no new observations, but growing discomfort with ref-as-default."? All of these points amount to "there are classes for which most uses will prefer val to varying degrees, but users may forget to say .val / avoid saying .val because it is clunky", which may turn into performance potholes.? These are surely known, so I will take this as Remi worrying that this will be a bigger and more persistent irritant than we think. (I think the idea of "class for which there is no ref type" is a non-starter; for all of the types you talk about (vectors, etc), you could make the same argument for `int`, but no one is saying we don't need `Integer`.? I think what you are really getting at here is coming back to some form of "I want val-default".) A control question I would ask (though let's keep the bikeshedding to a dull roar) is how much of this is about the undeniable clumsiness of the locution "Point.val".? If, for example, the val type were called "point" or "Point!", as some people have already publicly wished, does this change your concern that "users will get it wrong all the time"? On 9/6/2022 4:32 AM, Remi Forax wrote: > Hi everybody, > it seems to me that the current design has reached a kind of local maximum, which is nice and not nice at the same time, > so i would like to take the time to reflect on what we have sacrificed when moving to the current design. > > What is missing/not supported by the current model is value classes that should not be used by reference, > either because it will cause performance issues or because the user will not get the semantics he think he will get. > > Here is a list of such value types: > - unit types, value types like by example Nothing (which mean that a method never returns) with no fields. > Because creating a ref on it creates something :) > > - wrappers/monads that modify the semantics, by example a generic value class Atomic that plays the same role as an AtomicReference, AtomicInteger, etc > the problem here is that the default semantics is not the semantics the user want. > > - SIMD vectors, if those are nullable, the VM/JIT will insert implicit null checks which are not usually a problem apart in thigh loop like users write with SIMD vectors. > > - existing value classes in Scala or Kotlin, those are not nullable by default but in the current design, getClass() will happily reflect them with a nullable class making Scala/Kotlin second class citizens of the Java platform. > > Those are my 4 tent poles, they are maybe others, but currently we fail to provide a good answer for those cases. > > regards, > R?mi > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Wed Sep 7 21:27:28 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Wed, 7 Sep 2022 23:27:28 +0200 (CEST) Subject: What we have lost ? In-Reply-To: <8954f7d5-bfe4-d189-6cc9-e659bb353e21@oracle.com> References: <740286845.4338662.1662453133776.JavaMail.zimbra@u-pem.fr> <8954f7d5-bfe4-d189-6cc9-e659bb353e21@oracle.com> Message-ID: <563107867.703081.1662586048888.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "Remi Forax" , "valhalla-spec-experts" > > Sent: Wednesday, September 7, 2022 8:38:46 PM > Subject: Re: What we have lost ? > I would summarize this mail as "no new observations, but growing discomfort with > ref-as-default." All of these points amount to "there are classes for which > most uses will prefer val to varying degrees, but users may forget to say .val > / avoid saying .val because it is clunky", which may turn into performance > potholes. These are surely known, so I will take this as Remi worrying that > this will be a bigger and more persistent irritant than we think. If we take a step back, one of the main reason of introducing value types is to have a way to be closer to the actual CPU architecture which can also be described as solving Java performance potholes. So i think we should shy away that term. Dan help us to categorize the different issues, - singleton types and vector representing SIMD registers are similar cases where using the ref projection is far worst than the val projection, to the point where author of those classes may prefer to not have a ref projection. In the case of an empty value class, storing the val projection use no memory, so the difference in term of the behavior is big, In the case of a SIMD register, if the VM loose track of the creation of something which is typed as the reference projection, a nullcheck will to be emitted, while usually we don't care about the cost of a nullcheck, having a supplementary nullcheck in a thigh loop is a far bigger issue to the point where having a reference projection for such types is counter productive. - Atomic (or any class that play the role of a modifier like Stable, etc) is when the ref projection have the wrong semantics so should not exist and there is no need for a lightweight boxing given that you can always extract the value from the monad (and use its reference projection if necessary). - the last case is a case where the way the value classes are defined in Scala/Kotlin can not be retrofitted to use the VM value classes because getClass() always returns the class of the ref projection. This is also a case where those value classes should not have a ref projection. > (I think the idea of "class for which there is no ref type" is a non-starter; > for all of the types you talk about (vectors, etc), you could make the same > argument for `int`, but no one is saying we don't need `Integer`. I think what > you are really getting at here is coming back to some form of "I want > val-default".) There are types which should never be null for different reasons, for those types having a reference projection is an issue. A nullable int is something useful hence we need Integer, sadly not all the value types are like this. > A control question I would ask (though let's keep the bikeshedding to a dull > roar) is how much of this is about the undeniable clumsiness of the locution > "Point.val". If, for example, the val type were called "point" or "Point!", as > some people have already publicly wished, does this change your concern that > "users will get it wrong all the time"? I really believe that not all (value) types should have a reference projection. R?mi > On 9/6/2022 4:32 AM, Remi Forax wrote: >> Hi everybody, >> it seems to me that the current design has reached a kind of local maximum, >> which is nice and not nice at the same time, >> so i would like to take the time to reflect on what we have sacrificed when >> moving to the current design. >> What is missing/not supported by the current model is value classes that should >> not be used by reference, >> either because it will cause performance issues or because the user will not get >> the semantics he think he will get. >> Here is a list of such value types: >> - unit types, value types like by example Nothing (which mean that a method >> never returns) with no fields. >> Because creating a ref on it creates something :) >> - wrappers/monads that modify the semantics, by example a generic value class >> Atomic that plays the same role as an AtomicReference, AtomicInteger, etc >> the problem here is that the default semantics is not the semantics the user >> want. >> - SIMD vectors, if those are nullable, the VM/JIT will insert implicit null >> checks which are not usually a problem apart in thigh loop like users write >> with SIMD vectors. >> - existing value classes in Scala or Kotlin, those are not nullable by default >> but in the current design, getClass() will happily reflect them with a nullable >> class making Scala/Kotlin second class citizens of the Java platform. >> Those are my 4 tent poles, they are maybe others, but currently we fail to >> provide a good answer for those cases. >> regards, >> R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Sep 7 21:56:03 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 7 Sep 2022 17:56:03 -0400 Subject: What we have lost ? In-Reply-To: <563107867.703081.1662586048888.JavaMail.zimbra@u-pem.fr> References: <740286845.4338662.1662453133776.JavaMail.zimbra@u-pem.fr> <8954f7d5-bfe4-d189-6cc9-e659bb353e21@oracle.com> <563107867.703081.1662586048888.JavaMail.zimbra@u-pem.fr> Message-ID: <311e8d28-efb9-7b2e-2a9f-71fa73692bdc@oracle.com> > > (I think the idea of "class for which there is no ref type" is a > non-starter; for all of the types you talk about (vectors, etc), > you could make the same argument for `int`, but no one is saying > we don't need `Integer`.? I think what you are really getting at > here is coming back to some form of "I want val-default".) > > > There are types which should never be null for different reasons, for > those types having a reference projection is an issue. > A nullable int is something useful hence we need Integer, sadly not > all the value types are like this. Yes, they are.? If you ever want to call a method reflectively on a value object, you have to pass it to Method::invoke, which takes an Object.? That's a reference type. -------------- next part -------------- An HTML attachment was scrubbed... URL: From paul.sandoz at oracle.com Wed Sep 7 23:10:26 2022 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Wed, 7 Sep 2022 23:10:26 +0000 Subject: What we have lost ? In-Reply-To: <563107867.703081.1662586048888.JavaMail.zimbra@u-pem.fr> References: <740286845.4338662.1662453133776.JavaMail.zimbra@u-pem.fr> <8954f7d5-bfe4-d189-6cc9-e659bb353e21@oracle.com> <563107867.703081.1662586048888.JavaMail.zimbra@u-pem.fr> Message-ID: <24D3934E-DFD1-4CC0-91C0-A09D6C18A81D@oracle.com> The Vector API is I think a proof point that ref projection can be acceptable and can work. The Vector API implementation currently behaves approximately as if a vector instance (whose concrete class is private) is a ref to a value object. The C2 compiler performs aggressive unboxing and mapping "payloads" (analogous to value objects) vectors hold to vector registers, rather than scalarizing the element ?overlay? or view over the payload described by the vector?s shape and element type (the species). In this current model we generally don?t recommend storing vector instances in the heap (there are exceptions to that rule for constants), and instead vectors are loaded from or stored to memory containers such as arrays, byte buffers, or memory segments. After a few iterations of the API and implementation its starting to work rather well at generating efficient vectorized code on supporting hardware. We are currently early in the process of exploring alignment of the Vector API with Valhalla where: 1. the private concrete vector classes are value classes whose .val type is private; and 2. a vector value class has a "payload" field whose type is a value type. The "payload" is a non-atomic "bag of bits" e.g. values types of Bits256.val, Bits128.val etc, which would not be exposed in the public Vector API. So no utterance of .val is expected in this model. The API remains the same as it does today. We could decide the value classes of those ?bag of bits? could be public classes, and developers may interact with those using the .val. Unsure yet. As we explore and learn more the approach could change. I hope the exploration will be informative. Paul. > On Sep 7, 2022, at 2:27 PM, forax at univ-mlv.fr wrote: > > > > From: "Brian Goetz" > To: "Remi Forax" , "valhalla-spec-experts" > Sent: Wednesday, September 7, 2022 8:38:46 PM > Subject: Re: What we have lost ? > I would summarize this mail as "no new observations, but growing discomfort with ref-as-default." All of these points amount to "there are classes for which most uses will prefer val to varying degrees, but users may forget to say .val / avoid saying .val because it is clunky", which may turn into performance potholes. These are surely known, so I will take this as Remi worrying that this will be a bigger and more persistent irritant than we think. > > If we take a step back, one of the main reason of introducing value types is to have a way to be closer to the actual CPU architecture which can also be described as solving Java performance potholes. > So i think we should shy away that term. > > Dan help us to categorize the different issues, > - singleton types and vector representing SIMD registers are similar cases where using the ref projection is far worst than the val projection, to the point where author of those classes may prefer to not have a ref projection. > In the case of an empty value class, storing the val projection use no memory, so the difference in term of the behavior is big, > In the case of a SIMD register, if the VM loose track of the creation of something which is typed as the reference projection, a nullcheck will to be emitted, while usually we don't care about the cost of a nullcheck, having a supplementary nullcheck in a thigh loop is a far bigger issue to the point where having a reference projection for such types is counter productive. > > - Atomic (or any class that play the role of a modifier like Stable, etc) is when the ref projection have the wrong semantics so should not exist and there is no need for a lightweight boxing given that you can always extract the value from the monad (and use its reference projection if necessary). > > - the last case is a case where the way the value classes are defined in Scala/Kotlin can not be retrofitted to use the VM value classes because getClass() always returns the class of the ref projection. This is also a case where those value classes should not have a ref projection. > > > (I think the idea of "class for which there is no ref type" is a non-starter; for all of the types you talk about (vectors, etc), you could make the same argument for `int`, but no one is saying we don't need `Integer`. I think what you are really getting at here is coming back to some form of "I want val-default".) > > There are types which should never be null for different reasons, for those types having a reference projection is an issue. > A nullable int is something useful hence we need Integer, sadly not all the value types are like this. > > > A control question I would ask (though let's keep the bikeshedding to a dull roar) is how much of this is about the undeniable clumsiness of the locution "Point.val". If, for example, the val type were called "point" or "Point!", as some people have already publicly wished, does this change your concern that "users will get it wrong all the time"? > > I really believe that not all (value) types should have a reference projection. > > R?mi > > > > > On 9/6/2022 4:32 AM, Remi Forax wrote: > Hi everybody, > it seems to me that the current design has reached a kind of local maximum, which is nice and not nice at the same time, > so i would like to take the time to reflect on what we have sacrificed when moving to the current design. > > What is missing/not supported by the current model is value classes that should not be used by reference, > either because it will cause performance issues or because the user will not get the semantics he think he will get. > > Here is a list of such value types: > - unit types, value types like by example Nothing (which mean that a method never returns) with no fields. > Because creating a ref on it creates something :) > > - wrappers/monads that modify the semantics, by example a generic value class Atomic that plays the same role as an AtomicReference, AtomicInteger, etc > the problem here is that the default semantics is not the semantics the user want. > > - SIMD vectors, if those are nullable, the VM/JIT will insert implicit null checks which are not usually a problem apart in thigh loop like users write with SIMD vectors. > > - existing value classes in Scala or Kotlin, those are not nullable by default but in the current design, getClass() will happily reflect them with a nullable class making Scala/Kotlin second class citizens of the Java platform. > > Those are my 4 tent poles, they are maybe others, but currently we fail to provide a good answer for those cases. > > regards, > R?mi > > > > > > From daniel.smith at oracle.com Thu Sep 8 02:19:34 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 8 Sep 2022 02:19:34 +0000 Subject: What we have lost ? In-Reply-To: <1053890450.176406.1662537415380.JavaMail.zimbra@u-pem.fr> References: <740286845.4338662.1662453133776.JavaMail.zimbra@u-pem.fr> <08E4021C-C5A1-4E33-8E9B-F54DEE260A64@oracle.com> <1053890450.176406.1662537415380.JavaMail.zimbra@u-pem.fr> Message-ID: Summarizing my takeaways from talking over these use cases today: On Sep 7, 2022, at 12:56 AM, forax at univ-mlv.fr wrote: Here is a list of such value types: - unit types, value types like by example Nothing (which mean that a method never returns) with no fields. Because creating a ref on it creates something :) If you truly mean for such a class to have no instances, that's not something a class with a value type can assert?the default instance always exists. I can see how it would be nice, for example, to have a type like Void that is also non-nullable, but value types are not the feature to accomplish that. no, such classes have instances but they are empty. By example, an array of Nothing stores just its length in memory. Accessing a cell return the default value, Nothing.default which is equivalent to new Nothing(). And it can be used in generics too, a HashMap is more or less a set of ints. Clarifying: we're not talking about Nothing types, we're talking about Singleton types. The main concern is that an array of Singleton.ref must have an O(n) size, while an array of Singleton.val *could* have an O(1) size (if this optimization were implemented?it's not something we've prototyped so far). In a sense, that's a much worse penalty for forgetting ".val" than the up to 2x storage penalty you might typically pay for a null flag. Is this practical? Singleton[] is probably not something you would write directly, but it could make sense in a generics context (something like the HashMap R?mi referenced above). Not in the mainstream of our use cases, but a use case to keep in mind. - wrappers/monads that modify the semantics, by example a generic value class Atomic that plays the same role as an AtomicReference, AtomicInteger, etc the problem here is that the default semantics is not the semantics the user want. Okay, so say we have value class Atomic, and we're in a future where this gets specialized. I think you're saying it will be important to say 'Atomic.val' at all uses rather than 'Atomic'. But I'm not clear on the argument for why that would be. Can you elaborate? If Atomic is declared as a ref-by-default value class, Atomic behave as a ref an not as a Foo + some atomic magic. So it is always wrong to use it. As the author of Atomic, i don't want my users to have a way to shoot themselves into the foot. Here, Atomic should be flat-by-default and also the ref variant should not exist, so there is no way to misuse the Atomic API. (Point of confusion for me: no, we're not talking whether the *field* of Atomic is flattened. This discussion is about the treatment of the Atomic instance, the *wrapper* around the field.) The argument in this case is that the class Atomic is, essentially, a user-defined variable modifier. (If it could be spelled 'atomic Foo x', that would be great, but user-defined modifiers are not a feature of Java.) It doesn't need to create an extra layer of wrapping around whatever it's storing; it just needs to add some custom behavior to reads/writes. In standard usage, it should never be null (and should probably always be final...). Except... (sorry, just thinking about this now, didn't raise it earlier): how can a variable wrapper be a value class? The whole point is to control *mutation* of the payload. Value class fields are final. So I'll tentatively say that this isn't a category of use cases Valhalla is going to address. You can't have mutable fields without object pointers to get to them. - SIMD vectors, if those are nullable, the VM/JIT will insert implicit null checks which are not usually a problem apart in thigh loop like users write with SIMD vectors. The *storage* should definitely use a value type, so in this sort of application we'd encourage value-typed array allocations (and value-typed type arguments for wrapping data structures). In a loop over a flat array, I would expect it to be okay to talk about the reference type in source, and have the JIT generate optimal code to work with the underlying flat storage, without any allocations or null checks. My sense is that we suspect that this can work reliably, but it could use more targeted performance testing to confirm. Sorry for not being clear, i'm more worry about people using IntVector instead of IntVector.val on stack and the VM having to emit a nullcheck inside a loop, by example if there is a call to a method that returns an IntVector is not inlined. For me, the vector API represents operation so a ref-by-default IntVector is a kind of useless, worst it will work but performance will suffer with no simple way to fix that apart taking a look to the generated assembly code. In theory, there's no problem here: value class reference types can be scalarized everywhere on stack, and null checks/flags can often be optimized away. (And even if they can't?if a loop body can't be inlined and so contains method calls that do null checks, say?the overhead of null checks is probably not a significant extra cost.) But in practice, totally possible that there are some holes. Insert open invitation here for people to provide performance test cases where on-stack usage of reference types is noticeably more expensive than the equivalent code using value types. That would really help to ground this conversation (is it something we can optimize away, or an inherent limitation? is it a serious problem for reference-default, or a corner case where we can encourage sprinkling in ".val" as a workaround?) - existing value classes in Scala or Kotlin, those are not nullable by default but in the current design, getClass() will happily reflect them with a nullable class making Scala/Kotlin second class citizens of the Java platform. Is the problem here that Scala/Kotlin will want reference-default interpretation of names in Java source? (If so, , if you want a good user experience with Kotlin types, write Kotlin source.) I don't know the answer to that question but i disagree with your conclusion, interrop with Java, so with the Java ecosystem is important. Answer: no, we're not talking about Java source using Kotlin types. It's reflection, discussed below. Or is the problem that they will want the reflection API to behave differently, making Foo.val.class the "primary" class object, not a secondary one? (If so, again, the Java reflection API is Java-oriented, interop is not a major factor in its design.) Maybe I'm missing your point on this one? The problem is that we are forcing the companion class design to other languages than Java. C# has done the same mistake with their generics being too specific to C# so other languages on the .Net platform all suffer. Other languages than Java can not makes Foo.val.class their primary class object because the companion class design is bolt into the VM. Right: other languages might have value classes that are a good match for value-default?in particular they would be happiest to see the value type when they call 'getClass'. (Perhaps doing something else would be a behavioral incompatibility, should they choose to migrate their implementation from roll-your-own to native JVM features.) On this one, I think we agreed that this is a "nice to have", if it works out in a way that is convenient for other languages, but this isn't something that would drive our design decisions. -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Sat Sep 10 01:50:34 2022 From: john.r.rose at oracle.com (John Rose) Date: Fri, 09 Sep 2022 18:50:34 -0700 Subject: What we have lost ? In-Reply-To: References: <740286845.4338662.1662453133776.JavaMail.zimbra@u-pem.fr> <08E4021C-C5A1-4E33-8E9B-F54DEE260A64@oracle.com> <1053890450.176406.1662537415380.JavaMail.zimbra@u-pem.fr> Message-ID: <6BA4B504-CAD0-4790-A0EC-5CBA955BCAB5@oracle.com> On 7 Sep 2022, at 19:19, Dan Smith wrote: > Summarizing my takeaways from talking over these use cases today: > > On Sep 7, 2022, at 12:56 AM, > forax at univ-mlv.fr wrote: > > Here is a list of such value types: > - unit types, value types like by example Nothing (which mean that a > method > never returns) with no fields. > Because creating a ref on it creates something :) > > If you truly mean for such a class to have no instances, that's not > something a > class with a value type can assert?the default instance always > exists. I can > see how it would be nice, for example, to have a type like Void that > is also > non-nullable, but value types are not the feature to accomplish that. To pile on with Dan, value types are not at all like empty types (nothing, not-reached). Like all objects, they are records, products of fields, and so can be units (what Dan calls singletons), but not empties (nothings). Only types structured as sums can be empty. Background: A unit type is different from an empty type, just as the product identity element 1 (in various settings) differs from the sum identity element 0 (in various settings). A unit type has one instance and takes up no storage (O(1) storage globally for its metadata), but can be stored in many places. An empty type has zero instances, and takes up no storage because it cannot be stored anywhere, not even on stack. Void is a unit type, because you can return from a void method. NotReached is an empty type, because you cannot return from a method that declares its return as not reached. If you add a unit type to a union (sum type), you increase the information content of that union; a unit type is not an identity for sum-aggregation. By contrast, if you add an empty type to a union, you leave the union unchanged (since that union branch is never reached); an empty type is an identity for sum-aggregation in the same way that 0 is an identity for addition. Likewise, if you add a unit type to a product (tuple record), you leave the record unchanged in its information content, because a unit type is an identity for product-aggregation. But if you add an empty type to a record, you change it; the record can no longer be instantiated, so it collapses to an empty type (with a different label, maybe). This is one reason we associate 1 with units and 0 with ?empties?: the product of an empty times any type is empty, just as 0*x = 0. Taking this all into account, a type `Map` is *not* a set of ints. It is a set that *you cannot take any ints out of*. It is an empty type, isomorphic to `Nothing` itself (assuming `Nothing` is empty not a unit). I think the closest Java gets to mapping the world of empty results is `throws` clauses, which talk about how a method?s (normal) return point can fail to be reached. (From another POV, both kinds of method returns, normal and exceptional, are coordinated terms in a sum-aggregation of the form `Either`.) But `throws` don?t get all the way there, since regardless of its `throws` clauses, a method can *always* return normally, so it?s return type is never actually empty. An empty type is what you get when you dereference `null`. Maybe the type of dereferencing `null` could be something like `NotReached`? Maybe. But if we did such a thing, the generic type `NotReached` would have to be special-cased everywhere in the language. Probably you could never declare a field or variable of that type. (Except in dead code. But Java forbids you to write dead code.) Methods which return `NotReached` would be obligated to throw the indicated exception (or `RTE` or `Error`) instead of returning. FWIW, I really like that value types can express labeled units, and embrace all the quirky corollaries, such as that an array of those guys is isomorphic to a non-negative `int`. I look forward to the day when `Map.Entry` specializes so that the second component takes up zero bits, and so sets can be built on maps with storage efficiency. *Note* that `Void`! I really mean `java.lang.Void`, not a new value class. If I specialize to a type which is hardwired to have zero instances (and that means it is always `null`) I have a unit type that?s even better than a value-class with no fields, since the `null` is the only possible value, not `null` plus a default instance. Specialized generics should IMO specialize on `Void`, when we get there! The conversation about a hypothetical specialized generic called `Atomic` for Java is way premature, IMO. Is it mutable or immutable? Does it affect its container? ?Who knows? I am sympathetic with the idea that wrapping a specialized generic around a random type can somehow create a new kind of container for that type; I envision such generics for `Atomic`, `Contended`, `WeakReference`, `Lazy`, and many more, to replace various ad hoc mechanisms we have today. I want to try for it, but I don?t regard any of those as a necessary goal, just a ?nice to have if we can get it?. (One example: We suffer from the lack, today, of arrays of weak references. But it?s not clear that value-types-plus-arrays can ever help with that. I?m OK if they don?t. We don?t need to replicate all of C++?s `atomic` template tricks, nor the tricks from any particular language.) Regarding unwanted nulls from ref-default types: Meh; the JVM has a quarter century invested in dealing with unwanted nulls, to the point where folks just don?t think about them very much as a performance problem. As a correctness problem, they are a pain, but again we have many ways to cope. I think forgetting to put `.val` on your fields or array creations is just fine; you can ship code that has too many nulls with a clear conscience, as long as you have a way to recognize when they are a measurable problem. At that point, fix your code by adding `.val` in a few places. But you can wait to measure a need in most cases. People writing high-performance code with complex numbers or vectors will readily learn where they must ?salute the val?; it is no worse than the current question of when to use `float` or `Float`. The rest of us can mostly ignore the issue. To conclude, I don?t feel much ?loss? here, nor do I think we are ?lost?. But some of the confusion between units and empties did leave me ?at a loss?. Hence this message. ? John -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Sat Sep 10 02:03:38 2022 From: john.r.rose at oracle.com (John Rose) Date: Fri, 09 Sep 2022 19:03:38 -0700 Subject: What we have lost ? In-Reply-To: <6BA4B504-CAD0-4790-A0EC-5CBA955BCAB5@oracle.com> References: <740286845.4338662.1662453133776.JavaMail.zimbra@u-pem.fr> <08E4021C-C5A1-4E33-8E9B-F54DEE260A64@oracle.com> <1053890450.176406.1662537415380.JavaMail.zimbra@u-pem.fr> <6BA4B504-CAD0-4790-A0EC-5CBA955BCAB5@oracle.com> Message-ID: <0C6F38A0-E120-4F3F-AB57-05432928DFA2@oracle.com> P.S. There?s a little more to say about empties: > Taking this all into account, a type `Map` is *not* a set > of ints. It is a set that *you cannot take any ints out of*. It is > an empty type, isomorphic to `Nothing` itself (assuming `Nothing` is > empty not a unit). Oops, actually, while it is true that you can?t retrieve any bindings from `Map`, it is *not* true that the composite type is an empty. It is, in fact, an always-empty set/map. And *that* is either an empty identity object (lots of those to go around, whee!) or else the unique empty map of a particular value class. That latter guy acts like a unit type. I tripped myself up here on a corner-of-a-corner-case named ?0^0?. A zero-ary replicated product of an empty type is (if I?m keeping score right) a unit, not an empty. (A map is an unordered replicated product of tuples, so an empty map is a zero-ary product. Modulo identity, there is only ever just one empty map.) A unary, binary, or greater replicated product of empties is empty, but not a zero-ary. Going back to arithmetic, an N-way replicated product of type X looks like X^N. An N-way replicated product of an empty looks like 0^N. That in turn is (equivalent to) an empty, so 0^N = 0 for N>0, but 0^0 = 1. That?s true in the basically logical setting, I think. Under other possible rules 0^0 is 0 or NaN. I suppose the Java type system could adopt the analogs of those outcomes as well for `Map`: It could be declared by fiat to itself be a nothing-type (an empty) or a static error (that?s the NaN?). -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sun Sep 11 14:04:08 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sun, 11 Sep 2022 16:04:08 +0200 (CEST) Subject: What we have lost ? In-Reply-To: <24D3934E-DFD1-4CC0-91C0-A09D6C18A81D@oracle.com> References: <740286845.4338662.1662453133776.JavaMail.zimbra@u-pem.fr> <8954f7d5-bfe4-d189-6cc9-e659bb353e21@oracle.com> <563107867.703081.1662586048888.JavaMail.zimbra@u-pem.fr> <24D3934E-DFD1-4CC0-91C0-A09D6C18A81D@oracle.com> Message-ID: <35204479.2620653.1662905048877.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "Paul Sandoz" > To: "Remi Forax" > Cc: "Brian Goetz" , "valhalla-spec-experts" > Sent: Thursday, September 8, 2022 1:10:26 AM > Subject: Re: What we have lost ? > The Vector API is I think a proof point that ref projection can be acceptable > and can work. > > The Vector API implementation currently behaves approximately as if a vector > instance (whose concrete class is private) is a ref to a value object. The C2 > compiler performs aggressive unboxing and mapping "payloads" (analogous to > value objects) vectors hold to vector registers, rather than scalarizing the > element ?overlay? or view over the payload described by the vector?s shape and > element type (the species). > > In this current model we generally don?t recommend storing vector instances in > the heap (there are exceptions to that rule for constants), and instead vectors > are loaded from or stored to memory containers such as arrays, byte buffers, or > memory segments. After a few iterations of the API and implementation its > starting to work rather well at generating efficient vectorized code on > supporting hardware. > > > We are currently early in the process of exploring alignment of the Vector API > with Valhalla where: > > 1. the private concrete vector classes are value classes whose .val type is > private; and > 2. a vector value class has a "payload" field whose type is a value type. > > The "payload" is a non-atomic "bag of bits" e.g. values types of Bits256.val, > Bits128.val etc, which would not be exposed in the public Vector API. So no > utterance of .val is expected in this model. The API remains the same as it > does today. > > We could decide the value classes of those ?bag of bits? could be public > classes, and developers may interact with those using the .val. Unsure yet. As > we explore and learn more the approach could change. I hope the exploration > will be informative. The problem of the current API is that it only works if everything is inlined by C2, otherwise performance is awful. If a method is not inlined because its assembly code is too big, performance is awful, if a species is not a constant (some students just forget "final" when declaring the species as static final), performance is awful, if an operator (ADD, DIV, etc) is not a constant, performance is awful. I'm pretty sure, i've already said that but i believe there is a better API design that can work even if a method is not inlined, because being sure that everything is inlined is a hard goal to attain once the code starts to grow. For that a vector should be a generic value class parametrized by the vector species (so the species is always a constant) with the actual species been private, not visible from the language, an Int128Vector becomes an IntVector at compile time and an IntVector at runtime. In that world if a method is not inlined and a value class if a ref, you have nullchecks appearing and those will be hard to see apart taking a look to the assembly code. If an IntVector has no ref companion, you eliminate such kind of performance hiccup. > > Paul. R?mi > > > >> On Sep 7, 2022, at 2:27 PM, forax at univ-mlv.fr wrote: >> >> >> >> From: "Brian Goetz" >> To: "Remi Forax" , "valhalla-spec-experts" >> >> Sent: Wednesday, September 7, 2022 8:38:46 PM >> Subject: Re: What we have lost ? >> I would summarize this mail as "no new observations, but growing discomfort with >> ref-as-default." All of these points amount to "there are classes for which >> most uses will prefer val to varying degrees, but users may forget to say .val >> / avoid saying .val because it is clunky", which may turn into performance >> potholes. These are surely known, so I will take this as Remi worrying that >> this will be a bigger and more persistent irritant than we think. >> >> If we take a step back, one of the main reason of introducing value types is to >> have a way to be closer to the actual CPU architecture which can also be >> described as solving Java performance potholes. >> So i think we should shy away that term. >> >> Dan help us to categorize the different issues, >> - singleton types and vector representing SIMD registers are similar cases where >> using the ref projection is far worst than the val projection, to the point >> where author of those classes may prefer to not have a ref projection. >> In the case of an empty value class, storing the val projection use no memory, >> so the difference in term of the behavior is big, >> In the case of a SIMD register, if the VM loose track of the creation of >> something which is typed as the reference projection, a nullcheck will to be >> emitted, while usually we don't care about the cost of a nullcheck, having a >> supplementary nullcheck in a thigh loop is a far bigger issue to the point >> where having a reference projection for such types is counter productive. >> >> - Atomic (or any class that play the role of a modifier like Stable, etc) is >> when the ref projection have the wrong semantics so should not exist and there >> is no need for a lightweight boxing given that you can always extract the value >> from the monad (and use its reference projection if necessary). >> >> - the last case is a case where the way the value classes are defined in >> Scala/Kotlin can not be retrofitted to use the VM value classes because >> getClass() always returns the class of the ref projection. This is also a case >> where those value classes should not have a ref projection. >> >> >> (I think the idea of "class for which there is no ref type" is a non-starter; >> for all of the types you talk about (vectors, etc), you could make the same >> argument for `int`, but no one is saying we don't need `Integer`. I think what >> you are really getting at here is coming back to some form of "I want >> val-default".) >> >> There are types which should never be null for different reasons, for those >> types having a reference projection is an issue. >> A nullable int is something useful hence we need Integer, sadly not all the >> value types are like this. >> >> >> A control question I would ask (though let's keep the bikeshedding to a dull >> roar) is how much of this is about the undeniable clumsiness of the locution >> "Point.val". If, for example, the val type were called "point" or "Point!", as >> some people have already publicly wished, does this change your concern that >> "users will get it wrong all the time"? >> >> I really believe that not all (value) types should have a reference projection. >> >> R?mi >> >> >> >> >> On 9/6/2022 4:32 AM, Remi Forax wrote: >> Hi everybody, >> it seems to me that the current design has reached a kind of local maximum, >> which is nice and not nice at the same time, >> so i would like to take the time to reflect on what we have sacrificed when >> moving to the current design. >> >> What is missing/not supported by the current model is value classes that should >> not be used by reference, >> either because it will cause performance issues or because the user will not get >> the semantics he think he will get. >> >> Here is a list of such value types: >> - unit types, value types like by example Nothing (which mean that a method >> never returns) with no fields. >> Because creating a ref on it creates something :) >> >> - wrappers/monads that modify the semantics, by example a generic value class >> Atomic that plays the same role as an AtomicReference, AtomicInteger, etc >> the problem here is that the default semantics is not the semantics the user >> want. >> >> - SIMD vectors, if those are nullable, the VM/JIT will insert implicit null >> checks which are not usually a problem apart in thigh loop like users write >> with SIMD vectors. >> >> - existing value classes in Scala or Kotlin, those are not nullable by default >> but in the current design, getClass() will happily reflect them with a nullable >> class making Scala/Kotlin second class citizens of the Java platform. >> >> Those are my 4 tent poles, they are maybe others, but currently we fail to >> provide a good answer for those cases. >> >> regards, >> R?mi >> >> >> >> >> From paul.sandoz at oracle.com Mon Sep 12 17:31:09 2022 From: paul.sandoz at oracle.com (Paul Sandoz) Date: Mon, 12 Sep 2022 17:31:09 +0000 Subject: What we have lost ? In-Reply-To: <35204479.2620653.1662905048877.JavaMail.zimbra@u-pem.fr> References: <740286845.4338662.1662453133776.JavaMail.zimbra@u-pem.fr> <8954f7d5-bfe4-d189-6cc9-e659bb353e21@oracle.com> <563107867.703081.1662586048888.JavaMail.zimbra@u-pem.fr> <24D3934E-DFD1-4CC0-91C0-A09D6C18A81D@oracle.com> <35204479.2620653.1662905048877.JavaMail.zimbra@u-pem.fr> Message-ID: <07ED2168-C000-4D83-933F-C1B0AC54A97F@oracle.com> Part of the exploration that I omitted in my prior email is to enhance C2s calling convention to support vector arguments, thereby enabling the passing of vector arguments as vector registers. (This might also shed some light on how we might support Panama native calls too.) The constant species might come along for the ride as a side-argument with the ?bag of bits? e.g. an ad-hoc form of specialization. Hard to say exactly until we experiment. Paul. > On Sep 11, 2022, at 7:04 AM, forax at univ-mlv.fr wrote: > > ----- Original Message ----- >> From: "Paul Sandoz" >> To: "Remi Forax" >> Cc: "Brian Goetz" , "valhalla-spec-experts" >> Sent: Thursday, September 8, 2022 1:10:26 AM >> Subject: Re: What we have lost ? > >> The Vector API is I think a proof point that ref projection can be acceptable >> and can work. >> >> The Vector API implementation currently behaves approximately as if a vector >> instance (whose concrete class is private) is a ref to a value object. The C2 >> compiler performs aggressive unboxing and mapping "payloads" (analogous to >> value objects) vectors hold to vector registers, rather than scalarizing the >> element ?overlay? or view over the payload described by the vector?s shape and >> element type (the species). >> >> In this current model we generally don?t recommend storing vector instances in >> the heap (there are exceptions to that rule for constants), and instead vectors >> are loaded from or stored to memory containers such as arrays, byte buffers, or >> memory segments. After a few iterations of the API and implementation its >> starting to work rather well at generating efficient vectorized code on >> supporting hardware. >> >> >> We are currently early in the process of exploring alignment of the Vector API >> with Valhalla where: >> >> 1. the private concrete vector classes are value classes whose .val type is >> private; and >> 2. a vector value class has a "payload" field whose type is a value type. >> >> The "payload" is a non-atomic "bag of bits" e.g. values types of Bits256.val, >> Bits128.val etc, which would not be exposed in the public Vector API. So no >> utterance of .val is expected in this model. The API remains the same as it >> does today. >> >> We could decide the value classes of those ?bag of bits? could be public >> classes, and developers may interact with those using the .val. Unsure yet. As >> we explore and learn more the approach could change. I hope the exploration >> will be informative. > > > The problem of the current API is that it only works if everything is inlined by C2, otherwise performance is awful. > If a method is not inlined because its assembly code is too big, performance is awful, if a species is not a constant (some students just forget "final" when declaring the species as static final), performance is awful, if an operator (ADD, DIV, etc) is not a constant, performance is awful. > > I'm pretty sure, i've already said that but i believe there is a better API design that can work even if a method is not inlined, because being sure that everything is inlined is a hard goal to attain once the code starts to grow. > > For that a vector should be a generic value class parametrized by the vector species (so the species is always a constant) with the actual species been private, not visible from the language, an Int128Vector becomes an IntVector at compile time and an IntVector at runtime. > > In that world if a method is not inlined and a value class if a ref, you have nullchecks appearing and those will be hard to see apart taking a look to the assembly code. > If an IntVector has no ref companion, you eliminate such kind of performance hiccup. > >> >> Paul. > > R?mi From forax at univ-mlv.fr Mon Sep 12 22:48:12 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Tue, 13 Sep 2022 00:48:12 +0200 (CEST) Subject: What we have lost ? In-Reply-To: <07ED2168-C000-4D83-933F-C1B0AC54A97F@oracle.com> References: <740286845.4338662.1662453133776.JavaMail.zimbra@u-pem.fr> <8954f7d5-bfe4-d189-6cc9-e659bb353e21@oracle.com> <563107867.703081.1662586048888.JavaMail.zimbra@u-pem.fr> <24D3934E-DFD1-4CC0-91C0-A09D6C18A81D@oracle.com> <35204479.2620653.1662905048877.JavaMail.zimbra@u-pem.fr> <07ED2168-C000-4D83-933F-C1B0AC54A97F@oracle.com> Message-ID: <730956941.3449398.1663022892877.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "Paul Sandoz" > To: "Remi Forax" > Cc: "Brian Goetz" , "valhalla-spec-experts" > Sent: Monday, September 12, 2022 7:31:09 PM > Subject: Re: What we have lost ? > Part of the exploration that I omitted in my prior email is to enhance C2s > calling convention to support vector arguments, thereby enabling the passing of > vector arguments as vector registers. > (This might also shed some light on how we might support Panama native calls > too.) > > The constant species might come along for the ride as a side-argument with the > ?bag of bits? e.g. an ad-hoc form of specialization. Hard to say exactly until > we experiment. I want the VM to speculate on the type argument of a raw type / wildcard, so there is no need to pass the argument as a parameter. Obviously, it's harder that speculating on the implementation of a virtual method but a wildcard is a kind of virtual representation of several parametrized generics classes. If a method like foo() is not inlined static final VectorSpecies SPECIES = IntVector.SPECIES_128; IntVector foo() { return IntVector.broadcast(SPECIES, 42); } the VM can still know that the real return type is IntVector. If not in the case of the Vector API where we want to hide the Species, a generic method is enough because it will be specialized, so i'm not sure there is a need to pass a type argument as an argument on the stack. > > Paul. > R?mi > >> On Sep 11, 2022, at 7:04 AM, forax at univ-mlv.fr wrote: >> >> ----- Original Message ----- >>> From: "Paul Sandoz" >>> To: "Remi Forax" >>> Cc: "Brian Goetz" , "valhalla-spec-experts" >>> >>> Sent: Thursday, September 8, 2022 1:10:26 AM >>> Subject: Re: What we have lost ? >> >>> The Vector API is I think a proof point that ref projection can be acceptable >>> and can work. >>> >>> The Vector API implementation currently behaves approximately as if a vector >>> instance (whose concrete class is private) is a ref to a value object. The C2 >>> compiler performs aggressive unboxing and mapping "payloads" (analogous to >>> value objects) vectors hold to vector registers, rather than scalarizing the >>> element ?overlay? or view over the payload described by the vector?s shape and >>> element type (the species). >>> >>> In this current model we generally don?t recommend storing vector instances in >>> the heap (there are exceptions to that rule for constants), and instead vectors >>> are loaded from or stored to memory containers such as arrays, byte buffers, or >>> memory segments. After a few iterations of the API and implementation its >>> starting to work rather well at generating efficient vectorized code on >>> supporting hardware. >>> >>> >>> We are currently early in the process of exploring alignment of the Vector API >>> with Valhalla where: >>> >>> 1. the private concrete vector classes are value classes whose .val type is >>> private; and >>> 2. a vector value class has a "payload" field whose type is a value type. >>> >>> The "payload" is a non-atomic "bag of bits" e.g. values types of Bits256.val, >>> Bits128.val etc, which would not be exposed in the public Vector API. So no >>> utterance of .val is expected in this model. The API remains the same as it >>> does today. >>> >>> We could decide the value classes of those ?bag of bits? could be public >>> classes, and developers may interact with those using the .val. Unsure yet. As >>> we explore and learn more the approach could change. I hope the exploration >>> will be informative. >> >> >> The problem of the current API is that it only works if everything is inlined by >> C2, otherwise performance is awful. >> If a method is not inlined because its assembly code is too big, performance is >> awful, if a species is not a constant (some students just forget "final" when >> declaring the species as static final), performance is awful, if an operator >> (ADD, DIV, etc) is not a constant, performance is awful. >> >> I'm pretty sure, i've already said that but i believe there is a better API >> design that can work even if a method is not inlined, because being sure that >> everything is inlined is a hard goal to attain once the code starts to grow. >> >> For that a vector should be a generic value class parametrized by the vector >> species (so the species is always a constant) with the actual species been >> private, not visible from the language, an Int128Vector becomes an IntVector at >> compile time and an IntVector at runtime. >> >> In that world if a method is not inlined and a value class if a ref, you have >> nullchecks appearing and those will be hard to see apart taking a look to the >> assembly code. >> If an IntVector has no ref companion, you eliminate such kind of performance >> hiccup. >> >>> >>> Paul. >> > > R?mi From daniel.smith at oracle.com Tue Sep 20 23:41:03 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Tue, 20 Sep 2022 23:41:03 +0000 Subject: EG meeting *canceled*, 2022-09-21 Message-ID: <4D76D7E8-F510-4032-B827-99AE73E2570C@oracle.com> I don't think there's anything new here beyond what was covered last time, so let's cancel. (I do owe some detailed responses in the spec threads, though...) From brian.goetz at oracle.com Wed Sep 21 19:43:06 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 21 Sep 2022 15:43:06 -0400 Subject: Fwd: Value objects with no companion reference type In-Reply-To: References: Message-ID: <512ca8dc-4bb7-151d-075c-619499f5c5f4@oracle.com> Received on the -comments list. -------- Forwarded Message -------- Subject: Value objects with no companion reference type Date: Tue, 20 Sep 2022 16:17:06 -0500 From: Clement Cherlin To: valhalla-spec-comments at openjdk.org Are ref-less Objects compatible with the Java language? I think the answer may sadly be "no". Imagine OptionalValue is an interface for Optional-like types and Int128 is a 128-bit integer value type. no-ref value record NoRefOptional(T item) implements OptionalValue { } new OptionalValue[] = { new NoRefOptional<>(new Int128(3)) }; What happens? An interface-typed array is necessarily an array of references. You can't stuff a larger-than-reference-size inline value in there without breaking the fixed-size semantics of a Java array. Either you need a pseudo-reference which takes up the same space as a reference and acts like a reference but isn't a reference (what is it?), or you get an ArrayStoreException because the value doesn't fit. This will occur any time you try to store a no-ref object in a ref-typed-array of superclass or superinterface type, including the omnipresent Object[]. In order to wholly avoid the Object[] problem, no-ref values could not be cast to Object, same as primitives. No-ref values could not be cast to interface types, same as primitives. Client code would have to treat each one as a unique, unrelated type... same as primitives. The only way to pass no-ref values and store them in reference contexts would be to wrap them in heap objects, but without the possibility of auto-boxing. In essence, to treat them as Objects, you would need to either resort to a cumbersome generic like Optional, or manually implement a ref-mirror like Integer. Do we want that? Valhalla is finally unifying primitives with objects. No-ref values would recreate many of the sharp edges Valhalla is sanding off of primitives. While I fully believe Java *should* have been designed from the beginning to support user-defined non-null / no-ref objects, it wasn't. I think it's too late to shoehorn them in without breaking changes to the language. The bedrock assumption that you can store, pass, or return _any_ value via an "Object" or "Object[]" typed variable, parameter or method is too deeply ingrained into Java syntax (Object... x) and libraries (map.get()) to fix at this late date. While I badly want no-ref (or at least non-nullable) value types in Java, I don't see a way to get them without either a hard fork (Python 3 style), or making them behave in all the awkward, inconvenient ways primitives do pre-Valhalla. I think the best we can reasonably do, given Java's pervasive ref-ness, is non-nullable value types that can be converted into special non-null refs which will often be inlined (lightweight boxing). Those have challenges of their own including array initialization, covariant arrays, and heap pollution, but I'm hopeful those problems are solvable. You will still run into NullPointerExceptions attempting to cast null from Object or interface types to non-null types. That problem exists right now when unboxing a null primitive wrapper. So while it's no better, it's also no worse. I do still think .ref and .val projections as distinct types with their own .class objects and especially their own visibility mechanics is an undesirable outcome. If there's any reasonable way to unify them, they should be unified. Cheers, Clement -------------- next part -------------- An HTML attachment was scrubbed... URL: