<div dir="ltr">Oh, that's an interesting feature! Too bad, it works only when debugging info is off, which is probably less than 1% of compiled Java code I've seen. I've created a small sample to amuse people:<div><br></div><div><div style=""><pre style="color:rgb(8,8,8);font-family:"JetBrains Mono",monospace"><span style="color:rgb(0,51,179)">import </span><span style="color:rgb(0,0,0)">java.util.function.IntSupplier</span>;<br><span style="color:rgb(0,51,179)">import </span><span style="color:rgb(0,0,0)">java.util.stream.IntStream</span>;<br><br><span style="color:rgb(0,51,179)">public class </span><span style="color:rgb(0,0,0)">Benchmark </span>{<br> <span style="color:rgb(0,51,179)">private int </span><span style="color:rgb(0,98,122)">compute</span>() {<br> <span style="color:rgb(0,0,0)">IntSupplier s1 </span>= () -> <span style="color:rgb(0,0,0)">IntStream</span>.<span style="font-style:italic">range</span>(<span style="color:rgb(23,80,235)">0</span>, <span style="color:rgb(23,80,235)">10000</span>).map(<span style="color:rgb(0,0,0)">v </span>-> <span style="color:rgb(23,80,235)">1</span>).sum();<br> <span style="color:rgb(0,0,0)">IntSupplier s2 </span>= () -> <span style="color:rgb(0,0,0)">IntStream</span>.<span style="font-style:italic">range</span>(<span style="color:rgb(23,80,235)">0</span>, <span style="color:rgb(23,80,235)">10000</span>).map(<span style="color:rgb(0,0,0)">v </span>-> <span style="color:rgb(23,80,235)">1</span>).sum();<br> <span style="color:rgb(0,0,0)">IntSupplier s3 </span>= () -> <span style="color:rgb(0,0,0)">IntStream</span>.<span style="font-style:italic">range</span>(<span style="color:rgb(23,80,235)">0</span>, <span style="color:rgb(23,80,235)">10000</span>).map(<span style="color:rgb(0,0,0)">v </span>-> <span style="color:rgb(23,80,235)">1</span>).sum();<br> <span style="color:rgb(0,51,179)">return </span><span style="color:rgb(0,0,0)">s1</span>.getAsInt() + <span style="color:rgb(0,0,0)">s2</span>.getAsInt() + <span style="color:rgb(0,0,0)">s3</span>.getAsInt();<br> }<br> <br> <span style="color:rgb(0,51,179)">private void </span><span style="color:rgb(0,98,122)">measure</span>() {<br> <span style="color:rgb(0,51,179)">int </span><span style="color:rgb(0,0,0)">res </span>= <span style="color:rgb(23,80,235)">0</span>;<br> <span style="color:rgb(140,140,140);font-style:italic">// Warmup<br></span><span style="color:rgb(140,140,140);font-style:italic"> </span><span style="color:rgb(0,51,179)">for </span>(<span style="color:rgb(0,51,179)">int </span><span style="color:rgb(0,0,0)">i </span>= <span style="color:rgb(23,80,235)">0</span>; <span style="color:rgb(0,0,0)">i </span>< <span style="color:rgb(23,80,235)">20000</span>; <span style="color:rgb(0,0,0)">i</span>++) {<br> <span style="color:rgb(0,0,0)">res </span>+= compute();<br> }<br><br> <span style="color:rgb(140,140,140);font-style:italic">// Measurement<br></span><span style="color:rgb(140,140,140);font-style:italic"> </span><span style="color:rgb(0,51,179)">long </span><span style="color:rgb(0,0,0)">start </span>= <span style="color:rgb(0,0,0)">System</span>.<span style="font-style:italic">currentTimeMillis</span>();<br> <span style="color:rgb(0,51,179)">for </span>(<span style="color:rgb(0,51,179)">int </span><span style="color:rgb(0,0,0)">i </span>= <span style="color:rgb(23,80,235)">0</span>; <span style="color:rgb(0,0,0)">i </span>< <span style="color:rgb(23,80,235)">20000</span>; <span style="color:rgb(0,0,0)">i</span>++) {<br> <span style="color:rgb(0,0,0)">res </span>+= compute();<br> }<br> <span style="color:rgb(0,51,179)">long </span><span style="color:rgb(0,0,0)">end </span>= <span style="color:rgb(0,0,0)">System</span>.<span style="font-style:italic">currentTimeMillis</span>();<br> <span style="color:rgb(0,0,0)">System</span>.<span style="color:rgb(135,16,148);font-style:italic">out</span>.println(<span style="color:rgb(0,0,0)">res</span>);<br> <span style="color:rgb(0,0,0)">System</span>.<span style="color:rgb(135,16,148);font-style:italic">out</span>.println(<span style="color:rgb(6,125,23)">"Duration: " </span>+ (<span style="color:rgb(0,0,0)">end </span>- <span style="color:rgb(0,0,0)">start</span>) + <span style="color:rgb(6,125,23)">"ms"</span>);<br> }<br> <br> <span style="color:rgb(0,51,179)">public static void </span><span style="color:rgb(0,98,122)">main</span>(<span style="color:rgb(0,0,0)">String</span>[] <span style="color:rgb(0,0,0)">args</span>) {<br> <span style="color:rgb(0,51,179)">new </span>Benchmark().measure();<br> }<br>}<br><br></pre><pre style="color:rgb(8,8,8);font-family:"JetBrains Mono",monospace"><span style="color:rgb(34,34,34);font-family:Arial,Helvetica,sans-serif;white-space:normal">With -g the measured time is about 1800ms, while without -g it's about 6ms. That's because when lambdas are deduplicated, type profile for map() method is clean, and the whole stream can be inlined, and even the loop could be likely replaced with a constant. When lambdas are not deduplicated, this is not the case: type profile is polluted, and the whole stream cannot be inlined. So, deduplication can actually improve the performance, at least in such extreme cases.</span></pre><pre style="color:rgb(8,8,8);font-family:"JetBrains Mono",monospace"><span style="color:rgb(34,34,34);font-family:Arial,Helvetica,sans-serif;white-space:normal">By the way, while experimenting with this sample, I've noticed that deduplication of this particular code is broken since Java 18 (the code performs slowly both with and without -g, and reading the bytecode confirms that no deduplication is done). Should I report, or probably there's no interest in supporting this feature?</span></pre><pre style="color:rgb(8,8,8);font-family:"JetBrains Mono",monospace"><span style="color:rgb(34,34,34);font-family:Arial,Helvetica,sans-serif;white-space:normal"><br></span></pre><pre style=""><font face="Arial, Helvetica, sans-serif"><span style="white-space:normal">With best regards,</span></font><br></pre><pre style=""><font face="Arial, Helvetica, sans-serif"><span style="white-space:normal">Tagir Valeev</span></font></pre><pre style="color:rgb(8,8,8);font-family:"JetBrains Mono",monospace"><br></pre><pre style=""></pre></div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Oct 23, 2023 at 1:40 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"><br>
On 21/10/2023 19:59, John Rose wrote:<br>
> All I’m saying here is that sharing is not always a win. Sometimes the <br>
> opposite move, splitting and customization, is the performance win, <br>
> even though you have two copies of the code in the end. <br>
<br>
Something related to this topic, if we revisit this: currently javac <br>
deduplicates lambda bodies that are identical [1], so that only one BSM <br>
entry is generated (and then reused).<br>
<br>
The logic for doing that is a bit finicky, and it seems to go against <br>
the principle you outline above John. So, as we shift towards constant <br>
lambdas, perhaps there's a chance to look at this again.<br>
<br>
Cheers<br>
Maurizio<br>
<br>
[1] - <a href="https://bugs.openjdk.org/browse/JDK-8200301" rel="noreferrer" target="_blank">https://bugs.openjdk.org/browse/JDK-8200301</a><br>
<br>
</blockquote></div>