Skip to content

Instantly share code, notes, and snippets.

@magicwerk
Last active August 19, 2022 12:27
Show Gist options
  • Save magicwerk/8c38f0468db821ae65312279d7e7e7a5 to your computer and use it in GitHub Desktop.
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
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
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