As a reminder, Leyden will give us a more general tool for "moving stuff around" at build time than CDS does, and that the current CDS behavior may well be folded into a set of condensers.

We are trying to find the "perfect" place to put preload information, but we have (as usual) an overconstrained notion of perfection; what makes perfect sense for semantics or non-duplication may not make perfect sense for runtime behavior.

Leyden will let us cut this knot by letting us put the information in the classfile in the semantically sensible place, and let tooling boil it down later at pre-deployment time to a representation that is more efficient for runtime.

So what I suggest is focusing on capturing the source data, which IMO seems to still be some flavor of "class/method X needs to know more about value class V before making certain decisions".  Preloading is the mechanism of how we find out that "more", and aggregated representations such as per-module / CDS archives are a rearranging of the source data to achieve a more runtime friendly representation _for a particular configuration of classes_.

tl;dr: Let's design what captures the semantics we need, and treat computing e.g. optimal load order as a downstream transformation.

On 6/12/2023 9:26 AM, Dan Heidinga wrote:
The top-line goal for the preload efforts is to trigger the necessary "go and look" behaviour to support calling convention flattening for values.  We want the broadest, most reliable mechanism to ensure that we routinely get flattening in the calling convention for value types so that the flattening horizon can extend beyond a single compiled body (ie: a method and its inlines).

Summarizing the options presented so far:

A) Value classes should be put into the CDS archive to ensure they are loaded early enough, in a group, and in a form that the VM can quickly discover whether calling convention optimizations apply to them.  This involves either a class list to create a static archive (allows jdk classes) or using a dynamic archive with AppCDS.  Both cases require a "cold run" to generate the data needed for CDS and only capture classes that have been loaded during that run (I think that's correct?).

B) Use a "Watch List" to list class names that should be looked for.  When the name appears, trigger loading early enough to allow calling convention optimizations to apply. Name conflicts are "safe" as the worst case is a class is loaded early in multiple loaders but is only a value in one loader.  The watch list can be: global or per-module.  It's possible a tool like jlink or jmod could be used to generate the watch list by scanning all the classes included in the jimage/jmod file.

C) The per-class preload attribute.  Each class lists the value classes it may reference to ensure they are loaded early enough.  Potentially a lot of duplication as each class in an application would list many of the same value classes.

Did I miss any?

There's also another dimension we've touched on: how eager is eager loading.  Current preload behaviour is to batch load all the listed classes.  Alternatively, loading could wait until one of the classes was observed in method signature / field signature and load on an as-needed basis.

We've mostly concentrated on preload as an optimization for calling conventions but there may be other uses of the mechanism as well.  A user may want to ensure that classes are loaded early to prevent optimizations that need to be walked back later based on their knowledge of application behaviour.  For example, ensuring there is always more than a single implementor of an interface loaded to prevent CHA optimizations on some critical path where the second implementation is normally loaded late.  Or to ensure an entire sealed hierarchy is loaded together.  I haven't put much thought into this yet but expect users will find interesting ways to use "preload" if it's reliable enough for them.  (And of course, some will abuse it in ways that hurt their performance as well).

Which of these options meets the goal ("reliable, routine calling convention optimization for values") best?

--Dan

On Fri, Jun 9, 2023 at 9:38 PM John Rose <john.r.r...@oracle.com> wrote:

    On 9 Jun 2023, at 12:41, Dan Heidinga wrote:

        On Thu, Jun 8, 2023 at 4:51 PM John Rose
        <john.r.r...@oracle.com> wrote:

            On 8 Jun 2023, at 9:52, Dan Heidinga wrote:

            On Thu, Jun 8, 2023 at 12:44 PM John Rose
            <john.r.r...@oracle.com> wrote:

            On 8 Jun 2023, at 9:01, Dan Heidinga wrote:

            If we decouple the list of preloadable classes from the
            classfile, how
            would non-jdk classes be handled?> What if instead of
            ditching the

            attribute, or treating it like an

            optimization, we firmed up the contract and treated it as
            a guarantee…

            If we go down this route, let’s consider putting the
            control information
            into a module file (only) for starters. (Maybe class file
            later if
            needed.) There would be fewer states to document and test,
            since (by
            definition) class files could not get out of sync.

            A module would document, in one mplace, which types it
            would “prefer” to
            preload in order to optimize its APIs (internal or external).

            This might lead to more class loading than intended. The
            current approach
            has each classfile register the list of classes it wants
            preloaded to get
            the best linkage which means we only have to load those
            classes if we link
            the original class. There's a natural trigger for the
            preload and a
            limited set of classes to load.

            There’s a spectrum of tradeoffs here: We could put preload
            attributes on
            every method and field, to get the maximum amount of
            fine-grained lazy
            (pre-)loading, or put them in a global file per JVM
            instance. The more
            fine-grained, the harder it will be to write compliance
            testing, I think.

        Agreed. There's a sweet spot between expressiveness and overheads
        (testing, metadata, etc). Classfiles have historically been
        the place
        where the JVM tracks this kind of information as that fits
        well with
        separate compilation and avoids the "external metadata"
        problems of ie:
        GraalVM's extra-linguistic configuration files.

        When compiling the current class, javac already requires directly
        referenced classes to be findable and thus has the info
        required to write a
        preload attribute. Does javac necessarily have the same info when
        compiling the module-info classfile? Maybe when finding the
        non-exported
        packages for the module javac (or jlink? or jmod?) could also
        find the
        value classes that need preloading?

    That is what I am assuming. The module file would be edited by
    those guys. Or (maybe better) a plain flat textual list is put
    somewhere the JVM can find it.

        Moving it into a separate pass like this doesn't feel like
        quite the right
        fit though as it excludes the classpath and complicates the
        other tools
        processing of the modules.

    I think it’s better than that. When we are assembling a program
    (jlink or a Leyden condenser), the responsibility of publicizing
    value classes (for Preload) surely belongs to the declaration, not
    collectively on all the uses.

    So every module (jmod or whatever) that declares 1 or more value
    classes (if they are exported, at least) should list them on a
    publicized watch list.

    There is no need to replicate these watch lists across all
    potential API clients of a value class. There are reasons /not/ to
    do this, since the clients have only partial, provisional
    information about the values.

            Moving to a single per-module list loses the natural
            trigger and may
            pre-load more classes than the application will use. If
            Module A has
            classes {A, B, C} and each one preloads 5 separate
            classes, with a
            per-module list that's forcing the loading of 15
            additional classes (plus
            supers, etc). With a per-class list, we only preload the
            classes on a
            per-use basis. More of a pay for what you use model.

            Is there a natural trigger or way to limit the preloads to
            what I might
            use
            with the per-module file?

            That’s a very good question. I think what Preload *really
            is* is a list
            of “names that may require special handling before using
            in APIs”. They
            don’t need to be loaded when the preload attribute is
            parsed; they are
            simply put in a “watch list” to trigger additional loading
            *when
            necessary*. (This is already true.) So I think if we move
            the preload
            list to (say) the module level (if not a global file),
            then the JVM will
            have its watch list. (And, in fewer chunks than if we put
            all the stuff all
            the time redundantly in all class files that might need
            them: That requires
            frequent repetition.) The JVM can use its watch list as it
            does today, with
            watch lists populated separately for each class file.

        I initially thought a global list would lead to issues if two
        different
        classloaders defined classes of the same name but since this
        is a "go and
        look" signal, early loading based on name should be fine even
        in that case
        as each loader that mentions the name would be asked to be
        asked to load
        their version of the named class. So I think a per-JVM list
        would be OK
        from that perspective (though I still don't like it).

    Agreed.

            To emphasize: A watch list does not require loading. It
            means, “if you see
            this name at a point where you could use extra class info,
            then I encourage
            you to load sooner rather than later”. The only reason it
            is “a thing” at
            all is that the default behavior (of loading either as
            late as possible, or
            as part of a CDS-like thingy) should be changed only on an
            explicit signal.

        While true for what the JVM needs, this is hard behaviour to
        explain to
        users and challenging for compliance test writers (or maybe
        not if we
        continue to treat preload as an optimization).

    I’m trying to reduce this to a pure optimization. In that case,
    “watch lists” are just helpers, which are allowed to fail, and
    allowed to be garbage.

        Is this where we want to
        spend our complexity budget?

    (No, hence it should be an optimization.)

        Part of why I'm circling back to treating
        preload as a per-classfile attribute that forms a requirement
        on the VM
        rather than as an optimization is that the model becomes
        clearer for users,
        developers and testers.

    I think it’s still going to be murky. Why is putting the watch
    list on the API clients better than putting it on (or near) the
    value class definitions?

            And, hey, maybe CDS is all the primitive we need here:
            Just run -Xdump
            with all of your class path loaded. Et voila, no Preload
            at all.

        Users may find this behaviour surprising - I ran with a CDS
        archive and my
        JVM loaded classes earlier than it would have otherwise?

    CDS has the effect of making class loading in a more timely
    fashion, and (under Leyden) will almost certainly trigger
    reordering of loading as well. So promulgating a “watch list” has
    goals which align with CDS.

    I’m starting to think that the right “level” to pull for
    optimizing value-based APIs is to put the value classes in a CDS
    archive. That is a defacto watch list. The jlink guy should just
    make a table of all value classes. That’s the best form of Preload
    I can imagine, frankly.

Reply via email to