Created
October 14, 2025 03:45
-
-
Save retronym/170214abc4da8039075c563ff3e25773 to your computer and use it in GitHub Desktop.
CakePattern +-UseSecondarySupersTable JVM performance
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package scala.tools.nsc; | |
import org.openjdk.jmh.annotations.*; | |
import java.lang.reflect.Proxy; | |
import java.util.*; | |
import java.util.concurrent.TimeUnit; | |
@BenchmarkMode(Mode.AverageTime) | |
@OutputTimeUnit(TimeUnit.NANOSECONDS) | |
@State(Scope.Thread) | |
@Warmup(iterations = 4, time = 2) | |
@Measurement(iterations = 4, time = 2) | |
@Fork(value = 3) | |
public class CakePattern { | |
// Approximation of the "Cake Pattern" | |
static class API { | |
interface Names { | |
class Name { | |
final Names outer; | |
Name(Names outer) { | |
this.outer = outer; | |
} | |
} | |
} | |
} | |
static class Impl { | |
interface Names extends API.Names { | |
class Name extends API.Names.Name { | |
// Scala compiler synthetically adds this outer parameter to this constructor and super call. | |
// As such, it knows that the checkcast below is always successful (modulo null). | |
Name(Names outer) { | |
super(outer); | |
} | |
Names outer() { | |
return (Names) this.outer; | |
} | |
} | |
} | |
} | |
// approximation of scala.tools.nsc.Global (core class in the Scala 2 compiler) | |
static Impl.Names global(int numInterfaces) { | |
Objects.checkIndex(numInterfaces - 1, INTERFACES.length); | |
// randomly select interfaces to cache-bust speculative compilation schemes of HotSpot. | |
List<Class<?>> shuffled = new ArrayList<>(Arrays.asList(INTERFACES)); | |
Collections.shuffle(shuffled, new Random()); | |
List<Class<?>> l1 = new ArrayList<>(shuffled.subList(0, numInterfaces)); | |
// last in the list for the worst case linear search | |
l1.add(Impl.Names.class); | |
Class<?>[] interfaces = l1.toArray(Class[]::new); | |
ClassLoader loader = CakePattern.class.getClassLoader(); | |
return (Impl.Names) Proxy.newProxyInstance(loader, interfaces, (proxy, _, args) -> null); | |
} | |
private Impl.Names names; | |
@Param("80") | |
public int numInterfaces; | |
@Setup(Level.Iteration) | |
public void setup() { | |
names = global(numInterfaces); | |
} | |
@Benchmark | |
public Object measure() { | |
return new Impl.Names.Name(names).outer(); | |
} | |
// @formatter:off | |
static final Class<?>[] INTERFACES = { | |
I1.class, I2.class, I3.class, I4.class, I5.class, I6.class, I7.class, I8.class, I9.class, I10.class, I11.class, I12.class, I13.class, I14.class, I15.class, I16.class, I17.class, I18.class, I19.class, I20.class, I21.class, I22.class, I23.class, I24.class, I25.class, I26.class, I27.class, I28.class, I29.class, I30.class, I31.class, I32.class, I33.class, I34.class, I35.class, I36.class, I37.class, I38.class, I39.class, I40.class, I41.class, I42.class, I43.class, I44.class, I45.class, I46.class, I47.class, I48.class, I49.class, I50.class, I51.class, I52.class, I53.class, I54.class, I55.class, I56.class, I57.class, I58.class, I59.class, I60.class, I61.class, I62.class, I63.class, I64.class, I65.class, I66.class, I67.class, I68.class, I69.class, I70.class, I71.class, I72.class, I73.class, I74.class, I75.class, I76.class, I77.class, I78.class, I79.class, I80.class, I81.class, I82.class, I83.class, I84.class, I85.class, I86.class, I87.class, I88.class, I89.class, I90.class, I91.class, I92.class, I93.class, I94.class, I95.class, I96.class, I97.class, I98.class, I99.class, I100.class, I101.class, I102.class, I103.class, I104.class, I105.class, I106.class, I107.class, I108.class, I109.class, I110.class, I111.class, I112.class, I113.class, I114.class, I115.class, I116.class, I117.class, I118.class, I119.class, I120.class, I121.class, I122.class, I123.class, I124.class, I125.class, I126.class, I127.class, I128.class, I129.class, I130.class, I131.class, I132.class, I133.class, I134.class, I135.class, I136.class, I137.class, I138.class, I139.class, I140.class, I141.class, I142.class, I143.class, I144.class, I145.class, I146.class, I147.class, I148.class, I149.class, I150.class, I151.class, I152.class, I153.class, I154.class, I155.class, I156.class, I157.class, I158.class, I159.class, I160.class, I161.class, I162.class, I163.class, I164.class, I165.class, I166.class, I167.class, I168.class, I169.class, I170.class, I171.class, I172.class, I173.class, I174.class, I175.class, I176.class, I177.class, I178.class, I179.class, I180.class, I181.class, I182.class, I183.class, I184.class, I185.class, I186.class, I187.class, I188.class, I189.class, I190.class, I191.class, I192.class, I193.class, I194.class, I195.class, I196.class, I197.class, I198.class, I199.class, I200.class, I201.class, I202.class, I203.class, I204.class, I205.class, I206.class, I207.class, I208.class, I209.class, I210.class, I211.class, I212.class, I213.class, I214.class, I215.class, I216.class, I217.class, I218.class, I219.class, I220.class, I221.class, I222.class, I223.class, I224.class, I225.class, I226.class, I227.class, I228.class, I229.class, I230.class, I231.class, I232.class, I233.class, I234.class, I235.class, I236.class, I237.class, I238.class, I239.class, I240.class, I241.class, I242.class, I243.class, I244.class, I245.class, I246.class, I247.class, I248.class, I249.class, I250.class, I251.class, I252.class, I253.class, I254.class, I255.class, I256.class, I257.class, I258.class, I259.class, I260.class, I261.class, I262.class, I263.class, I264.class, I265.class, I266.class, I267.class, I268.class, I269.class, I270.class, I271.class, I272.class, I273.class, I274.class, I275.class, I276.class, I277.class, I278.class, I279.class, I280.class, I281.class, I282.class, I283.class, I284.class, I285.class, I286.class, I287.class, I288.class, I289.class, I290.class, I291.class, I292.class, I293.class, I294.class, I295.class, I296.class, I297.class, I298.class, I299.class, I300.class | |
}; | |
interface I1 {}; interface I2 {}; interface I3 { }; interface I4 { }; interface I5 { }; interface I6 { }; interface I7 { }; interface I8 { }; interface I9 { }; interface I10 { }; interface I11 { }; interface I12 { }; interface I13 { }; interface I14 { }; interface I15 { }; interface I16 { }; interface I17 { }; interface I18 { }; interface I19 { }; interface I20 { }; interface I21 { }; interface I22 { }; interface I23 { }; interface I24 { }; interface I25 { }; interface I26 { }; interface I27 { }; interface I28 { }; interface I29 { }; interface I30 { }; interface I31 { }; interface I32 { }; interface I33 { }; interface I34 { }; interface I35 { }; interface I36 { }; interface I37 { }; interface I38 { }; interface I39 { }; interface I40 { }; interface I41 { }; interface I42 { }; interface I43 { }; interface I44 { }; interface I45 { }; interface I46 { }; interface I47 { }; interface I48 { }; interface I49 { }; interface I50 { }; interface I51 { }; interface I52 { }; interface I53 { }; interface I54 { }; interface I55 { }; interface I56 { }; interface I57 { }; interface I58 { }; interface I59 { }; interface I60 { }; interface I61 { }; interface I62 { }; interface I63 { }; interface I64 { }; interface I65 { }; interface I66 { }; interface I67 { }; interface I68 { }; interface I69 { }; interface I70 { }; interface I71 { }; interface I72 { }; interface I73 { }; interface I74 { }; interface I75 { }; interface I76 { }; interface I77 { }; interface I78 { }; interface I79 { }; interface I80 { }; interface I81 { }; interface I82 { }; interface I83 { }; interface I84 { }; interface I85 { }; interface I86 { }; interface I87 { }; interface I88 { }; interface I89 { }; interface I90 { }; interface I91 { }; interface I92 { }; interface I93 { }; interface I94 { }; interface I95 { }; interface I96 { }; interface I97 { }; interface I98 { }; interface I99 { }; interface I100 { }; interface I101 { }; interface I102 { }; interface I103 { }; interface I104 { }; interface I105 { }; interface I106 { }; interface I107 { }; interface I108 { }; interface I109 { }; interface I110 { }; interface I111 { }; interface I112 { }; interface I113 { }; interface I114 { }; interface I115 { }; interface I116 { }; interface I117 { }; interface I118 { }; interface I119 { }; interface I120 { }; interface I121 { }; interface I122 { }; interface I123 { }; interface I124 { }; interface I125 { }; interface I126 { }; interface I127 { }; interface I128 { }; interface I129 { }; interface I130 { }; interface I131 { }; interface I132 { }; interface I133 { }; interface I134 { }; interface I135 { }; interface I136 { }; interface I137 { }; interface I138 { }; interface I139 { }; interface I140 { }; interface I141 { }; interface I142 { }; interface I143 { }; interface I144 { }; interface I145 { }; interface I146 { }; interface I147 { }; interface I148 { }; interface I149 { }; interface I150 { }; interface I151 { }; interface I152 { }; interface I153 { }; interface I154 { }; interface I155 { }; interface I156 { }; interface I157 { }; interface I158 { }; interface I159 { }; interface I160 { }; interface I161 { }; interface I162 { }; interface I163 { }; interface I164 { }; interface I165 { }; interface I166 { }; interface I167 { }; interface I168 { }; interface I169 { }; interface I170 { }; interface I171 { }; interface I172 { }; interface I173 { }; interface I174 { }; interface I175 { }; interface I176 { }; interface I177 { }; interface I178 { }; interface I179 { }; interface I180 { }; interface I181 { }; interface I182 { }; interface I183 { }; interface I184 { }; interface I185 { }; interface I186 { }; interface I187 { }; interface I188 { }; interface I189 { }; interface I190 { }; interface I191 { }; interface I192 { }; interface I193 { }; interface I194 { }; interface I195 { }; interface I196 { }; interface I197 { }; interface I198 { }; interface I199 { }; interface I200 { }; interface I201 { }; interface I202 { }; interface I203 { }; interface I204 { }; interface I205 { }; interface I206 { }; interface I207 { }; interface I208 { }; interface I209 { }; interface I210 { }; interface I211 { }; interface I212 { }; interface I213 { }; interface I214 { }; interface I215 { }; interface I216 { }; interface I217 { }; interface I218 { }; interface I219 { }; interface I220 { }; interface I221 { }; interface I222 { }; interface I223 { }; interface I224 { }; interface I225 { }; interface I226 { }; interface I227 { }; interface I228 { }; interface I229 { }; interface I230 { }; interface I231 { }; interface I232 { }; interface I233 { }; interface I234 { }; interface I235 { }; interface I236 { }; interface I237 { }; interface I238 { }; interface I239 { }; interface I240 { }; interface I241 { }; interface I242 { }; interface I243 { }; interface I244 { }; interface I245 { }; interface I246 { }; interface I247 { }; interface I248 { }; interface I249 { }; interface I250 { }; interface I251 { }; interface I252 { }; interface I253 { }; interface I254 { }; interface I255 { }; interface I256 { }; interface I257 { }; interface I258 { }; interface I259 { }; interface I260 { }; interface I261 { }; interface I262 { }; interface I263 { }; interface I264 { }; interface I265 { }; interface I266 { }; interface I267 { }; interface I268 { }; interface I269 { }; interface I270 { }; interface I271 { }; interface I272 { }; interface I273 { }; interface I274 { }; interface I275 { }; interface I276 { }; interface I277 { }; interface I278 { }; interface I279 { }; interface I280 { }; interface I281 { }; interface I282 { }; interface I283 { }; interface I284 { }; interface I285 { }; interface I286 { }; interface I287 { }; interface I288 { }; interface I289 { }; interface I290 { }; interface I291 { }; interface I292 { }; interface I293 { }; interface I294 { }; interface I295 { }; interface I296 { }; interface I297 { }; interface I298 { }; interface I299 { }; interface I300 { }; | |
// @formatter:on | |
} | |
/* +UseSecondarySupersTable | |
[info] # JMH version: 1.37 | |
[info] # VM version: JDK 25, OpenJDK 64-Bit Server VM, 25+36-LTS | |
[info] # VM invoker: /Users/jz/.sdkman/candidates/java/25-zulu/zulu-25.jdk/Contents/Home/bin/java | |
[info] # VM options: -DscalaVersion=2.13.16 -DscalaRef=v2.13.16 -Dsbt.launcher=/opt/homebrew/Cellar/sbt/1.11.0/libexec/bin/sbt-launch.jar -XX:+UnlockDiagnosticVMOptions -XX:+UseSecondarySupersTable | |
[info] # Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable) | |
[info] # Warmup: 4 iterations, 2 s each | |
[info] # Measurement: 4 iterations, 2 s each | |
[info] # Timeout: 10 min per iteration | |
[info] # Threads: 1 thread, will synchronize iterations | |
[info] # Benchmark mode: Average time, time/op | |
[info] # Benchmark: scala.tools.nsc.CakePattern.measure | |
[info] # Parameters: (numInterfaces = 80) | |
[info] # Run progress: 0.00% complete, ETA 00:00:16 | |
[info] # Fork: 1 of 1 | |
... | |
[info] ns percent samples top | |
[info] ---------- ------- ------- --- | |
[info] 7640000000 94.91% 764 lookup_secondary_supers_table_slow_path | |
[info] 310000000 3.85% 31 scala.tools.nsc.CakePattern.measure | |
[info] 70000000 0.87% 7 scala.tools.nsc.CakePattern$Impl$Names$Name.<init> | |
[info] 10000000 0.12% 1 Matcher::match_tree | |
[info] 10000000 0.12% 1 __psynch_cvwait | |
[info] 10000000 0.12% 1 java.util.AbstractQueue.addAll | |
[info] Async profiler results: | |
[info] /Users/jz/code/compiler-benchmark/micro/scala.tools.nsc.CakePattern.measure-AverageTime-numInterfaces-80/summary-cpu.txt | |
[info] # Run complete. Total time: 00:00:19 | |
[info] REMEMBER: The numbers below are just data. ... | |
[info] Benchmark (numInterfaces) Mode Cnt Score Error Units | |
[info] CakePattern.measure 80 avgt 4 24.877 ± 7.768 ns/op | |
[info] CakePattern.measure:async 80 avgt NaN --- | |
[success] Total time: 25 s, completed 14 Oct 2025, 1:40:17 pm | |
*/ | |
/* -UseSecondarySupersTable | |
[info] # JMH version: 1.37 | |
[info] # VM version: JDK 25, OpenJDK 64-Bit Server VM, 25+36-LTS | |
[info] # VM invoker: /Users/jz/.sdkman/candidates/java/25-zulu/zulu-25.jdk/Contents/Home/bin/java | |
[info] # VM options: -DscalaVersion=2.13.16 -DscalaRef=v2.13.16 -Dsbt.launcher=/opt/homebrew/Cellar/sbt/1.11.0/libexec/bin/sbt-launch.jar -XX:+UnlockDiagnosticVMOptions -XX:-UseSecondarySupersTable | |
[info] # Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable) | |
[info] # Warmup: 4 iterations, 2 s each | |
[info] # Measurement: 4 iterations, 2 s each | |
[info] # Timeout: 10 min per iteration | |
[info] # Threads: 1 thread, will synchronize iterations | |
[info] # Benchmark mode: Average time, time/op | |
[info] # Benchmark: scala.tools.nsc.CakePattern.measure | |
[info] # Parameters: (numInterfaces = 80) | |
[info] # Run progress: 0.00% complete, ETA 00:00:16 | |
[info] # Fork: 1 of 1 | |
[info] # Preparing profilers: AsyncProfiler | |
[info] ns percent samples top | |
[info] ---------- ------- ------- --- | |
[info] 5050000000 62.66% 505 scala.tools.nsc.CakePattern.measure | |
[info] 2870000000 35.61% 287 scala.tools.nsc.jmh_generated.CakePattern_measure_jmhTest.measure_avgt_jmhStub | |
[info] 100000000 1.24% 10 scala.tools.nsc.CakePattern$Impl$Names$Name.<init> | |
[info] 30000000 0.37% 3 __psynch_cvwait | |
[info] 10000000 0.12% 1 PhaseChaitin::elide_copy | |
[info] Async profiler results: | |
[info] /Users/jz/code/compiler-benchmark/micro/scala.tools.nsc.CakePattern.measure-AverageTime-numInterfaces-80/summary-cpu.txt | |
[info] # Run complete. Total time: 00:00:16 | |
[info] REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on | |
[info] why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial | |
[info] experiments, perform baseline and negative tests that provide experimental control, make sure | |
[info] the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. | |
[info] Do not assume the numbers tell you what you want them to tell. | |
[info] NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise | |
[info] extra caution when trusting the results, look into the generated code to check the benchmark still | |
[info] works, and factor in a small probability of new VM bugs. Additionally, while comparisons between | |
[info] different JVMs are already problematic, the performance difference caused by different Blackhole | |
[info] modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons. | |
[info] Benchmark (numInterfaces) Mode Cnt Score Error Units | |
[info] CakePattern.measure 80 avgt 4 0.584 ± 0.198 ns/op | |
[info] CakePattern.measure:async 80 avgt NaN --- | |
[success] Total time: 18 s, completed 14 Oct 2025, 1:43:26 pm | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment