<div dir="ltr">Hello!<div><br></div><div>There's a simpler workaround. No need to declare a method, you can just add explicit type arguments to Arrays.asList call:</div><div><br></div><div> List<Map.Entry<String, String>> value =<br> Arrays.<Map.Entry<String, String>>asList(<br> Map.entry("key", "value"),<br> Map.entry("key", "value"),<br></div><div> ...</div><div> Map.entry("key", "value"),<br> Map.entry("key", "value")<br> );<br><br>Now, Arrays.asList becomes a standalone call, and instead of having a single huge inference session, one has many small sessions, and the compilation is fast again.</div><div><br></div><div>This problem is old and well-known. IntelliJ IDEA even has an inspection for it since ages. It's called "Javac quirks" [1], and we provide a quick-fix that adds explicit type arguments when the number of inference variables exceeds some threshold. Probably thanks to this, the OpenJDK project receives much fewer complaints about this problem than it could be :-) </div><div><br></div><div>With best regards,</div><div>Tagir Valeev</div><div><br></div><div>[1] <a href="https://www.jetbrains.com/help/inspectopedia/JavacQuirks.html">https://www.jetbrains.com/help/inspectopedia/JavacQuirks.html</a></div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Thu, Jan 16, 2025 at 11:29 PM Maurizio Cimadamore <<a href="mailto:maurizio.cimadamore@oracle.com">maurizio.cimadamore@oracle.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi,<br>
as Vicente mentioned, this is sadly a known issue. Each call to <br>
Map.entry adds two inference variables. These variables are all thrown <br>
into the same cauldron, as the inference engine wants to reason globally <br>
about them.<br>
<br>
We had overcome many similar performance potholes in the past, <br>
essentially by detecting and pruning away inference variables where e.g. <br>
X = Y (so we only keep one of them). In this case, unfortunately, <br>
there's no such equality to detect, and we can't really prune the set of <br>
inference variables. We have some ideas on how to maybe do it, but they <br>
are all fairly complex, and the risk of affecting correctness is rather <br>
high.<br>
<br>
In the meantime, there's maybe a workaround for you to try: let's remove <br>
the generic-ness of Map.Entry from the picture, by declaring a new method:<br>
<br>
Map.Entry<String, String> newStringEntry(String key, String val) { <br>
return Map.entry(key, val); }<br>
<br>
Now, call the new method when you build the big list. I believe this <br>
should bring compilation back to normal times.<br>
<br>
Hope this helps.<br>
<br>
Maurizio<br>
<br>
<br>
<br>
On 16/01/2025 12:33, Dawid Weiss wrote:<br>
><br>
> Hello,<br>
><br>
> I came across an interesting quirk of javac today. I was looking at <br>
> unusually long compilation times of a bunch of test files. One of <br>
> those files looks like this:<br>
><br>
> public class JavacBoom {<br>
> public void ahem() {<br>
> List<Map.Entry<String, String>> value =<br>
> Arrays.asList(<br>
> Map.entry("key", "value"),<br>
> Map.entry("key", "value"),<br>
> Map.entry("key", "value"),<br>
> ...<br>
> Map.entry("key", "value"),<br>
> );<br>
> }<br>
> }<br>
><br>
> Nothing special, except value is populated with many, many key-value <br>
> pairs. I think type inference must have some kind of exponential <br>
> algorithm in there because here is what it looks like when you <br>
> increase the number of keys:<br>
><br>
> count of Map.entry(k,v), real time [seconds] javac JavacBoom<br>
> 1, 0.4<br>
> 10, 0.43<br>
> 100, 1.08<br>
> 200, 3.4<br>
> 300, 10.8<br>
> 400, 22.9<br>
> 500, 50<br>
><br>
> So if you have 500 entries like the above, it'll take 50 seconds to <br>
> compile a single ~10k bytes java source input. Here is a github gist <br>
> with the source code containing 500 entries if you'd like to try locally:<br>
> <a href="https://gist.github.com/dweiss/93caf579a89f3dbbfac5c292048e42ad" rel="noreferrer" target="_blank">https://gist.github.com/dweiss/93caf579a89f3dbbfac5c292048e42ad</a><br>
><br>
> The compiler points here (roughly, single stack trace).<br>
><br>
> "main" #1 [1070081] prio=5 os_prio=0 cpu=1687.19ms elapsed=1.76s <br>
> tid=0x00007de1b802a3f0 nid=1070081 runnable [0x00007de1bcdfb000]<br>
> java.lang.Thread.State: RUNNABLE<br>
> at <br>
> com.sun.tools.javac.code.Type.equalsIgnoreMetadata(jdk.compiler@23.0.1/Type.java:563)<br>
> at <br>
> com.sun.tools.javac.code.Types$Subst.visitTypeVar(jdk.compiler@23.0.1/Types.java:3355)<br>
> at <br>
> com.sun.tools.javac.code.Types$Subst.visitTypeVar(jdk.compiler@23.0.1/Types.java:3331)<br>
> at <br>
> com.sun.tools.javac.code.Type$TypeVar.accept(jdk.compiler@23.0.1/Type.java:1709)<br>
> at <br>
> com.sun.tools.javac.code.Types$DefaultTypeVisitor.visit(jdk.compiler@23.0.1/Types.java:4920)<br>
> at com.sun.tools.javac.code.Type.map(jdk.compiler@23.0.1/Type.java:319)<br>
> at <br>
> com.sun.tools.javac.code.Types.subst(jdk.compiler@23.0.1/Types.java:3328)<br>
> at <br>
> com.sun.tools.javac.comp.InferenceContext.asUndetVar(jdk.compiler@23.0.1/InferenceContext.java:206)<br>
> at <br>
> com.sun.tools.javac.comp.Infer$GraphSolver$InferenceGraph.initNodes(jdk.compiler@23.0.1/Infer.java:1907)<br>
> at <br>
> com.sun.tools.javac.comp.Infer$GraphSolver$InferenceGraph.<init>(jdk.compiler@23.0.1/Infer.java:1852)<br>
> at <br>
> com.sun.tools.javac.comp.Infer$GraphSolver.solve(jdk.compiler@23.0.1/Infer.java:1654)<br>
> at <br>
> com.sun.tools.javac.comp.InferenceContext.solve(jdk.compiler@23.0.1/InferenceContext.java:487)<br>
> at <br>
> com.sun.tools.javac.comp.InferenceContext.solve(jdk.compiler@23.0.1/InferenceContext.java:494)<br>
> at <br>
> com.sun.tools.javac.comp.Infer.instantiateMethod(jdk.compiler@23.0.1/Infer.java:211)<br>
> at <br>
> com.sun.tools.javac.comp.Resolve.rawInstantiate(jdk.compiler@23.0.1/Resolve.java:619)<br>
> at <br>
> com.sun.tools.javac.comp.Resolve.selectBest(jdk.compiler@23.0.1/Resolve.java:1588)<br>
> at <br>
> com.sun.tools.javac.comp.Resolve.findMethodInScope(jdk.compiler@23.0.1/Resolve.java:1794)<br>
> at <br>
> com.sun.tools.javac.comp.Resolve.findMethod(jdk.compiler@23.0.1/Resolve.java:1865)<br>
> at <br>
> com.sun.tools.javac.comp.Resolve.findMethod(jdk.compiler@23.0.1/Resolve.java:1838)<br>
> at <br>
> com.sun.tools.javac.comp.Resolve$12.doLookup(jdk.compiler@23.0.1/Resolve.java:2770)<br>
> at <br>
> com.sun.tools.javac.comp.Resolve$BasicLookupHelper.lookup(jdk.compiler@23.0.1/Resolve.java:3485)<br>
> at <br>
> com.sun.tools.javac.comp.Resolve.lookupMethod(jdk.compiler@23.0.1/Resolve.java:3749)<br>
> at <br>
> com.sun.tools.javac.comp.Resolve.resolveQualifiedMethod(jdk.compiler@23.0.1/Resolve.java:2767)<br>
> at <br>
> com.sun.tools.javac.comp.Resolve.resolveQualifiedMethod(jdk.compiler@23.0.1/Resolve.java:2761)<br>
> at <br>
> com.sun.tools.javac.comp.Attr.selectSym(jdk.compiler@23.0.1/Attr.java:4546)<br>
> at <br>
> com.sun.tools.javac.comp.Attr.visitSelect(jdk.compiler@23.0.1/Attr.java:4433)<br>
> at <br>
> com.sun.tools.javac.tree.JCTree$JCFieldAccess.accept(jdk.compiler@23.0.1/JCTree.java:2570)<br>
> at <br>
> com.sun.tools.javac.comp.Attr.attribTree(jdk.compiler@23.0.1/Attr.java:674)<br>
> at <br>
> com.sun.tools.javac.comp.Attr.visitApply(jdk.compiler@23.0.1/Attr.java:2641)<br>
> at <br>
> com.sun.tools.javac.tree.JCTree$JCMethodInvocation.accept(jdk.compiler@23.0.1/JCTree.java:1857)<br>
> at <br>
> com.sun.tools.javac.comp.Attr.attribTree(jdk.compiler@23.0.1/Attr.java:674)<br>
> at <br>
> com.sun.tools.javac.comp.Attr.attribExpr(jdk.compiler@23.0.1/Attr.java:720)<br>
> at <br>
> com.sun.tools.javac.comp.Attr.visitVarDef(jdk.compiler@23.0.1/Attr.java:1318)<br>
><br>
> I don't have the karma to submit jira issues, but I think this can be <br>
> considered a showstopper?<br>
><br>
> Dawid<br>
</blockquote></div>