Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Significant increase of Quarkus compiled binary after upgrade from 3.6.7 to 3.7.1 #38600

Open
gsmet opened this issue Feb 6, 2024 Discussed in #38572 · 16 comments
Open

Significant increase of Quarkus compiled binary after upgrade from 3.6.7 to 3.7.1 #38600

gsmet opened this issue Feb 6, 2024 Discussed in #38572 · 16 comments

Comments

@gsmet
Copy link
Member

gsmet commented Feb 6, 2024

Discussed in #38572

Originally posted by vincejv February 3, 2024
Any one noticed a significant increase in the binaries compiled with 3.7.1 compared to 3.6.7? I'm using few Quarkus libraries on top of the framework, mostly just rest client related, and I have observed the docker image published in docker hub increased by 10mb+ (https://hub.docker.com/r/vincejv/qbittorrent-exporter/tags) 0.0.18 (37mb) vs 0.0.17 (28mb).

I thought removing support for older java version would actually reduce the binaries as it will remove legacy code?

Source code: https://github.com/vincejv/qbittorrent-exporter

@zakkak
Copy link
Contributor

zakkak commented Feb 6, 2024

Reproducer

git clone https://github.com/vincejv/qbittorrent-exporter
cd qbittorrent-exporter
git worktree add ../qbittorrent-exporter-3.6
./gradlew build -Dquarkus.package.type=native
cd ../qbittorrent-exporter-3.6
sed -i 's/3\.7\.1/3.6.7/g' gradle.properties
./gradlew build -Dquarkus.package.type=native -D

Build output

3.6.7

Warning: The option '-H:ReflectionConfigurationResources=META-INF/native-image/io.micrometer/micrometer-core/reflect-config.json' is experimental and must be enabled via '-H:+UnlockExperimentalVMOptions' in the future.
Warning: The option '-H:ReflectionConfigurationResources=META-INF/native-image/io.netty/netty-transport/reflection-config.json' is experimental and must be enabled via '-H:+UnlockExperimentalVMOptions' in the future.
Warning: Please re-evaluate whether any experimental option is required, and either remove or unlock it. The build output lists all active experimental options, including where they come from and possible alternatives. If you think an experimental option should be considered as stable, please file an issue.
========================================================================================================================
GraalVM Native Image: Generating 'qbittorrent-exporter-unspecified-runner' (executable)...
========================================================================================================================
For detailed information and explanations on the build output, visit:
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
------------------------------------------------------------------------------------------------------------------------
[1/8] Initializing...                                                                                    (1.9s @ 0.26GB)
 Java version: 21.0.1+12-LTS, vendor version: Mandrel-23.1.1.0-Final
 Graal compiler: optimization level: 2, target machine: compatibility
 C compiler: gcc (redhat, x86_64, 13.2.1)
 Garbage collector: Serial GC (max heap size: 80% of RAM)
 3 user-specific feature(s):
 - com.oracle.svm.thirdparty.gson.GsonFeature
 - io.quarkus.runner.Feature: Auto-generated class by Quarkus from the existing extensions
 - io.quarkus.runtime.graal.DisableLoggingFeature: Disables INFO logging during the analysis phase
------------------------------------------------------------------------------------------------------------------------
 4 experimental option(s) unlocked:
 - '-H:+AllowFoldMethods' (origin(s): command line)
 - '-H:BuildOutputJSONFile' (origin(s): command line)
 - '-H:-UseServiceLoaderFeature' (origin(s): command line)
 - '-H:ReflectionConfigurationResources' (origin(s): 'META-INF/native-image/io.micrometer/micrometer-core/native-image.properties' in 'file:///home/zakkak/code/tmp/qbittorrent-exporter/build/qbittorrent-exporter-unspecified-native-image-source-jar/lib/io.micrometer.micrometer-core-1.11.5.jar', 'META-INF/native-image/io.netty/netty-transport/native-image.properties' in 'file:///home/zakkak/code/tmp/qbittorrent-exporter/build/qbittorrent-exporter-unspecified-native-image-source-jar/lib/io.netty.netty-transport-4.1.100.Final.jar')
------------------------------------------------------------------------------------------------------------------------
Build resources:
 - 17.47GB of memory (28.2% of 61.94GB system memory, determined at start)
 - 32 thread(s) (100.0% of 32 available processor(s), determined at start)
[2/8] Performing analysis...  [*****]                                                                    (6.4s @ 1.37GB)
   12,164 reachable types   (86.5% of   14,059 total)
   17,966 reachable fields  (59.3% of   30,287 total)
   60,966 reachable methods (57.9% of  105,323 total)
    3,762 types,   345 fields, and 3,934 methods registered for reflection
       61 types,    59 fields, and    55 methods registered for JNI access
        4 native libraries: dl, pthread, rt, z
[3/8] Building universe...                                                                               (1.6s @ 1.48GB)
[4/8] Parsing methods...      [*]                                                                        (0.8s @ 1.58GB)
[5/8] Inlining methods...     [***]                                                                      (0.4s @ 1.52GB)
[6/8] Compiling methods...    [**]                                                                       (4.3s @ 2.01GB)
[7/8] Layouting methods...    [**]                                                                       (2.3s @ 1.56GB)
[8/8] Creating image...       [**]                                                                       (2.7s @ 2.07GB)
  24.84MB (46.91%) for code area:    38,892 compilation units
  27.71MB (52.34%) for image heap:  319,952 objects and 83 resources
 406.17kB ( 0.75%) for other data
  52.95MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 origins of code area:                                Top 10 object types in image heap:
  12.69MB java.base                                            7.87MB byte[] for code metadata
   1.35MB java.net.http                                        4.17MB byte[] for java.lang.String
   1.35MB svm.jar (Native Image)                               2.98MB java.lang.Class
 997.90kB modified-io.vertx.vertx-core-4.4.6.jar               2.89MB java.lang.String
 981.48kB qbittorrent-exporter-unspecified-runner.jar          1.03MB byte[] for general heap data
 575.14kB org.yaml.snakeyaml-2.2.jar                           1.02MB com.oracle.svm.core.hub.DynamicHubCompanion
 498.01kB io.netty.netty-buffer-4.1.100.Final.jar            737.07kB byte[] for reflection metadata
 483.75kB io.netty.netty-common-4.1.100.Final.jar            571.48kB java.lang.String[]
 418.46kB io.netty.netty-codec-http-4.1.100.Final.jar        442.42kB c.o.svm.core.hub.DynamicHub$ReflectionMetadata
 380.80kB io.netty.netty-codec-http2-4.1.100.Final.jar       427.66kB java.lang.Object[]
   4.94MB for 85 more packages                                 5.62MB for 2994 more object types
------------------------------------------------------------------------------------------------------------------------
Recommendations:
 HEAP: Set max heap for improved and more predictable memory usage.
------------------------------------------------------------------------------------------------------------------------
                       1.5s (7.2% of total time) in 118 GCs | Peak RSS: 3.30GB | CPU load: 15.27
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
 /home/zakkak/code/tmp/qbittorrent-exporter/build/qbittorrent-exporter-unspecified-native-image-source-jar/qbittorrent-exporter-unspecified-runner (executable)
 /home/zakkak/code/tmp/qbittorrent-exporter/build/qbittorrent-exporter-unspecified-native-image-source-jar/qbittorrent-exporter-unspecified-runner-build-output-stats.json (build_info)
========================================================================================================================
Finished generating 'qbittorrent-exporter-unspecified-runner' in 20.8s.

3.7.1

Your application is setting the 'quarkus.locales' configuration key to include 'all'. All JDK locales, languages, currencies, etc. will be included, inflating the size of the executable.
========================================================================================================================
GraalVM Native Image: Generating 'qbittorrent-exporter-unspecified-runner' (executable)...
========================================================================================================================
For detailed information and explanations on the build output, visit:
https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/BuildOutput.md
------------------------------------------------------------------------------------------------------------------------
[1/8] Initializing...                                                                                    (1.9s @ 0.27GB)
 Java version: 21.0.1+12-LTS, vendor version: Mandrel-23.1.1.0-Final
 Graal compiler: optimization level: 2, target machine: compatibility
 C compiler: gcc (redhat, x86_64, 13.2.1)
 Garbage collector: Serial GC (max heap size: 80% of RAM)
 3 user-specific feature(s):
 - com.oracle.svm.thirdparty.gson.GsonFeature
 - io.quarkus.runner.Feature: Auto-generated class by Quarkus from the existing extensions
 - io.quarkus.runtime.graal.DisableLoggingFeature: Disables INFO logging during the analysis phase
------------------------------------------------------------------------------------------------------------------------
 4 experimental option(s) unlocked:
 - '-H:+IncludeAllLocales' (origin(s): command line)
 - '-H:+AllowFoldMethods' (origin(s): command line)
 - '-H:BuildOutputJSONFile' (origin(s): command line)
 - '-H:-UseServiceLoaderFeature' (origin(s): command line)
------------------------------------------------------------------------------------------------------------------------
Build resources:
 - 18.13GB of memory (29.3% of 61.94GB system memory, determined at start)
 - 32 thread(s) (100.0% of 32 available processor(s), determined at start)
[2/8] Performing analysis...  [*****]                                                                    (6.9s @ 1.50GB)
   13,994 reachable types   (88.1% of   15,893 total)
   18,053 reachable fields  (59.4% of   30,383 total)
   64,721 reachable methods (58.3% of  111,023 total)
   27,459 types,   345 fields, and 5,737 methods registered for reflection
       61 types,    59 fields, and    55 methods registered for JNI access
        4 native libraries: dl, pthread, rt, z
[3/8] Building universe...                                                                               (1.9s @ 1.62GB)
[4/8] Parsing methods...      [*]                                                                        (0.9s @ 1.91GB)
[5/8] Inlining methods...     [***]                                                                      (0.4s @ 1.28GB)
[6/8] Compiling methods...    [***]                                                                      (5.2s @ 1.91GB)
[7/8] Layouting methods...    [**]                                                                       (2.2s @ 1.45GB)
[8/8] Creating image...       [**]                                                                       (3.9s @ 2.21GB)
  25.38MB (34.34%) for code area:    40,769 compilation units
  48.12MB (65.12%) for image heap:  616,599 objects and 21,975 resources
 405.39kB ( 0.54%) for other data
  73.89MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 origins of code area:                                Top 10 object types in image heap:
  12.70MB java.base                                            9.45MB byte[] for java.lang.String
   1.71MB svm.jar (Native Image)                               8.00MB byte[] for code metadata
   1.35MB java.net.http                                        6.27MB byte[] for general heap data
   1.07MB qbittorrent-exporter-unspecified-runner.jar          5.58MB java.lang.String
1018.87kB modified-io.vertx.vertx-core-4.5.1.jar               3.49MB java.lang.Class
 575.06kB org.yaml.snakeyaml-2.2.jar                           2.24MB java.util.concurrent.ConcurrentHashMap$Node
 500.26kB io.netty.netty-buffer-4.1.106.Final.jar              2.09MB java.lang.Object[]
 490.29kB io.netty.netty-common-4.1.106.Final.jar              1.17MB com.oracle.svm.core.hub.DynamicHubCompanion
 420.50kB io.netty.netty-codec-http-4.1.106.Final.jar        860.11kB byte[] for reflection metadata
 382.09kB io.netty.netty-codec-http2-4.1.106.Final.jar       858.40kB org.graalvm.collections.Pair
   4.95MB for 84 more packages                                 8.14MB for 2999 more object types
------------------------------------------------------------------------------------------------------------------------
Recommendations:
 HEAP: Set max heap for improved and more predictable memory usage.
------------------------------------------------------------------------------------------------------------------------
                       2.2s (8.0% of total time) in 119 GCs | Peak RSS: 4.19GB | CPU load: 13.80
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
 /home/zakkak/code/tmp/qbittorrent-exporter/build/qbittorrent-exporter-unspecified-native-image-source-jar/qbittorrent-exporter-unspecified-runner (executable)
 /home/zakkak/code/tmp/qbittorrent-exporter/build/qbittorrent-exporter-unspecified-native-image-source-jar/qbittorrent-exporter-unspecified-runner-build-output-stats.json (build_info)
