[ 
https://issues.apache.org/jira/browse/CAMEL-25963?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18130234#comment-18130234
 ] 

Aurélien Pupier edited comment on CAMEL-25963 at 10/22/26, 10:19 AM:
---------------------------------------------------------------------

crazy first investigation with IBM Bob:


h3. Investigation: why camel-util takes long to compile

_Analysis by IBM Bob (AI assistant) on behalf of [operator name]_

h4. Finding

The long compile time of {{camel-util}} comes from a single class: 
{{org.apache.camel.util.ArtifactUtils}}. It is not caused by the size of the 
module.

Measured with plain {{javac -proc:none}} (JDK 25):

||Compiled||Time||
|All 88 source files|~24 s|
|All files except {{MimeTypeHelper}}|~18.5 s|
|Each file on its own (all except {{ArtifactUtils}})|~0.7 s (mostly JVM 
start-up)|
|{{ArtifactUtils.java}} on its own|~21 s|

{{MimeTypeHelper}} (a switch with ~1000 cases) accounts for only ~5 s, so it is 
a minor factor.

h4. Root cause

{{ArtifactUtils}} contains four generated tables ({{COMPONENTS}} (~420 
entries), {{LANGUAGES}}, {{DATAFORMATS}}, {{BEANS}}) written as:

{code:java}
Map.ofEntries(
    Map.entry("a2a", "camel-a2a"),
    Map.entry("activemq", "camel-activemq"), ...
{code}

Both {{Map.ofEntries(Entry...)}} and {{Map.entry(K, V)}} are generic methods. 
javac has to infer the type of every nested {{Map.entry(...)}} together with 
the outer call, and this cost grows much faster than linearly with the number 
of arguments.

Isolated reproduction with synthetic keys:

||Entries||{{Map.ofEntries(Map.entry...)}}||Array of strings + loop||
|100|0.8 s|0.3 s|
|200|2.6 s|0.3 s|
|400|17.5 s|0.3 s|

Doubling from 200 to 400 entries multiplies the time by about 7. The array form 
stays flat. The tables grow with every new component, so the problem keeps 
getting worse.

h4. How to find such classes

# Compile each source file on its own with {{javac -proc:none -sourcepath 
src/main/java}} and sort by time. Anything far above the ~0.7 s JVM start-up 
baseline is a suspect.
# Bisect: compile everything except the suspect and see whether the total drops.
# Take {{jstack}} dumps of the javac/Maven process during a slow build. 
Repeated {{javac.comp.Infer}}, {{DeferredAttr}} or {{Resolve}} frames point to 
generic or lambda inference.
# Rule out the {{nullcheck}} profile (forked javac with Error Prone and 
NullAway) and annotation processors.

h4. Proposed fix

Change the generator so it no longer emits {{Map.ofEntries(Map.entry(...), 
...)}}. Emit a flat {{String[]}} of key/value pairs filled into a map in a 
{{static}} block instead. The generated blocks in {{ArtifactUtils}} ({{// 
COMPONENTS: START}} and similar) must not be edited by hand.

Other generated tables with the same pattern (for example {{SensitiveUtils}}) 
should be timed as well.

No code has been changed yet.


was (Author: apupier):
crazy first investigation with IBM Bob:

What I measured
I used plain javac with -proc:none, on JDK 25.

Compiled        Time
All 88 files    about 24 s
All files except MimeTypeHelper about 18.5 s
Each file on its own (all except ArtifactUtils) about 0.7 s, mostly JVM start-up
ArtifactUtils.java on its own   21.4 s
MimeTypeHelper has a switch with about 1000 cases. It costs only about 5 s of 
the total, so it is a minor factor.

Why ArtifactUtils is slow
The file has four huge tables, COMPONENTS, LANGUAGES, DATAFORMATS and BEANS. 
The first has about 420 entries. They are written like this:

Map.ofEntries(
    Map.entry("a2a", "camel-a2a"),
    Map.entry("activemq", "camel-activemq"), ...

Map.ofEntries(Entry<? extends K, ? extends V>...) and Map.entry(K, V) are both 
generic methods. Each Map.entry(...) argument is a generic method call that 
javac must type-infer together with the outer call. That inference work grows 
much faster than linearly with the number of arguments.

I reproduced this in isolation, with the same pattern and synthetic keys:

Entries Map.ofEntries(Map.entry…)       Array of strings plus a loop
100     0.8 s   0.3 s
200     2.6 s   0.3 s
400     17.5 s  0.3 s
Doubling the entries from 200 to 400 multiplies the time by about 7. The array 
form stays flat.

This has probably got worse as the generated tables grew with each new 
component. The git log shows commits that added to them, for example the wasm 
removal and the Citrus, OpenFeature and Kafka share components.

How to find slow classes yourself
Time each file separately. This is what found it:
cd core/camel-util
CP=$(find ~/.m2 -name 'slf4j-api-2*.jar' | head -1)
for f in $(find src/main/java -name '*.java'); do
  s=$(date +%s.%N)
  javac -proc:none -d /tmp/out -cp "$CP" -sourcepath src/main/java $f 
>/dev/null 2>&1
  echo "$(echo "$(date +%s.%N) - $s" | bc) $f"
done | sort -rn | head

Anything far above the roughly 0.7 s JVM start-up baseline is a suspect.
Bisect. Compile everything except the suspect, as I did with MimeTypeHelper. If 
the total barely changes, it is not the cause.
Take thread dumps during a slow build. Run jstack <pid> a few times on the 
javac or Maven process. The repeated com.sun.tools.javac.comp.Infer, 
DeferredAttr or Resolve frames point at generic or lambda inference. A profiler 
such as async-profiler gives the same information.
Check the profiles. The nullcheck profile in parent/pom.xml forks javac and 
adds Error Prone and NullAway, which slows every module. If you built with it, 
rule it out first. Annotation processors add time too, which is why I used 
-proc:none.
Fix
Replace the Map.ofEntries(Map.entry(...), ...) pattern with a form that needs 
no nested generic inference. For example, use a flat String[] of key/value 
pairs filled into a map in a static {} block. Because the table is generated, 
the change belongs in the generator, not in the file's COMPONENTS: START 
blocks, which say "do NOT edit". I have not yet found which build tool writes 
it.

The same pattern may appear in other generated tables, such as SensitiveUtils, 
which the Javadoc mentions as the model. They are worth a quick per-file timing 
check too.

I haven't changed any code. I can locate the generator and make the change if 
you want.




> Investigate compile and recompile time of camel util
> ----------------------------------------------------
>
>                 Key: CAMEL-25963
>                 URL: https://issues.apache.org/jira/browse/CAMEL-25963
>             Project: Camel
>          Issue Type: Task
>          Components: camel-util
>    Affects Versions: 4.24.0
>            Reporter: Aurélien Pupier
>            Priority: Minor
>
> camel-util compile/recompile is part of the critical path for parallel 
> builds. It is taking 2 times 39 seconds, it is strange that it takes so much 
> time as no Java code is generated (so recompile might be useless?) and also 
> there are only 87 classes, 39 seconds to compile them seems a lot
> https://develocity.apache.org/s/7mkwa3uanbtie/timeline?details=v3fqci3hrxtzy&end=1792654076711&page=2&sort=execution&start=1792653568985
> https://develocity.apache.org/s/7mkwa3uanbtie/timeline?details=wmvchkusjznpu&end=1792654076711&page=6&sort=execution&start=1792653568985
> seems the recompile is doe for absolutely all modules, i'm wondering if it 
> should notbe part only when there is some Java code generated. 
> https://issues.apache.org/jira/browse/CAMEL-14628



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to