Reducing classes loaded by ClassFile API usage
-
liangchenblue at gmail.com
Thu May 2 14:34:48 UTC 2024
Hi Claes,
Class-File API is suitable for parsing and transforming class files too,
yet these functionalities are rarely used within the JDK itself, which
almost exclusively spin new classes. These no-op tests are simulating the
overheads from trying to go into a detailed object (such as only
transforming a particular instruction in a particular method in a class but
keeping everything else intact otherwise). With ASM, all details have to be
visited and all Strings are automatically expanded; Class-File API already
wins over ASM in this aspect, as CF API is more lazy.
For Class-File generation, I think our API has mostly focused on its
stack-map generation bottleneck (one being using MethodTypeDesc to speed up
slot counting); otherwise I don't recall we had major performance
improvements for writing, so Adam is using approaches like storing known
constants into a bound byte array constant pool so we can share the CP
prefixes.
Chen
On Thu, May 2, 2024 at 9:22 AM Claes Redestad <claes.redestad at oracle.com>
wrote:
> A performance loss where exactly?
>
> For classfile generation (and reflection) I’d be more concerned with
> cold-to-lukewarm cases of getting an app up and running than, say, the
> number you might get in synthetic benchmarks running the API at peak
> performance.
>
>
> 2 maj 2024 kl. 13:50 skrev Brian Goetz <brian.goetz at oracle.com>:
>
> For what it’s worth, there was an earlier experiment that merged the bound
> and unbound representation classes, and you could see a measurable
> performance loss just because these classes have more fields and there were
> more branches to access them.
>
> Sent from my iPad
>
> On May 2, 2024, at 7:34 AM, Claes Redestad <claes.redestad at oracle.com>
> wrote:
>
> Hi,
>
> Looking at replacing ASM with the ClassFile API (CFA) in various places we
> have observed both startup and footprint regressions. Startup times
> increase 4-5 ms on Hello World, 40 ms on a small GUI app and 250ms on a
> larger app. So there’s both a one-off cost and a scaling factor here.
>
> We’ve been doing some analysis and picked a lot of low-hanging fruit.
> Bytecode executed has been reduced to about the same level and we’ve found
> improvements in dependencies such as the java.lang.constant API. All good.
> And the number of classes loaded on a Hello World style application has
> dropped by about 50. Great!
>
> Still the overall picture persists: a Hello World style application that
> initializes a lambda takes a wee bit longer and the footprint is decidedly.
> The main culprit now that some low-hanging fruit has been plucked seem to
> be that the trivial use of CFA to spin up lambda proxies is loading in
> about 160 classes: An ASM-based baseline loads 691 classes. The best recent
> CFA version (a merge of https://github.com/openjdk/jdk/pull/19006 and
> https://github.com/openjdk/jdk/pull/17108) loads 834. A net 143 class
> difference.
>
> Loading classes slows down startup, increases memory footprint, grows the
> default CDS archive. And involving more classes - and more code - is often
> costly even accompanied with some of the solutions being explored to ”fix”
> startup at large.
>
> So why is this?
>
> The CFA is mainly split up into two package stuctures, one public under
> java.lang.classfile and one internal under jdk.internal.classfile.impl. In
> the public side most of the types are sealed interfaces, which are then
> implemented by an assortment of abstract and concrete classes under
> jdk.internal.classfile.impl. Very neat. But I do fear this means we are at
> least doubling the number of loaded classes from this neat separation.
>
> While it’s a bit late in the game I still feel I must propose striking up
> a conversation about what, if anything, we could consider that would reduce
> the number of loaded classes. Whether they are interfaces, abstract or
> concrete classes. I think any savings would be very welcome.
>
> Here’s an idea:
>
> There are a number of cases where the separation seem unnecessary:
>
> public sealed interface ArrayLoadInstruction extends Instruction
> permits AbstractInstruction.UnboundArrayLoadInstruction {
> *…*
>
> static ArrayLoadInstruction of(Opcode op) {
> Util.checkKind(op, Opcode.Kind.ARRAY_LOAD);
> return new AbstractInstruction.UnboundArrayLoadInstruction(op);
> }
> }
>
> An interface in java.lang.classfile.instruction which only permits a
> single implementation class - and as it happens has a static factory
> method which is the only place where that concrete instruction is called.
>
> Making single-use interfaces such as this one a final class is doable[1],
> but now we’d have some instructions modeled as an interface, others as
> classes. Cats and dogs, living together. And it gets messy quick for
> instructions that can be bound or unbound, since those inherit from
> abstract BoundInstruction or UnboundInstruction respectively. But perhaps
> internal implementation details like whether an instruction is bound or
> unbound ought to be modeled with composition rather than inheritance (and
> optional CodeImpl + pos tuple) in a shared base class? Then it might follow
> that each of the interfaces in java.lang.classfile.instruction can really
> be a single final class. If all concrete instructions were folded into
> their corresponding interface that could reduce the total number of
> implementation classes by 46 (though only 6 of those seem to be on a Hello
> World)
>
> Yikes, that’s a deep cut for a small, incremental gain. From an API
> consumer point of view I can’t say there’s much difference, and the
> factories can still (be evolved to) produce different concrete types when
> necessary.
>
> Maybe someone can think of other, simpler ways to reduce the number of
> types floating around in the ClassFile API?
>
> Thank you for your consideration.
>
> Claes
>
> [1]
> https://github.com/openjdk/jdk/compare/master...cl4es:jdk:fold_instruction_example?expand=1
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/classfile-api-dev/attachments/20240502/be1458ec/attachment.htm>
More information about the classfile-api-dev
mailing list