========================================================================================================================
Finished generating 'qbittorrent-exporter-unspecified-runner' in 27.8s.

Observations

  1. The binary size increases from 52.95MB to 73.89MB (3.6.7 vs 3.7.1)
  2. The build process in 3.7.1 mentions that:
Your application is setting the 'quarkus.locales' configuration key to include 'all'. All JDK locales, languages, currencies, etc. will be included, inflating the size of the executable.

which is known to increase the binary size.

Test

Changing quarkus.locales to en-US in src/main/resources/application.yaml and rebuilding results in a binary size equal to 53.14MB.

quarkus.locales=all support was added in 3.7 by #37106, prior to that setting the locales to all probably did nothing (it resulted in -H:IncludeLocales=all being passed to native-image which AFAIK will silently fail to detect the all locale and will thus do nothing.)

cc @Karm

@geoand
Copy link
Contributor

geoand commented Feb 7, 2024

The binary size increases from 52.95MB to 73.89MB (3.6.7 vs 3.7.1)

This is pretty bad if you ask me :)

@zakkak
Copy link
Contributor

zakkak commented Feb 7, 2024

The binary size increases from 52.95MB to 73.89MB (3.6.7 vs 3.7.1)

This is pretty bad if you ask me :)

@geoand this is not the default behavior though. This is only happening because the user is requesting all locales to be included and it's documented in https://github.com/Karm/quarkus/blob/c025a1b29c02b6a4b8b773d6041c9e50119147a0/docs/src/main/asciidoc/validation.adoc?plain=1#L400-L401

@geoand
Copy link
Contributor

geoand commented Feb 7, 2024

Oh, then I misread the investigation.

Thanks for the clarification!

@gsmet
Copy link
Member Author

gsmet commented Feb 7, 2024

I think we can close this issue, thanks @zakkak .

@gsmet gsmet closed this as completed Feb 7, 2024
@gsmet gsmet closed this as not planned Won't fix, can't repro, duplicate, stale Feb 7, 2024
@adamhenderson
Copy link

adamhenderson commented Feb 8, 2024

Is this actually closed? I'm still seeing large native binary sizes (52.7MB) when:

Quarkus 3.7.1

quarkus create
cd ./code-with-quarkus
quarkus build --native

application.properties:
quarkus.locales=en-US

[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Running Quarkus native-image plugin on GRAALVM 23.1 JDK 21.0.1+12-jvmci-23.1-b19

@gsmet
Copy link
Member Author

gsmet commented Feb 8, 2024

52 MB for a basic application looks like a lot.

@gsmet gsmet reopened this Feb 8, 2024
@gsmet
Copy link
Member Author

gsmet commented Feb 8, 2024

I think we need some further investigation.

50+ MB for a mostly empty application with only RESTEasy Reactive around is too much IMHO. I think we were a lot better before.

... digging ...

The same app created with Quarkus 2.16 is 40 MB. I also tested 2.13 with the same result.

I also did an experiment with Quarkus 1.13.3.Final and RESTEasy Classic (not RESTEasy Reactive at first as I wasn't sure it was already there) and I ended up with a 29 MB binary.
Switch to RESTEasy Reactive again with 1.13 and got 31 MB.

So we basically have our default REST app going like that:

Version Size in MB
1.13 with RESTEasy Classic 29
1.13 with RESTEasy Reactive 31
2.16 with RESTEasy Reactive 40
3.7 with RESTEasy Reactive 52

This is not looking good.

/cc @maxandersen @geoand @cescoffier @zakkak

@geoand
Copy link
Contributor

geoand commented Feb 8, 2024

Do you also have numbers for 3.2?

@zakkak
Copy link
Contributor

zakkak commented Feb 9, 2024

If I am not mistaken

2.16 is using GraalVM/Mandrel 22.3
3.2 is using GraalVM/Mandrel 23.0
3.7 is using GraalVM/Mandrel 23.1

In each of these updates we know that GraalVM/Mandrel increased the resulting binary size:

  1. https://quarkus.io/blog/mandrel-23-0-image-size-increase/
  2. https://quarkus.io/blog/mandrel-23-1-image-size-increase/

It's also worth keeping in mind that if the runtime performance is not impacted by these changes, while they bring some improvements in other aspects (like stability, debugging, etc.), the binary size shouldn't matter that much (at least in the common case).

@gsmet
Copy link
Member Author

gsmet commented Feb 9, 2024

Here are some new data points obtained with:

  • the Quarkus default app obtained by using quarkus create app (but making sure we use RESTEasy Reactive as we changed the default at some point, except for the first number which is basically the baseline I still had in mind...)
  • normalized with a container build (not sure why but I obtain 52 for 3.7 when building with a local GraalVM)
Version Size in MB
1.13 with RESTEasy Classic 29
1.13 with RESTEasy Reactive 31
2.0 with RESTEasy Reactive 39
2.1 with RESTEasy Reactive 40
2.16 with RESTEasy Reactive 40
3.0 with RESTEasy Reactive 42
3.1 with RESTEasy Reactive 42
3.2 with RESTEasy Reactive 44
3.3 with RESTEasy Reactive 44
3.4 with RESTEasy Reactive 44
3.5 with RESTEasy Reactive 47
3.6 with RESTEasy Reactive 47
3.7 with RESTEasy Reactive 48

@gsmet
Copy link
Member Author

gsmet commented Feb 9, 2024

It's also worth keeping in mind that if the runtime performance is not impacted by these changes, while they bring some improvements in other aspects (like stability, debugging, etc.), the binary size shouldn't matter that much (at least in the common case).

I dunno, I can't say I'm excited about a 65% increase in binary size for a very simple app.

@cescoffier
Copy link
Member

cescoffier commented Feb 9, 2024

@gsmet maybe you can add the graalVM version you used for each entry.

The reason is that without large changes (so 3.x) we have a bump of +2 Mb between 3.1 and 3.2 and +# Mb between 3.4 and 3.5...

@maxandersen
Copy link
Member

a picture shows it better ..
image

so its not a massive jump from 3.6 to 3.7 here at least? its more 3.4 to 3.5?

@maxandersen
Copy link
Member

similar issue in #38683 but more affecting time.

@zakkak
Copy link
Contributor

zakkak commented Feb 15, 2024

@gsmet maybe you can add the graalVM version you used for each entry.

Assuming that @gsmet used the default builder image of each version the table with the Mandrel/GraalVM version is:

Version Mandrel/GraalVM Version Size in MB
1.13 with RESTEasy Classic GraalVM CE 21.0 - Java 11 29
1.13 with RESTEasy Reactive GraalVM CE 21.0 - Java 11 31
2.0 with RESTEasy Reactive GraalVM CE 21.1 - Java 11 39
2.1 with RESTEasy Reactive GraalVM CE 21.0 - Java 11 40
2.16 with RESTEasy Reactive Mandrel 22.3 - Java 17 40
3.0 with RESTEasy Reactive Mandrel 22.3 - Java 17 42
3.1 with RESTEasy Reactive Mandrel 22.3 - Java 17 42
3.2 with RESTEasy Reactive Mandrel 23.0 - Java 17 44
3.3 with RESTEasy Reactive Mandrel 23.0 - Java 17 44
3.4 with RESTEasy Reactive Mandrel 23.0 - Java 17 44
3.5 with RESTEasy Reactive Mandrel 23.1 - Java 21 47
3.6 with RESTEasy Reactive Mandrel 23.1 - Java 21 47
3.7 with RESTEasy Reactive Mandrel 23.1 - Java 21 48

so its not a massive jump from 3.6 to 3.7 here at least? its more 3.4 to 3.5?

Correct, we should either move this discussion in a different issue or edit the title (since we are now discussing a different thing than the issue reported originally).

Now regarding the increases between 3.1 and 3.2, and later between 3.4 and 3.5 one can read https://quarkus.io/blog/mandrel-23-0-image-size-increase/ and https://quarkus.io/blog/mandrel-23-1-image-size-increase/ (at least for the part that Mandrel/GraalVM is responsible for).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants