graal icon indicating copy to clipboard operation
graal copied to clipboard

Project Crema: Open World for Native Image

Open alina-yur opened this issue 7 months ago • 6 comments

TL;DR

Crema will lift Native Image's default closed-world assumption by allowing dynamic loading and execution of classes at run time. It builds upon Native Image Layers and the JDWP-based debugger by adding a Java interpreter to the application layer. This will significantly improve usability with libraries that produce JVM bytecode at run time. Additionally, it will enable dynamic loading of plugins, such as javac annotation processors.

Goals

  • When enabled, be able to dynamically load classes, link them to AOT-compiled classes, and execute their bytecodes.
  • Load classes from run time-created class loaders.
  • Load classes from build time-created class loaders after re-linking them to their classpath.
  • Configure which AOT classes can be linked to by dynamically loaded classes and which class loaders are enabled at run time.
  • In future releases Crema can also potentially provide a JIT compiler for the dynamically loaded bytecodes.

Non-Goals

  • It is not a goal to enable this capability by default, or to be able to add this capability to native images built prior to Crema.
  • It is not a goal to be able to fallback to classloading for classes that are included in the AOT image (although some "AOT" methods could be executed via the bytecode interpreter).

alina-yur avatar Jun 04 '25 09:06 alina-yur

Does this mean that the native image will have to include the byte codes (or AST) of all AOT compiled methods because they can potentially all be deoptimized after newly loaded classes invalidate assumptions that were made during AOT compilation?

simonis avatar Jun 06 '25 09:06 simonis

The open world mode (-H:-ClosedWorld, also used for non-final layers) doesn't make assumptions in its compiled code that can be invalidated by loading more classes. So we shouldn't need bytecode for methods that were AOT-compiled.

For methods that are not included in the image but that are declared in a type that is included in the image it might be necessary to keep bytecodes so that they can be executed if they become reachable from newly loaded code.

gilles-duboscq avatar Jun 06 '25 10:06 gilles-duboscq

Hi, What's the difference or relationship with https://openjdk.org/projects/leyden/.

liudonghua123 avatar Oct 02 '25 08:10 liudonghua123

Project Crema adds more "just-in-time" behaviours to fully ahead-of-time compiled code thanks to the ability to dynamically load (and just-in-time compile) extension code. This is different from the approach in project Leyden which uses training runs and caching to improve startup and warmup of a system that otherwise relies on "just-in-time" compilation.

gilles-duboscq avatar Oct 02 '25 10:10 gilles-duboscq

The open world mode (-H:-ClosedWorld, also used for non-final layers) doesn't make assumptions in its compiled code that can be invalidated by loading more classes. So we shouldn't need bytecode for methods that were AOT-compiled.

On the other hand, does that not imply that when compiling a non-final layer you are unable to do most (all?) of the build-time init that is currently possible using Native image? Also, does it not also mean that many of closed world optimizations that contribute to the performance of generated GraalVM compiled code will no longer be applicable? if so then do both of those outcomes have consequences for the footprint of the final image?

adinn avatar Oct 02 '25 13:10 adinn

@adinn Thank you for the questions!

The ability to do build time initialization for the ahead-of-time compiled code is unaffected. Specifically, one can build for example Quarkus (and its dependencies) into an ahead-of-time compiled base image including Quarkus' build time initialization optimizations and then run the user code (e.g., the defined controllers and REST end points) in Crema. The image binary is larger, but the startup and footprint benefits are largely unaffected.

The peak performance of the ahead-of-time compiled code compiled with open world assumption is in our benchmark measurements currently around ~5-10% lower. We do think we can reduce this in the future to be indistinguishable from closed world for practical purposes. You can see recent work in this area for example at pull request #12129 . What also helps is to seal classes and modules and in this way reduce the open world surface.

We do continue to recommend full ahead-of-time compilation wherever possible due to the full performance predictability and the smaller image sizes. But this work enables some important use cases for native image. This includes for example javac with annotation processors or also jshell. In the context of Quarkus, this can enable instant startup during development while the user can change his logic without recompiling the image, which is an approach I discussed with @Sanne recently.

thomaswue avatar Oct 02 '25 14:10 thomaswue