1+ package com .maxmind .db ;
2+
3+ import java .lang .invoke .MethodHandle ;
4+ import java .lang .invoke .MethodHandles ;
5+ import java .lang .reflect .Constructor ;
6+ import java .util .concurrent .ThreadLocalRandom ;
7+
8+ /**
9+ * Realistic benchmark comparing MethodHandle vs Reflection performance
10+ * using actual MaxMind DB deserialization patterns with caching.
11+ */
12+ public class RealisticPerformanceBenchmark {
13+
14+ public static class GeoRecord {
15+ private final String country ;
16+ private final String city ;
17+ private final Double latitude ;
18+ private final Double longitude ;
19+
20+ @ MaxMindDbConstructor
21+ public GeoRecord (
22+ @ MaxMindDbParameter (name = "country" ) String country ,
23+ @ MaxMindDbParameter (name = "city" ) String city ,
24+ @ MaxMindDbParameter (name = "latitude" ) Double latitude ,
25+ @ MaxMindDbParameter (name = "longitude" ) Double longitude ) {
26+ this .country = country ;
27+ this .city = city ;
28+ this .latitude = latitude ;
29+ this .longitude = longitude ;
30+ }
31+
32+ public String getCountry () { return country ; }
33+ public String getCity () { return city ; }
34+ public Double getLatitude () { return latitude ; }
35+ public Double getLongitude () { return longitude ; }
36+ }
37+
38+ public static void main (String [] args ) throws Throwable {
39+ int warmupIterations = 100_000 ;
40+ int benchmarkIterations = 1_000_000 ;
41+
42+ // Simulate realistic parameters with varied data
43+ Object [][] parameterSets = generateTestParameters (benchmarkIterations );
44+ Class <?>[] parameterTypes = {String .class , String .class , Double .class , Double .class };
45+
46+ // Cached constructor and MethodHandle (realistic usage)
47+ Constructor <GeoRecord > constructor = GeoRecord .class .getDeclaredConstructor (parameterTypes );
48+ MethodHandle methodHandle = MethodHandles .lookup ().unreflectConstructor (constructor );
49+
50+ // Warmup
51+ runReflectionBenchmark (constructor , parameterSets , warmupIterations );
52+ runMethodHandleBenchmark (methodHandle , parameterSets , warmupIterations );
53+
54+ System .out .println ("Realistic MaxMind DB deserialization benchmark:" );
55+ System .out .println ("Iterations: " + benchmarkIterations );
56+ System .out .println ("Pattern: Cached constructor/MethodHandle with varied parameters" );
57+ System .out .println ();
58+
59+ // Benchmark with cached instances (realistic usage)
60+ long reflectionTime = runReflectionBenchmark (constructor , parameterSets , benchmarkIterations );
61+ System .out .println ("Cached Reflection time: " + reflectionTime + "ms" );
62+
63+ long methodHandleTime = runMethodHandleBenchmark (methodHandle , parameterSets , benchmarkIterations );
64+ System .out .println ("Cached MethodHandle time: " + methodHandleTime + "ms" );
65+
66+ double improvement = ((double ) reflectionTime / methodHandleTime - 1 ) * 100 ;
67+ System .out .printf ("MethodHandle improvement: %.1f%%\n " , improvement );
68+ }
69+
70+ private static Object [][] generateTestParameters (int count ) {
71+ Object [][] parameters = new Object [count ][];
72+ String [] countries = {"US" , "CA" , "GB" , "DE" , "FR" , "JP" , "AU" , "BR" };
73+ String [] cities = {"New York" , "London" , "Tokyo" , "Paris" , "Sydney" , "Toronto" };
74+
75+ for (int i = 0 ; i < count ; i ++) {
76+ ThreadLocalRandom random = ThreadLocalRandom .current ();
77+ parameters [i ] = new Object []{
78+ countries [random .nextInt (countries .length )],
79+ cities [random .nextInt (cities .length )],
80+ random .nextDouble (-90 , 90 ), // latitude
81+ random .nextDouble (-180 , 180 ) // longitude
82+ };
83+ }
84+ return parameters ;
85+ }
86+
87+ private static long runReflectionBenchmark (Constructor <GeoRecord > constructor ,
88+ Object [][] parameterSets , int iterations ) throws Exception {
89+ long start = System .currentTimeMillis ();
90+ for (int i = 0 ; i < iterations ; i ++) {
91+ constructor .newInstance (parameterSets [i % parameterSets .length ]);
92+ }
93+ long end = System .currentTimeMillis ();
94+ return end - start ;
95+ }
96+
97+ private static long runMethodHandleBenchmark (MethodHandle methodHandle ,
98+ Object [][] parameterSets , int iterations ) throws Throwable {
99+ long start = System .currentTimeMillis ();
100+ for (int i = 0 ; i < iterations ; i ++) {
101+ methodHandle .invokeWithArguments (parameterSets [i % parameterSets .length ]);
102+ }
103+ long end = System .currentTimeMillis ();
104+ return end - start ;
105+ }
106+ }
0 commit comments