Last active
August 19, 2022 12:27
-
-
Save magicwerk/8c38f0468db821ae65312279d7e7e7a5 to your computer and use it in GitHub Desktop.
Example uses JMH to analyze performance of different implementations using different kind of call sites
This file contains 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
Benchmark Mode Cnt Score Error Units | |
CallSitePerformance.RunJmh.test0Simple thrpt 5 65395895.716 ± 3585776.026 ops/s | |
CallSitePerformance.RunJmh.test1Monomorphic thrpt 5 65828331.877 ± 1730449.138 ops/s | |
CallSitePerformance.RunJmh.test2Bimorphic thrpt 5 56950468.449 ± 2723720.552 ops/s | |
CallSitePerformance.RunJmh.test3Megamorphic3 thrpt 5 26138713.308 ± 467371.313 ops/s | |
CallSitePerformance.RunJmh.test4Megamorphic4 thrpt 5 27125605.963 ± 484781.834 ops/s | |
CallSitePerformance.RunJmh.test5MegamorphicEnum thrpt 5 59799107.787 ± 743002.166 ops/s | |
CallSitePerformance.RunJmh.test6MegamorphicInstance thrpt 5 59339463.418 ± 2235822.067 ops/s |
This file contains 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 org.magicwerk.brownies.collections.sandbox; | |
import org.magicwerk.brownies.collections.GapList; | |
import org.magicwerk.brownies.collections.IList; | |
import org.magicwerk.brownies.collections.sandbox.CallSitePerformance.RunJmh.EnumCalc.CalcMode; | |
import org.magicwerk.brownies.test.JmhRunner; | |
import org.magicwerk.brownies.test.JmhRunner.Options; | |
import org.openjdk.jmh.annotations.Benchmark; | |
import org.openjdk.jmh.annotations.Scope; | |
import org.openjdk.jmh.annotations.State; | |
import org.openjdk.jmh.runner.RunnerException; | |
/** Class {@link CallSitePerformance} uses JMH to analyze performance of different implementations using different kind of call sites. */ | |
public class CallSitePerformance { | |
public static void main(String[] args) throws RunnerException { | |
new CallSitePerformance().run(); | |
} | |
void run() { | |
Options opts = new Options().includeClass(RunJmh.class); | |
JmhRunner runner = new JmhRunner(); | |
runner.runJmh(opts); | |
} | |
public static class RunJmh { | |
/** Simple implementation of calculation without inheritance for maximum performance */ | |
static class SimpleCalc { | |
int calc(int n) { | |
return n + 1; | |
} | |
} | |
/** Calculation inheritance for examples with inheritance */ | |
static interface Calc { | |
int calc(int n); | |
} | |
/** Implementation 1 of Calc interface */ | |
static class Calc1 implements Calc { | |
@Override | |
public int calc(int n) { | |
return n + 1; | |
} | |
} | |
/** Implementation 2 of Calc interface */ | |
static class Calc2 implements Calc { | |
@Override | |
public int calc(int n) { | |
return n + 2; | |
} | |
} | |
/** Implementation 3 of Calc interface */ | |
static class Calc3 implements Calc { | |
@Override | |
public int calc(int n) { | |
return n + 3; | |
} | |
} | |
/** Implementation 4 of Calc interface */ | |
static class Calc4 implements Calc { | |
@Override | |
public int calc(int n) { | |
return n + 4; | |
} | |
} | |
/** Example how megamorphic calls can be optimized using an enumeration */ | |
static class EnumCalc implements Calc { | |
enum CalcMode { | |
Calc1, | |
Calc2, | |
Calc3, | |
Calc4 | |
} | |
CalcMode mode; | |
EnumCalc(CalcMode mode) { | |
this.mode = mode; | |
} | |
@Override | |
public int calc(int n) { | |
switch (mode) { | |
case Calc1: | |
return n + 1; | |
case Calc2: | |
return n + 2; | |
case Calc3: | |
return n + 3; | |
case Calc4: | |
return n + 4; | |
default: | |
throw new AssertionError(); | |
} | |
} | |
} | |
/** Example how megamorphic calls can be optimized by providing separated call sites for the different classes */ | |
static class InstanceCalc implements Calc { | |
Calc calc; | |
InstanceCalc(Calc calc) { | |
this.calc = calc; | |
} | |
@Override | |
public int calc(int n) { | |
if (calc instanceof Calc1) { | |
return calc.calc(n); | |
} else if (calc instanceof Calc2) { | |
return calc.calc(n); | |
} else if (calc instanceof Calc3) { | |
return calc.calc(n); | |
} else if (calc instanceof Calc4) { | |
return calc.calc(n); | |
} else { | |
throw new AssertionError(); | |
} | |
} | |
} | |
@State(Scope.Benchmark) | |
public static class BenchmarkState { | |
SimpleCalc simpleCalc = new SimpleCalc(); | |
IList<SimpleCalc> simple = GapList.create(simpleCalc, simpleCalc, simpleCalc, simpleCalc); | |
Calc calc1 = new Calc1(); | |
Calc calc2 = new Calc2(); | |
Calc calc3 = new Calc3(); | |
Calc calc4 = new Calc4(); | |
IList<Calc> monomorphic = GapList.create(calc1, calc1, calc1, calc1); | |
IList<Calc> bimorphic = GapList.create(calc1, calc2, calc1, calc2); | |
IList<Calc> megamorphic3 = GapList.create(calc1, calc2, calc3, calc1); | |
IList<Calc> megamorphic4 = GapList.create(calc1, calc2, calc3, calc4); | |
IList<Calc> enums = GapList.create(new EnumCalc(CalcMode.Calc1), new EnumCalc(CalcMode.Calc2), new EnumCalc(CalcMode.Calc3), | |
new EnumCalc(CalcMode.Calc4)); | |
IList<Calc> instances = GapList.create(new InstanceCalc(calc1), new InstanceCalc(calc2), new InstanceCalc(calc3), | |
new InstanceCalc(calc4)); | |
} | |
/** Benchmark of simple call without inheritance */ | |
@Benchmark | |
public Object test0Simple(BenchmarkState state) { | |
return testSimple(state.simple); | |
} | |
/** Benchmark of monomorphic calls */ | |
@Benchmark | |
public Object test1Monomorphic(BenchmarkState state) { | |
return test(state.monomorphic); | |
} | |
/** Benchmark of bimorphic calls */ | |
@Benchmark | |
public Object test2Bimorphic(BenchmarkState state) { | |
return test(state.bimorphic); | |
} | |
/** Benchmark of megamorphic calls with 3 targets */ | |
@Benchmark | |
public Object test3Megamorphic3(BenchmarkState state) { | |
return test(state.megamorphic3); | |
} | |
/** Benchmark of megamorphic calls with 4 targets */ | |
@Benchmark | |
public Object test4Megamorphic4(BenchmarkState state) { | |
return test(state.megamorphic4); | |
} | |
/** Benchmark of optimized megamorphic calls using an enumeration */ | |
@Benchmark | |
public Object test5MegamorphicEnum(BenchmarkState state) { | |
return test(state.enums); | |
} | |
/** Benchmark of optimized megamorphic calls using if statements and instanceof */ | |
@Benchmark | |
public Object test6MegamorphicInstance(BenchmarkState state) { | |
return test(state.enums); | |
} | |
int testSimple(IList<SimpleCalc> calcs) { | |
int n = 0; | |
for (int i = 0; i < calcs.size(); i++) { | |
SimpleCalc calc = calcs.get(i); | |
n += calc.calc(i); | |
} | |
return n; | |
} | |
int test(IList<Calc> calcs) { | |
int n = 0; | |
for (int i = 0; i < calcs.size(); i++) { | |
Calc calc = calcs.get(i); | |
n += calc.calc(i); | |
} | |
return n; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment