Skip to content

Commit

Permalink
Overhaul benchmarking infrastructure (#63)
Browse files Browse the repository at this point in the history
Significantly reduces the overhead of benchmarking and provides more reliable real-world numbers
PaintNinja authored Jan 17, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 76ff4ca commit da2bba6
Showing 29 changed files with 1,617 additions and 743 deletions.
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ plugins {
id 'java-library'
id 'maven-publish'
id 'com.github.ben-manes.versions' version '0.50.0'
id 'org.gradlex.extra-java-module-info' version '1.4.2'
id 'org.gradlex.extra-java-module-info' version '1.8'
id 'net.minecraftforge.gradleutils' version '2.3.3'
id 'net.minecraftforge.licenser' version '1.0.1'
}
@@ -16,7 +16,7 @@ print("Version: $version")

java {
toolchain.languageVersion = JavaLanguageVersion.of(16)
modularity.inferModulePath.set(true)
modularity.inferModulePath = true
withSourcesJar()
}

@@ -101,4 +101,4 @@ allprojects {
// Tests are expensive to run all variants, so only run if asked to
if (!project.hasProperty('bulk_tests'))
ext.VALID_VMS = ['Adoptium': [17]]//, 18, 19, 20, 21] ]
}
}
24 changes: 12 additions & 12 deletions buildSrc/src/main/groovy/AggregateJmh.groovy
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ abstract class AggregateJmh extends DefaultTask {

int javaCount = 0
for (def file : this.inputs.files) {
if (file.equals(pastFile))
if (file == pastFile)
continue
def (javaName,javaVersion) = file.name.substring('jmh-'.length(), file.name.length() - 5).split('-')

@@ -61,8 +61,8 @@ abstract class AggregateJmh extends DefaultTask {
}
}

def onlyOneSuite = results.keySet().size() == 1 && results.values().iterator().next().keySet().size() == 1
def loadOld = collate.isPresent() && collate.get()
boolean onlyOneSuite = results.keySet().size() == 1 && results.values().iterator().next().keySet().size() == 1
boolean loadOld = collate.isPresent() && collate.get()

def markdown,csv = 'Something went fucky'
if (javaCount == 1) {
@@ -85,7 +85,7 @@ abstract class AggregateJmh extends DefaultTask {
outputCsv.asFile.get().text = csv
}

static def rsplit(def str, def del, int limit = -1) {
static def rsplit(def str, String del, int limit = -1) {
def lst = []
def x = 0, idx
def tmp = str
@@ -97,7 +97,7 @@ abstract class AggregateJmh extends DefaultTask {
return lst
}

static def mergePast(def to, def from) {
static void mergePast(def to, def from) {
def pkg = to.keySet().iterator().next()
if (!from.containsKey(pkg))
return
@@ -135,7 +135,7 @@ abstract class AggregateJmh extends DefaultTask {
*
* This allows for comparison between multiple environments and implementations
*/
static def formatCollated(def results, def resultsPast, def timeUnit) {
static def formatCollated(def results, def resultsPast, String timeUnit) {
def columns = [' ']
def rows = []
def base = [:]
@@ -213,7 +213,7 @@ abstract class AggregateJmh extends DefaultTask {
return markdown
}

static def buildCsv(def columns, def rows, def headers = true, def prefix = '') {
static String buildCsv(def columns, def rows, boolean headers = true, String prefix = '') {
columns = [] + columns
rows = [] + rows
for (int x = 0; x < rows.size(); x++)
@@ -226,7 +226,7 @@ abstract class AggregateJmh extends DefaultTask {
x--
}
}
def csv = ''
String csv = ''
if (headers)
csv += columns.join('\t') + '\n'
for (def row : rows)
@@ -252,9 +252,9 @@ abstract class AggregateJmh extends DefaultTask {
* | postStaticHundred | 5878.851 | 5325.055 | -9% |
* Relative - Percentage change vs first suite
*/
static def formatBulk(def results, def resultsPast, def timeUnit, def javas, def versions) {
def markdown = ''
def onlyOneSuite = results.keySet().size() == 1 && results.values().iterator().next().keySet().size() == 1
static def formatBulk(def results, def resultsPast, String timeUnit, def javas, def versions) {
String markdown = ''
boolean onlyOneSuite = results.keySet().size() == 1 && results.values().iterator().next().keySet().size() == 1
def columns = [' ']
def csvTable = [:]
versions.forEach { version -> columns += [version, 'Change', 'Relative'] }
@@ -330,7 +330,7 @@ abstract class AggregateJmh extends DefaultTask {
}
}
}
def csvData = ''
String csvData = ''
for (def line : csv)
csvData += line.join('\t') + '\n'

65 changes: 39 additions & 26 deletions eventbus-jmh/build.gradle
Original file line number Diff line number Diff line change
@@ -28,11 +28,11 @@ dependencies {
implementation(libs.securemodules)
implementation(libs.modlauncher)
implementation(libs.unsafe)
implementation(project(':eventbus-test-jar'))
implementation(projects.eventbusTestJar)

implementation('org.openjdk.jmh:jmh-core:1.37')
runtimeOnly('org.openjdk.jmh:jmh-generator-annprocess:1.37')
annotationProcessor('org.openjdk.jmh:jmh-generator-annprocess:1.37')
implementation libs.jmh.core
runtimeOnly libs.jmh.annotationProcessor
annotationProcessor libs.jmh.annotationProcessor
}

extraJavaModuleInfo {
@@ -57,15 +57,16 @@ tasks.register('archiveJfr', ArchiveJfr) {
output = file('build/jmh_profile_results/archive/')
}

tasks.register('jmh').configure {
dependsOn('aggregateJmh', 'archiveJfr')
tasks.register('jmh') {
dependsOn 'aggregateJmh'
}

VALID_VMS.each { javaVendor, javaVersions ->
javaVersions.each { int javaVersion ->
def output = file("build/jmh_results/jmh-${javaVendor}-${javaVersion}.json")
def outputJfr = file("build/jmh_profile_results/last_run/${javaVendor}-${javaVersion}/")
def task = tasks.register("jmh${javaVendor}${javaVersion}", JavaExec) {
((Map<String, List<Integer>>) ext.VALID_VMS).forEach { javaVendor, javaVersions ->
for (Integer javaVersion in javaVersions) {
var output = file("build/jmh_results/jmh-${javaVendor}-${javaVersion}.json")
var outputJfr = file("build/jmh_profile_results/last_run/${javaVendor}-${javaVersion}/")
outputJfr.mkdirs()
var task = tasks.register("jmh${javaVendor}${javaVersion}", JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainModule = 'net.minecraftforge.eventbus.jmh'
mainClass = 'net.minecraftforge.eventbus.benchmarks.Main'
@@ -81,32 +82,44 @@ VALID_VMS.each { javaVendor, javaVersions ->
'-tu', 'ns', // Time unit: [m, s, ms, us, ns]
'-f', '1', // Forks per benchmark
'-rf', 'json', // Results File Format
'-prof', 'stack', // Profiler: Simple and naive Java stack profiler
'-prof', "jfr:dir=${outputJfr}", // Profiler: Java Flight Recorder profiler

// Todo: Conditionally enable these when given a debug flag. Log a warning if they are enabled
// saying that they should only be used for identifying hotspots and to not trust the
// results of the benchmarks when they are enabled.
// '-prof', 'stack', // Profiler: Simple and naive Java stack profiler
// '-prof', "jfr:dir=${outputJfr}", // Profiler: Java Flight Recorder profiler

// Comment out the line below to enable BenchmarkClassLoader. This sets up a transformed environment
// with ModLauncher but *doesn't* call useModLauncher() in BusBuilder.
'-e', 'BenchmarkClassLoader.*',

'-v', 'EXTRA', // Verbosity [SILENT, NORMAL, EXTRA]
'-foe', 'true', // Fail on error
'-rff', output,
]
if (project.hasProperty('bench'))
args += project.property('bench')

doFirst {
if (outputJfr.exists())
outputJfr.deleteDir()
outputJfr.deleteDir()
if (!output.parentFile.exists())
output.parentFile.mkdirs()
output.parentFile.mkdirs()
if (output.exists())
output.delete()
}
javaLauncher.set(javaToolchains.launcherFor {
it.vendor.set(JvmVendorSpec."${javaVendor.toUpperCase(Locale.ROOT)}" as JvmVendorSpec)
it.languageVersion.set(JavaLanguageVersion.of(javaVersion))
it.implementation.set(JvmImplementation.VENDOR_SPECIFIC)
})
javaLauncher = javaToolchains.launcherFor {
vendor = JvmVendorSpec."${javaVendor.toUpperCase(Locale.ROOT)}" as JvmVendorSpec
languageVersion = JavaLanguageVersion.of(javaVersion)
implementation = JvmImplementation.VENDOR_SPECIFIC
}
}
tasks.named('aggregateJmh') {
dependsOn task
inputs.file output
}
tasks.named('archiveJfr') {
dependsOn task
inputs.dir outputJfr
}
aggregateJmh.dependsOn(task)
aggregateJmh.inputs.file(output)
archiveJfr.dependsOn(task)
archiveJfr.inputs.dir(outputJfr)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -5,39 +5,204 @@
package net.minecraftforge.eventbus.benchmarks;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.Blackhole;

@State(Scope.Benchmark)
public class BenchmarkCacheConcurrent extends BenchmarkBase {
@Setup
public void setup(BenchmarkParams params) {
// Note: It's okay to reuse the Consumers from BenchmarkNoLoader here because JMH resets the static fields between benchmarks.
public class BenchmarkCacheConcurrent {
public static void setup() {
System.setProperty("eb.cache_type", "concurrent");
setupNormal(params);
}
@TearDown
public void teardown() {
System.getProperties().remove("eb.cache_type");

public static void teardown() {
System.clearProperty("eb.cache_type");
}

public static class Posting {
@State(Scope.Benchmark)
public static class Mixed {
@Setup
public static void setup() {
BenchmarkCacheConcurrent.setup();
var preload = BenchmarkNoLoader.Posting.Mixed.POST_MIXED;
preload = BenchmarkNoLoader.Posting.Mixed.POST_MIXED_DOZEN;
preload = BenchmarkNoLoader.Posting.Mixed.POST_MIXED_HUNDRED;
}

@TearDown
public static void teardown() {
BenchmarkCacheConcurrent.teardown();
}

@Benchmark
public static void postMixed(Blackhole bh) {
BenchmarkNoLoader.Posting.Mixed.POST_MIXED.accept(bh);
}

@Benchmark
public static void postMixedDozen(Blackhole bh) {
BenchmarkNoLoader.Posting.Mixed.POST_MIXED_DOZEN.accept(bh);
}

@Benchmark
public static void postMixedHundred(Blackhole bh) {
BenchmarkNoLoader.Posting.Mixed.POST_MIXED_HUNDRED.accept(bh);
}
}

@State(Scope.Benchmark)
public static class Dynamic {
@Setup
public static void setup() {
BenchmarkCacheConcurrent.setup();
var preload = BenchmarkNoLoader.Posting.Dynamic.POST_DYNAMIC;
preload = BenchmarkNoLoader.Posting.Dynamic.POST_DYNAMIC_DOZEN;
preload = BenchmarkNoLoader.Posting.Dynamic.POST_DYNAMIC_HUNDRED;
}

@TearDown
public static void teardown() {
BenchmarkCacheConcurrent.teardown();
}

@Benchmark
public static void postDynamic(Blackhole bh) {
BenchmarkNoLoader.Posting.Dynamic.POST_DYNAMIC.accept(bh);
}

@Benchmark
public static void postDynamicDozen(Blackhole bh) {
BenchmarkNoLoader.Posting.Dynamic.POST_DYNAMIC_DOZEN.accept(bh);
}

@Benchmark
public static void postDynamicHundred(Blackhole bh) {
BenchmarkNoLoader.Posting.Dynamic.POST_DYNAMIC_HUNDRED.accept(bh);
}
}

@State(Scope.Benchmark)
public static class Lambda {
@Setup
public static void setup() {
BenchmarkCacheConcurrent.setup();
var preload = BenchmarkNoLoader.Posting.Lambda.POST_LAMBDA;
preload = BenchmarkNoLoader.Posting.Lambda.POST_LAMBDA_DOZEN;
preload = BenchmarkNoLoader.Posting.Lambda.POST_LAMBDA_HUNDRED;
}

@TearDown
public static void teardown() {
BenchmarkCacheConcurrent.teardown();
}

@Benchmark
public static void postLambda(Blackhole bh) {
BenchmarkNoLoader.Posting.Lambda.POST_LAMBDA.accept(bh);
}

@Benchmark
public static void postLambdaDozen(Blackhole bh) {
BenchmarkNoLoader.Posting.Lambda.POST_LAMBDA_DOZEN.accept(bh);
}

@Benchmark
public static void postLambdaHundred(Blackhole bh) {
BenchmarkNoLoader.Posting.Lambda.POST_LAMBDA_HUNDRED.accept(bh);
}
}

@State(Scope.Benchmark)
public static class Static {
@Setup
public static void setup() {
BenchmarkCacheConcurrent.setup();
var preload = BenchmarkNoLoader.Posting.Static.POST_STATIC;
preload = BenchmarkNoLoader.Posting.Static.POST_STATIC_DOZEN;
preload = BenchmarkNoLoader.Posting.Static.POST_STATIC_HUNDRED;
}

@TearDown
public static void teardown() {
BenchmarkCacheConcurrent.teardown();
}

@Benchmark
public static void postStatic(Blackhole bh) {
BenchmarkNoLoader.Posting.Static.POST_STATIC.accept(bh);
}

@Benchmark
public static void postStaticDozen(Blackhole bh) {
BenchmarkNoLoader.Posting.Static.POST_STATIC_DOZEN.accept(bh);
}

@Benchmark
public static void postStaticHundred(Blackhole bh) {
BenchmarkNoLoader.Posting.Static.POST_STATIC_HUNDRED.accept(bh);
}
}
}

public static class Registering {
@State(Scope.Benchmark)
public static class Dynamic {
@Setup
public static void setup() {
BenchmarkCacheConcurrent.setup();
var preload = BenchmarkNoLoader.Registering.Dynamic.REGISTER_DYNAMIC;
}

@TearDown
public static void teardown() {
BenchmarkCacheConcurrent.teardown();
}

@Benchmark
public static void registerDynamic() {
BenchmarkNoLoader.Registering.Dynamic.REGISTER_DYNAMIC.run();
}
}

@State(Scope.Benchmark)
public static class Lambda {
@Setup
public static void setup() {
BenchmarkCacheConcurrent.setup();
var preload = BenchmarkNoLoader.Registering.Lambda.REGISTER_LAMBDA;
}

@TearDown
public static void teardown() {
BenchmarkCacheConcurrent.teardown();
}

@Benchmark
public static void registerLambda() {
BenchmarkNoLoader.Registering.Lambda.REGISTER_LAMBDA.run();
}
}

@State(Scope.Benchmark)
public static class Static {
@Setup
public static void setup() {
BenchmarkCacheConcurrent.setup();
var preload = BenchmarkNoLoader.Registering.Static.REGISTER_STATIC;
}

@TearDown
public static void teardown() {
BenchmarkCacheConcurrent.teardown();
}

@Benchmark
public static void registerStatic() {
BenchmarkNoLoader.Registering.Static.REGISTER_STATIC.run();
}
}
}
@Setup(Level.Iteration) public void setupIteration() { super.setupIteration(); }

@Benchmark public void postDynamic() { run(); }
@Benchmark public void postDynamicDozen() { run(); }
@Benchmark public void postDynamicHundred() { run(); }
@Benchmark public void postLambda() { run(); }
@Benchmark public void postLambdaDozen() { run(); }
@Benchmark public void postLambdaHundred() { run(); }
@Benchmark public void postStatic() { run(); }
@Benchmark public void postStaticDozen() { run(); }
@Benchmark public void postStaticHundred() { run(); }
@Benchmark public void postMixed() { run(); }
@Benchmark public void postMixedDozen() { run(); }
@Benchmark public void postMixedHundred() { run(); }
@Benchmark public void registerDynamic() { run(); }
@Benchmark public void registerLambda() { run(); }
@Benchmark public void registerStatic() { run(); }
}
Original file line number Diff line number Diff line change
@@ -5,39 +5,203 @@
package net.minecraftforge.eventbus.benchmarks;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.Blackhole;

@State(Scope.Benchmark)
public class BenchmarkCacheCopy extends BenchmarkBase {
@Setup
public void setup(BenchmarkParams params) {
public class BenchmarkCacheCopy {
public static void setup() {
System.setProperty("eb.cache_type", "copy");
setupNormal(params);
}
@TearDown
public void teardown() {
System.getProperties().remove("eb.cache_type");

public static void teardown() {
System.clearProperty("eb.cache_type");
}

public static class Posting {
@State(Scope.Benchmark)
public static class Mixed {
@Setup
public static void setup() {
BenchmarkCacheCopy.setup();
var preload = BenchmarkNoLoader.Posting.Mixed.POST_MIXED;
preload = BenchmarkNoLoader.Posting.Mixed.POST_MIXED_DOZEN;
preload = BenchmarkNoLoader.Posting.Mixed.POST_MIXED_HUNDRED;
}

@TearDown
public static void teardown() {
BenchmarkCacheCopy.teardown();
}

@Benchmark
public static void postMixed(Blackhole bh) {
BenchmarkNoLoader.Posting.Mixed.POST_MIXED.accept(bh);
}

@Benchmark
public static void postMixedDozen(Blackhole bh) {
BenchmarkNoLoader.Posting.Mixed.POST_MIXED_DOZEN.accept(bh);
}

@Benchmark
public static void postMixedHundred(Blackhole bh) {
BenchmarkNoLoader.Posting.Mixed.POST_MIXED_HUNDRED.accept(bh);
}
}

@State(Scope.Benchmark)
public static class Dynamic {
@Setup
public static void setup() {
BenchmarkCacheCopy.setup();
var preload = BenchmarkNoLoader.Posting.Dynamic.POST_DYNAMIC;
preload = BenchmarkNoLoader.Posting.Dynamic.POST_DYNAMIC_DOZEN;
preload = BenchmarkNoLoader.Posting.Dynamic.POST_DYNAMIC_HUNDRED;
}

@TearDown
public static void teardown() {
BenchmarkCacheCopy.teardown();
}

@Benchmark
public static void postDynamic(Blackhole bh) {
BenchmarkNoLoader.Posting.Dynamic.POST_DYNAMIC.accept(bh);
}

@Benchmark
public static void postDynamicDozen(Blackhole bh) {
BenchmarkNoLoader.Posting.Dynamic.POST_DYNAMIC_DOZEN.accept(bh);
}

@Benchmark
public static void postDynamicHundred(Blackhole bh) {
BenchmarkNoLoader.Posting.Dynamic.POST_DYNAMIC_HUNDRED.accept(bh);
}
}

@State(Scope.Benchmark)
public static class Lambda {
@Setup
public static void setup() {
BenchmarkCacheCopy.setup();
var preload = BenchmarkNoLoader.Posting.Lambda.POST_LAMBDA;
preload = BenchmarkNoLoader.Posting.Lambda.POST_LAMBDA_DOZEN;
preload = BenchmarkNoLoader.Posting.Lambda.POST_LAMBDA_HUNDRED;
}

@TearDown
public static void teardown() {
BenchmarkCacheCopy.teardown();
}

@Benchmark
public static void postLambda(Blackhole bh) {
BenchmarkNoLoader.Posting.Lambda.POST_LAMBDA.accept(bh);
}

@Benchmark
public static void postLambdaDozen(Blackhole bh) {
BenchmarkNoLoader.Posting.Lambda.POST_LAMBDA_DOZEN.accept(bh);
}

@Benchmark
public static void postLambdaHundred(Blackhole bh) {
BenchmarkNoLoader.Posting.Lambda.POST_LAMBDA_HUNDRED.accept(bh);
}
}

@State(Scope.Benchmark)
public static class Static {
@Setup
public static void setup() {
BenchmarkCacheCopy.setup();
var preload = BenchmarkNoLoader.Posting.Static.POST_STATIC;
preload = BenchmarkNoLoader.Posting.Static.POST_STATIC_DOZEN;
preload = BenchmarkNoLoader.Posting.Static.POST_STATIC_HUNDRED;
}

@TearDown
public static void teardown() {
BenchmarkCacheCopy.teardown();
}

@Benchmark
public static void postStatic(Blackhole bh) {
BenchmarkNoLoader.Posting.Static.POST_STATIC.accept(bh);
}

@Benchmark
public static void postStaticDozen(Blackhole bh) {
BenchmarkNoLoader.Posting.Static.POST_STATIC_DOZEN.accept(bh);
}

@Benchmark
public static void postStaticHundred(Blackhole bh) {
BenchmarkNoLoader.Posting.Static.POST_STATIC_HUNDRED.accept(bh);
}
}
}

public static class Registering {
@State(Scope.Benchmark)
public static class Dynamic {
@Setup
public static void setup() {
BenchmarkCacheConcurrent.setup();
var preload = BenchmarkNoLoader.Registering.Dynamic.REGISTER_DYNAMIC;
}

@TearDown
public static void teardown() {
BenchmarkCacheConcurrent.teardown();
}

@Benchmark
public static void registerDynamic() {
BenchmarkNoLoader.Registering.Dynamic.REGISTER_DYNAMIC.run();
}
}

@State(Scope.Benchmark)
public static class Lambda {
@Setup
public static void setup() {
BenchmarkCacheConcurrent.setup();
var preload = BenchmarkNoLoader.Registering.Lambda.REGISTER_LAMBDA;
}

@TearDown
public static void teardown() {
BenchmarkCacheConcurrent.teardown();
}

@Benchmark
public static void registerLambda() {
BenchmarkNoLoader.Registering.Lambda.REGISTER_LAMBDA.run();
}
}

@State(Scope.Benchmark)
public static class Static {
@Setup
public static void setup() {
BenchmarkCacheConcurrent.setup();
var preload = BenchmarkNoLoader.Registering.Static.REGISTER_STATIC;
}

@TearDown
public static void teardown() {
BenchmarkCacheConcurrent.teardown();
}

@Benchmark
public static void registerStatic() {
BenchmarkNoLoader.Registering.Static.REGISTER_STATIC.run();
}
}
}
@Setup(Level.Iteration) public void setupIteration() { super.setupIteration(); }

@Benchmark public void postDynamic() { run(); }
@Benchmark public void postDynamicDozen() { run(); }
@Benchmark public void postDynamicHundred() { run(); }
@Benchmark public void postLambda() { run(); }
@Benchmark public void postLambdaDozen() { run(); }
@Benchmark public void postLambdaHundred() { run(); }
@Benchmark public void postStatic() { run(); }
@Benchmark public void postStaticDozen() { run(); }
@Benchmark public void postStaticHundred() { run(); }
@Benchmark public void postMixed() { run(); }
@Benchmark public void postMixedDozen() { run(); }
@Benchmark public void postMixedHundred() { run(); }
@Benchmark public void registerDynamic() { run(); }
@Benchmark public void registerLambda() { run(); }
@Benchmark public void registerStatic() { run(); }
}
Original file line number Diff line number Diff line change
@@ -4,31 +4,198 @@
*/
package net.minecraftforge.eventbus.benchmarks;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.BenchmarkParams;

@State(Scope.Benchmark)
public class BenchmarkClassLoader extends BenchmarkBase {
@Setup public void setup(BenchmarkParams params) { setupTransformed(params, false); }
@Setup(Level.Iteration) public void setupIteration() { super.setupIteration(); }

@Benchmark public void postDynamic() { run(); }
@Benchmark public void postDynamicDozen() { run(); }
@Benchmark public void postDynamicHundred() { run(); }
@Benchmark public void postLambda() { run(); }
@Benchmark public void postLambdaDozen() { run(); }
@Benchmark public void postLambdaHundred() { run(); }
@Benchmark public void postStatic() { run(); }
@Benchmark public void postStaticDozen() { run(); }
@Benchmark public void postStaticHundred() { run(); }
@Benchmark public void postMixed() { run(); }
@Benchmark public void postMixedDozen() { run(); }
@Benchmark public void postMixedHundred() { run(); }
@Benchmark public void registerDynamic() { run(); }
@Benchmark public void registerLambda() { run(); }
@Benchmark public void registerStatic() { run(); }
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;

import java.util.function.Consumer;

public class BenchmarkClassLoader {
public static class Posting {
@State(Scope.Benchmark)
public static class Mixed {
public static final Consumer<Blackhole> POST_MIXED;
public static final Consumer<Blackhole> POST_MIXED_DOZEN;
public static final Consumer<Blackhole> POST_MIXED_HUNDRED;

static {
BenchmarkUtils.setupTransformedEnvironment();
POST_MIXED = BenchmarkUtils.getPostingBenchmark("noLoaderMixed", 1);
POST_MIXED_DOZEN = BenchmarkUtils.getPostingBenchmark("noLoaderMixed", 12);
POST_MIXED_HUNDRED = BenchmarkUtils.getPostingBenchmark("noLoaderMixed", 100);
}

@Benchmark
public static void postMixed(Blackhole bh) {
POST_MIXED.accept(bh);
}

@Benchmark
public static void postMixedDozen(Blackhole bh) {
POST_MIXED_DOZEN.accept(bh);
}

@Benchmark
public static void postMixedHundred(Blackhole bh) {
POST_MIXED_HUNDRED.accept(bh);
}
}

@State(Scope.Benchmark)
public static class Dynamic {
public static final Consumer<Blackhole> POST_DYNAMIC;
public static final Consumer<Blackhole> POST_DYNAMIC_DOZEN;
public static final Consumer<Blackhole> POST_DYNAMIC_HUNDRED;

static {
BenchmarkUtils.setupTransformedEnvironment();
POST_DYNAMIC = BenchmarkUtils.getPostingBenchmark("noLoaderDynamic", 1);
POST_DYNAMIC_DOZEN = BenchmarkUtils.getPostingBenchmark("noLoaderDynamic", 12);
POST_DYNAMIC_HUNDRED = BenchmarkUtils.getPostingBenchmark("noLoaderDynamic", 100);
}

@Benchmark
public static void postDynamic(Blackhole bh) {
POST_DYNAMIC.accept(bh);
}

@Benchmark
public static void postDynamicDozen(Blackhole bh) {
POST_DYNAMIC_DOZEN.accept(bh);
}

@Benchmark
public static void postDynamicHundred(Blackhole bh) {
POST_DYNAMIC_HUNDRED.accept(bh);
}
}

@State(Scope.Benchmark)
public static class Lambda {
public static final Consumer<Blackhole> POST_LAMBDA;
public static final Consumer<Blackhole> POST_LAMBDA_DOZEN;
public static final Consumer<Blackhole> POST_LAMBDA_HUNDRED;

static {
BenchmarkUtils.setupTransformedEnvironment();
POST_LAMBDA = BenchmarkUtils.getPostingBenchmark("noLoaderLambda", 1);
POST_LAMBDA_DOZEN = BenchmarkUtils.getPostingBenchmark("noLoaderLambda", 12);
POST_LAMBDA_HUNDRED = BenchmarkUtils.getPostingBenchmark("noLoaderLambda", 100);
}

@Benchmark
public static void postLambda(Blackhole bh) {
POST_LAMBDA.accept(bh);
}

@Benchmark
public static void postLambdaDozen(Blackhole bh) {
POST_LAMBDA_DOZEN.accept(bh);
}

@Benchmark
public static void postLambdaHundred(Blackhole bh) {
POST_LAMBDA_HUNDRED.accept(bh);
}
}

@State(Scope.Benchmark)
public static class Static {
public static final Consumer<Blackhole> POST_STATIC;
public static final Consumer<Blackhole> POST_STATIC_DOZEN;
public static final Consumer<Blackhole> POST_STATIC_HUNDRED;

static {
BenchmarkUtils.setupTransformedEnvironment();
POST_STATIC = BenchmarkUtils.getPostingBenchmark("noLoaderStatic", 1);
POST_STATIC_DOZEN = BenchmarkUtils.getPostingBenchmark("noLoaderStatic", 12);
POST_STATIC_HUNDRED = BenchmarkUtils.getPostingBenchmark("noLoaderStatic", 100);
}

@Benchmark
public static void postStatic(Blackhole bh) {
POST_STATIC.accept(bh);
}

@Benchmark
public static void postStaticDozen(Blackhole bh) {
POST_STATIC_DOZEN.accept(bh);
}

@Benchmark
public static void postStaticHundred(Blackhole bh) {
POST_STATIC_HUNDRED.accept(bh);
}
}
}

public static class Registering {
@State(Scope.Benchmark)
public static class Dynamic {
public static final Runnable SETUP_REGISTER_DYNAMIC;
public static final Runnable REGISTER_DYNAMIC;

static {
BenchmarkUtils.setupTransformedEnvironment();
Runnable[] results = BenchmarkUtils.getRegistrationBenchmark("noLoaderDynamic");
SETUP_REGISTER_DYNAMIC = results[0];
REGISTER_DYNAMIC = results[1];
}

@Setup(Level.Iteration)
public void setupIteration() {
SETUP_REGISTER_DYNAMIC.run();
}

@Benchmark
public void registerDynamic() {
REGISTER_DYNAMIC.run();
}
}

@State(Scope.Benchmark)
public static class Lambda {
public static final Runnable SETUP_REGISTER_LAMBDA;
public static final Runnable REGISTER_LAMBDA;

static {
BenchmarkUtils.setupTransformedEnvironment();
Runnable[] results = BenchmarkUtils.getRegistrationBenchmark("noLoaderLambda");
SETUP_REGISTER_LAMBDA = results[0];
REGISTER_LAMBDA = results[1];
}

@Setup(Level.Iteration)
public void setupIteration() {
SETUP_REGISTER_LAMBDA.run();
}

@Benchmark
public void registerLambda() {
REGISTER_LAMBDA.run();
}
}

@State(Scope.Benchmark)
public static class Static {
public static final Runnable SETUP_REGISTER_STATIC;
public static final Runnable REGISTER_STATIC;

static {
BenchmarkUtils.setupTransformedEnvironment();
Runnable[] results = BenchmarkUtils.getRegistrationBenchmark("noLoaderStatic");
SETUP_REGISTER_STATIC = results[0];
REGISTER_STATIC = results[1];
}

@Setup(Level.Iteration)
public void setupIteration() {
SETUP_REGISTER_STATIC.run();
}

@Benchmark
public void registerStatic() {
REGISTER_STATIC.run();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -4,31 +4,198 @@
*/
package net.minecraftforge.eventbus.benchmarks;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.BenchmarkParams;

@State(Scope.Benchmark)
public class BenchmarkModLauncher extends BenchmarkBase {
@Setup public void setup(BenchmarkParams params) { setupTransformed(params, true); }
@Setup(Level.Iteration) public void setupIteration() { super.setupIteration(); }

@Benchmark public void postDynamic() { run(); }
@Benchmark public void postDynamicDozen() { run(); }
@Benchmark public void postDynamicHundred() { run(); }
@Benchmark public void postLambda() { run(); }
@Benchmark public void postLambdaDozen() { run(); }
@Benchmark public void postLambdaHundred() { run(); }
@Benchmark public void postStatic() { run(); }
@Benchmark public void postStaticDozen() { run(); }
@Benchmark public void postStaticHundred() { run(); }
@Benchmark public void postMixed() { run(); }
@Benchmark public void postMixedDozen() { run(); }
@Benchmark public void postMixedHundred() { run(); }
@Benchmark public void registerDynamic() { run(); }
@Benchmark public void registerLambda() { run(); }
@Benchmark public void registerStatic() { run(); }
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;

import java.util.function.Consumer;

public class BenchmarkModLauncher {
public static class Posting {
@State(Scope.Benchmark)
public static class Mixed {
private static final Consumer<Blackhole> POST_MIXED;
private static final Consumer<Blackhole> POST_MIXED_DOZEN;
private static final Consumer<Blackhole> POST_MIXED_HUNDRED;

static {
BenchmarkUtils.setupTransformedEnvironment();
POST_MIXED = BenchmarkUtils.getPostingBenchmark("modLauncherMixed", 1);
POST_MIXED_DOZEN = BenchmarkUtils.getPostingBenchmark("modLauncherMixed", 12);
POST_MIXED_HUNDRED = BenchmarkUtils.getPostingBenchmark("modLauncherMixed", 100);
}

@Benchmark
public static void postMixed(Blackhole bh) {
POST_MIXED.accept(bh);
}

@Benchmark
public static void postMixedDozen(Blackhole bh) {
POST_MIXED_DOZEN.accept(bh);
}

@Benchmark
public static void postMixedHundred(Blackhole bh) {
POST_MIXED_HUNDRED.accept(bh);
}
}

@State(Scope.Benchmark)
public static class Dynamic {
private static final Consumer<Blackhole> POST_DYNAMIC;
private static final Consumer<Blackhole> POST_DYNAMIC_DOZEN;
private static final Consumer<Blackhole> POST_DYNAMIC_HUNDRED;

static {
BenchmarkUtils.setupTransformedEnvironment();
POST_DYNAMIC = BenchmarkUtils.getPostingBenchmark("modLauncherDynamic", 1);
POST_DYNAMIC_DOZEN = BenchmarkUtils.getPostingBenchmark("modLauncherDynamic", 12);
POST_DYNAMIC_HUNDRED = BenchmarkUtils.getPostingBenchmark("modLauncherDynamic", 100);
}

@Benchmark
public static void postDynamic(Blackhole bh) {
POST_DYNAMIC.accept(bh);
}

@Benchmark
public static void postDynamicDozen(Blackhole bh) {
POST_DYNAMIC_DOZEN.accept(bh);
}

@Benchmark
public static void postDynamicHundred(Blackhole bh) {
POST_DYNAMIC_HUNDRED.accept(bh);
}
}

@State(Scope.Benchmark)
public static class Lambda {
private static final Consumer<Blackhole> POST_LAMBDA;
private static final Consumer<Blackhole> POST_LAMBDA_DOZEN;
private static final Consumer<Blackhole> POST_LAMBDA_HUNDRED;

static {
BenchmarkUtils.setupTransformedEnvironment();
POST_LAMBDA = BenchmarkUtils.getPostingBenchmark("modLauncherLambda", 1);
POST_LAMBDA_DOZEN = BenchmarkUtils.getPostingBenchmark("modLauncherLambda", 12);
POST_LAMBDA_HUNDRED = BenchmarkUtils.getPostingBenchmark("modLauncherLambda", 100);
}

@Benchmark
public static void postLambda(Blackhole bh) {
POST_LAMBDA.accept(bh);
}

@Benchmark
public static void postLambdaDozen(Blackhole bh) {
POST_LAMBDA_DOZEN.accept(bh);
}

@Benchmark
public static void postLambdaHundred(Blackhole bh) {
POST_LAMBDA_HUNDRED.accept(bh);
}
}

@State(Scope.Benchmark)
public static class Static {
private static final Consumer<Blackhole> POST_STATIC;
private static final Consumer<Blackhole> POST_STATIC_DOZEN;
private static final Consumer<Blackhole> POST_STATIC_HUNDRED;

static {
BenchmarkUtils.setupTransformedEnvironment();
POST_STATIC = BenchmarkUtils.getPostingBenchmark("modLauncherStatic", 1);
POST_STATIC_DOZEN = BenchmarkUtils.getPostingBenchmark("modLauncherStatic", 12);
POST_STATIC_HUNDRED = BenchmarkUtils.getPostingBenchmark("modLauncherStatic", 100);
}

@Benchmark
public static void postStatic(Blackhole bh) {
POST_STATIC.accept(bh);
}

@Benchmark
public static void postStaticDozen(Blackhole bh) {
POST_STATIC_DOZEN.accept(bh);
}

@Benchmark
public static void postStaticHundred(Blackhole bh) {
POST_STATIC_HUNDRED.accept(bh);
}
}
}

public static class Registering {
@State(Scope.Benchmark)
public static class Dynamic {
private static final Runnable SETUP_REGISTER_DYNAMIC;
private static final Runnable REGISTER_DYNAMIC;

static {
BenchmarkUtils.setupTransformedEnvironment();
Runnable[] results = BenchmarkUtils.getRegistrationBenchmark("modLauncherDynamic");
SETUP_REGISTER_DYNAMIC = results[0];
REGISTER_DYNAMIC = results[1];
}

@Setup(Level.Iteration)
public void setupIteration() {
SETUP_REGISTER_DYNAMIC.run();
}

@Benchmark
public void registerDynamic() {
REGISTER_DYNAMIC.run();
}
}

@State(Scope.Benchmark)
public static class Lambda {
private static final Runnable SETUP_REGISTER_LAMBDA;
private static final Runnable REGISTER_LAMBDA;

static {
BenchmarkUtils.setupTransformedEnvironment();
Runnable[] results = BenchmarkUtils.getRegistrationBenchmark("modLauncherLambda");
SETUP_REGISTER_LAMBDA = results[0];
REGISTER_LAMBDA = results[1];
}

@Setup(Level.Iteration)
public void setupIteration() {
SETUP_REGISTER_LAMBDA.run();
}

@Benchmark
public void registerLambda() {
REGISTER_LAMBDA.run();
}
}

@State(Scope.Benchmark)
public static class Static {
private static final Runnable SETUP_REGISTER_STATIC;
private static final Runnable REGISTER_STATIC;

static {
BenchmarkUtils.setupTransformedEnvironment();
Runnable[] results = BenchmarkUtils.getRegistrationBenchmark("modLauncherStatic");
SETUP_REGISTER_STATIC = results[0];
REGISTER_STATIC = results[1];
}

@Setup(Level.Iteration)
public void setupIteration() {
SETUP_REGISTER_STATIC.run();
}

@Benchmark
public void registerStatic() {
REGISTER_STATIC.run();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -4,31 +4,198 @@
*/
package net.minecraftforge.eventbus.benchmarks;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.BenchmarkParams;

@State(Scope.Benchmark)
public class BenchmarkNoLoader extends BenchmarkBase {
@Setup public void setup(BenchmarkParams params) { setupNormal(params); }
@Setup(Level.Iteration) public void setupIteration() { super.setupIteration(); }

@Benchmark public void postDynamic() { run(); }
@Benchmark public void postDynamicDozen() { run(); }
@Benchmark public void postDynamicHundred() { run(); }
@Benchmark public void postLambda() { run(); }
@Benchmark public void postLambdaDozen() { run(); }
@Benchmark public void postLambdaHundred() { run(); }
@Benchmark public void postStatic() { run(); }
@Benchmark public void postStaticDozen() { run(); }
@Benchmark public void postStaticHundred() { run(); }
@Benchmark public void postMixed() { run(); }
@Benchmark public void postMixedDozen() { run(); }
@Benchmark public void postMixedHundred() { run(); }
@Benchmark public void registerDynamic() { run(); }
@Benchmark public void registerLambda() { run(); }
@Benchmark public void registerStatic() { run(); }
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;

import java.util.function.Consumer;

public class BenchmarkNoLoader {
public static class Posting {
@State(Scope.Benchmark)
public static class Mixed {
public static final Consumer<Blackhole> POST_MIXED;
public static final Consumer<Blackhole> POST_MIXED_DOZEN;
public static final Consumer<Blackhole> POST_MIXED_HUNDRED;

static {
BenchmarkUtils.setupNormalEnvironment();
POST_MIXED = BenchmarkUtils.getPostingBenchmark("noLoaderMixed", 1);
POST_MIXED_DOZEN = BenchmarkUtils.getPostingBenchmark("noLoaderMixed", 12);
POST_MIXED_HUNDRED = BenchmarkUtils.getPostingBenchmark("noLoaderMixed", 100);
}

@Benchmark
public static void postMixed(Blackhole bh) {
POST_MIXED.accept(bh);
}

@Benchmark
public static void postMixedDozen(Blackhole bh) {
POST_MIXED_DOZEN.accept(bh);
}

@Benchmark
public static void postMixedHundred(Blackhole bh) {
POST_MIXED_HUNDRED.accept(bh);
}
}

@State(Scope.Benchmark)
public static class Dynamic {
public static final Consumer<Blackhole> POST_DYNAMIC;
public static final Consumer<Blackhole> POST_DYNAMIC_DOZEN;
public static final Consumer<Blackhole> POST_DYNAMIC_HUNDRED;

static {
BenchmarkUtils.setupNormalEnvironment();
POST_DYNAMIC = BenchmarkUtils.getPostingBenchmark("noLoaderDynamic", 1);
POST_DYNAMIC_DOZEN = BenchmarkUtils.getPostingBenchmark("noLoaderDynamic", 12);
POST_DYNAMIC_HUNDRED = BenchmarkUtils.getPostingBenchmark("noLoaderDynamic", 100);
}

@Benchmark
public static void postDynamic(Blackhole bh) {
POST_DYNAMIC.accept(bh);
}

@Benchmark
public static void postDynamicDozen(Blackhole bh) {
POST_DYNAMIC_DOZEN.accept(bh);
}

@Benchmark
public static void postDynamicHundred(Blackhole bh) {
POST_DYNAMIC_HUNDRED.accept(bh);
}
}

@State(Scope.Benchmark)
public static class Lambda {
public static final Consumer<Blackhole> POST_LAMBDA;
public static final Consumer<Blackhole> POST_LAMBDA_DOZEN;
public static final Consumer<Blackhole> POST_LAMBDA_HUNDRED;

static {
BenchmarkUtils.setupNormalEnvironment();
POST_LAMBDA = BenchmarkUtils.getPostingBenchmark("noLoaderLambda", 1);
POST_LAMBDA_DOZEN = BenchmarkUtils.getPostingBenchmark("noLoaderLambda", 12);
POST_LAMBDA_HUNDRED = BenchmarkUtils.getPostingBenchmark("noLoaderLambda", 100);
}

@Benchmark
public static void postLambda(Blackhole bh) {
POST_LAMBDA.accept(bh);
}

@Benchmark
public static void postLambdaDozen(Blackhole bh) {
POST_LAMBDA_DOZEN.accept(bh);
}

@Benchmark
public static void postLambdaHundred(Blackhole bh) {
POST_LAMBDA_HUNDRED.accept(bh);
}
}

@State(Scope.Benchmark)
public static class Static {
public static final Consumer<Blackhole> POST_STATIC;
public static final Consumer<Blackhole> POST_STATIC_DOZEN;
public static final Consumer<Blackhole> POST_STATIC_HUNDRED;

static {
BenchmarkUtils.setupNormalEnvironment();
POST_STATIC = BenchmarkUtils.getPostingBenchmark("noLoaderStatic", 1);
POST_STATIC_DOZEN = BenchmarkUtils.getPostingBenchmark("noLoaderStatic", 12);
POST_STATIC_HUNDRED = BenchmarkUtils.getPostingBenchmark("noLoaderStatic", 100);
}

@Benchmark
public static void postStatic(Blackhole bh) {
POST_STATIC.accept(bh);
}

@Benchmark
public static void postStaticDozen(Blackhole bh) {
POST_STATIC_DOZEN.accept(bh);
}

@Benchmark
public static void postStaticHundred(Blackhole bh) {
POST_STATIC_HUNDRED.accept(bh);
}
}
}

public static class Registering {
@State(Scope.Benchmark)
public static class Dynamic {
public static final Runnable SETUP_REGISTER_DYNAMIC;
public static final Runnable REGISTER_DYNAMIC;

static {
BenchmarkUtils.setupTransformedEnvironment();
Runnable[] results = BenchmarkUtils.getRegistrationBenchmark("noLoaderDynamic");
SETUP_REGISTER_DYNAMIC = results[0];
REGISTER_DYNAMIC = results[1];
}

@Setup(Level.Iteration)
public void setupIteration() {
SETUP_REGISTER_DYNAMIC.run();
}

@Benchmark
public void registerDynamic() {
REGISTER_DYNAMIC.run();
}
}

@State(Scope.Benchmark)
public static class Lambda {
public static final Runnable SETUP_REGISTER_LAMBDA;
public static final Runnable REGISTER_LAMBDA;

static {
BenchmarkUtils.setupTransformedEnvironment();
Runnable[] results = BenchmarkUtils.getRegistrationBenchmark("noLoaderLambda");
SETUP_REGISTER_LAMBDA = results[0];
REGISTER_LAMBDA = results[1];
}

@Setup(Level.Iteration)
public void setupIteration() {
SETUP_REGISTER_LAMBDA.run();
}

@Benchmark
public void registerLambda() {
REGISTER_LAMBDA.run();
}
}

@State(Scope.Benchmark)
public static class Static {
public static final Runnable SETUP_REGISTER_STATIC;
public static final Runnable REGISTER_STATIC;

static {
BenchmarkUtils.setupTransformedEnvironment();
Runnable[] results = BenchmarkUtils.getRegistrationBenchmark("noLoaderStatic");
SETUP_REGISTER_STATIC = results[0];
REGISTER_STATIC = results[1];
}

@Setup(Level.Iteration)
public void setupIteration() {
SETUP_REGISTER_STATIC.run();
}

@Benchmark
public void registerStatic() {
REGISTER_STATIC.run();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.eventbus.benchmarks;

import cpw.mods.modlauncher.Launcher;
import cpw.mods.modlauncher.api.ServiceRunner;
import net.minecraftforge.securemodules.SecureModuleClassLoader;
import net.minecraftforge.unsafe.UnsafeHacks;
import org.openjdk.jmh.infra.Blackhole;

import java.io.File;
import java.io.IOException;
import java.lang.module.ModuleFinder;
import java.util.List;
import java.util.function.Consumer;

public final class BenchmarkUtils {
private BenchmarkUtils() {}

private static final String MANAGER = "net.minecraftforge.eventbus.testjar.benchmarks.BenchmarkManager";

public static void setupTransformedEnvironment() {
try {
var jar = findTestJar();

setupBaseEnvironment();

System.setProperty("test.harness.game", jar.getAbsolutePath()); // Jars to put in the game layer
System.setProperty("test.harness.callable", BenchmarkUtils.TestCallback.class.getName());

// Reflectively call `new Launcher().run("--launchTarget", "testharness")` to skip
// some logging in Launcher#main(String[])
var launcher = Class.forName(Launcher.class.getName());
var ctr = launcher.getDeclaredConstructor();
UnsafeHacks.setAccessible(ctr);
var inst = ctr.newInstance();
var main = launcher.getDeclaredMethod("run", String[].class);
UnsafeHacks.setAccessible(main);
main.invoke(inst, (Object) new String[] { "--launchTarget", "testharness" });

validateEnvironment(true);
} catch (Exception e) {
sneak(e);
}
}

public static void setupNormalEnvironment() {
setupBaseEnvironment();
validateEnvironment(false);
}

// Boilerplate that ModLauncher's test harness requires
public static final class TestCallback {
// This is reflectively called by ModLauncher's test harness launch target. The required method name and type
// are hardcoded, and its containing class is defined by the system property "test.harness.callable".
public static ServiceRunner supplier() {
return ServiceRunner.NOOP;
}
}

@SuppressWarnings("unchecked")
public static Consumer<Blackhole> getPostingBenchmark(String name, int multiplier) {
try {
return (Consumer<Blackhole>) getBenchmarkManager()
.getDeclaredMethod("getPostingBenchmark", String.class, int.class)
.invoke(null, name, multiplier);
} catch (Exception e) {
sneak(e);
throw new AssertionError("Unreachable");
}
}

public static Runnable[] getRegistrationBenchmark(String name) {
try {
return (Runnable[]) getBenchmarkManager()
.getDeclaredMethod("getRegistrationBenchmark", String.class)
.invoke(null, name);
} catch (Exception e) {
sneak(e);
throw new AssertionError("Unreachable");
}
}

private static void setupBaseEnvironment() {
try {
var jar = findTestJar();
var cfg = ModuleLayer.boot().configuration().resolveAndBind(ModuleFinder.of(jar.toPath()), ModuleFinder.ofSystem(), List.of("net.minecraftforge.eventbus.testjars"));
var cl = new SecureModuleClassLoader("MODULE-CLASSLOADER", null, cfg, List.of(ModuleLayer.boot()));
Thread.currentThread().setContextClassLoader(cl);
} catch (Exception e) {
sneak(e);
throw new AssertionError("Unreachable");
}
}

private static File findTestJar() throws IOException {
var jar = new File("../eventbus-test-jar/build/libs/eventbus-test-jar.jar").getCanonicalFile();
if (!jar.exists())
throw new RuntimeException("Could not find test jar at: " + jar);

return jar;
}

private static Class<?> getBenchmarkManager() throws Exception {
return Class.forName(MANAGER, false, Thread.currentThread().getContextClassLoader());
}

private static void validateEnvironment(boolean shouldBeTransformed) {
try {
getBenchmarkManager()
.getDeclaredMethod("validate", boolean.class)
.invoke(null, shouldBeTransformed);
} catch (Exception e) {
sneak(e);
}
}

@SuppressWarnings("unchecked")
private static <E extends Throwable> void sneak(Throwable e) throws E {
throw (E) e;
}
}
Original file line number Diff line number Diff line change
@@ -4,7 +4,9 @@
*/
package net.minecraftforge.eventbus.benchmarks;

public class Main {
public final class Main {
private Main() {}

public static void main(String[] args) throws Exception {
org.openjdk.jmh.Main.main(args);
}
Original file line number Diff line number Diff line change
@@ -6,48 +6,43 @@

import cpw.mods.jarhandling.SecureJar;
import cpw.mods.modlauncher.api.*;
import joptsimple.OptionSpecBuilder;
import org.jetbrains.annotations.NotNull;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.function.BiFunction;

/**
* Test Launcher Service
*/
public class MockTransformerService implements ITransformationService {
@NotNull
@Override public String name() { return "test"; }
@Override public void arguments(BiFunction<String, String, OptionSpecBuilder> argumentBuilder) { }
@Override public void argumentValues(OptionResult result) { }
@Override public void initialize(IEnvironment environment) { }
@Override public void onLoad(IEnvironment env, Set<String> otherServices) throws IncompatibleEnvironmentException { }
@SuppressWarnings("rawtypes")
@NotNull
@Override public List<ITransformer> transformers() { return List.of(); }

public final class MockTransformerService implements ITransformationService {
@Override
public List<Resource> beginScanning(IEnvironment environment) {
var ret = new ArrayList<>(scan(IModuleLayerManager.Layer.PLUGIN));
ret.addAll(scan(IModuleLayerManager.Layer.SERVICE));
return ret;
public @NotNull String name() {
return "test";
}

@Override
public List<Resource> completeScan(IModuleLayerManager layerManager) {
return scan(IModuleLayerManager.Layer.GAME);
public void initialize(IEnvironment environment) {}

@Override
public void onLoad(IEnvironment env, Set<String> otherServices) throws IncompatibleEnvironmentException {}

@SuppressWarnings("rawtypes")
@Override
public @NotNull List<ITransformer> transformers() {
return List.of();
}

private List<Resource> scan(IModuleLayerManager.Layer layer) {
var prop = System.getProperty("test.harness." + layer.name().toLowerCase(Locale.ROOT));
@Override
public List<Resource> completeScan(IModuleLayerManager layerManager) {
var prop = System.getProperty("test.harness.game");
if (prop == null)
return List.of();

var jars = new ArrayList<SecureJar>();
for (var path : prop.split(","))
jars.add(SecureJar.from(FileSystems.getDefault().getPath(path)));
return List.of(new Resource(layer, jars));
jars.add(SecureJar.from(Path.of(path)));

return List.of(new Resource(IModuleLayerManager.Layer.GAME, jars));
}
}
11 changes: 9 additions & 2 deletions eventbus-test-jar/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

plugins {
id 'eclipse'
id 'java-library'
id 'org.gradlex.extra-java-module-info'
id 'net.minecraftforge.gradleutils'
id 'net.minecraftforge.licenser'
}
@@ -19,6 +19,13 @@ dependencies {
implementation(rootProject)
implementation(libs.bundles.asm)
implementation(libs.unsafe)

implementation libs.jmh.core
}

extraJavaModuleInfo {
failOnMissingModuleInfo = false
automaticModule('org.openjdk.jmh:jmh-core', 'jmh.core')
}

license {
@@ -29,4 +36,4 @@ license {
eclipse.classpath {
containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
file.whenMerged { entries.findAll { it.kind == 'lib' || it.path == 'org.eclipse.buildship.core.gradleclasspathcontainer' }.each { it.entryAttributes['module'] = 'true' } }
}
}
1 change: 1 addition & 0 deletions eventbus-test-jar/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
requires org.objectweb.asm;
requires org.objectweb.asm.commons;
requires net.minecraftforge.unsafe;
requires jmh.core;

exports net.minecraftforge.eventbus.testjar;
exports net.minecraftforge.eventbus.testjar.events;

This file was deleted.

Original file line number Diff line number Diff line change
@@ -5,90 +5,97 @@

package net.minecraftforge.eventbus.testjar.benchmarks;

import java.util.function.Function;
import java.util.function.Supplier;

import net.minecraftforge.eventbus.api.BusBuilder;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.testjar.events.CancelableEvent;
import org.openjdk.jmh.infra.Blackhole;

public class BenchmarkManager {
private final Supplier<IEventBus> bus;
private final Benchmark benchmark;
public final Runnable setupIteration;
public final Runnable run;
private static Function<String, String> renamer;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.function.Consumer;
import java.util.function.Supplier;

public BenchmarkManager(String benchmark, boolean shouldTransform, boolean ml, boolean wrapped) {
this.validate(shouldTransform);
if (ml) bus = () -> BusBuilder.builder().useModLauncher().build();
else bus = () -> BusBuilder.builder().build();
renamer = getRenamer(wrapped);
this.benchmark = getBenchmark(benchmark);
this.benchmark.setup(bus);
setupIteration = this.benchmark::setupIteration;
run = this.benchmark::run;
}
public final class BenchmarkManager {
private BenchmarkManager() {}

private static Function<String, String> getRenamer(boolean wrapped) {
if (!wrapped)
return null;
private static final MethodType RETURNS_CONSUMER = MethodType.methodType(Consumer.class);
private static final MethodType CONSUMER_FI_TYPE = MethodType.methodType(void.class, Object.class);

public static void validate(boolean shouldBeTransformed) {
try {
var mtd = IEventBus.class.getDeclaredMethod("rename", String.class);
return name -> {
try {
return (String)mtd.invoke(null, name);
} catch (Exception e) {
return sneak(e);
}
};
//noinspection JavaReflectionMemberAccess
CancelableEvent.class.getDeclaredField("LISTENER_LIST");
if (!shouldBeTransformed)
throw new RuntimeException("LISTENER_LIST field exists!");
} catch (Exception e) {
return sneak(e);
if (shouldBeTransformed)
throw new RuntimeException("Transformer did not apply!", e);
}
}

private static Benchmark getBenchmark(String name) {
switch (name) {
case "registerDynamic": return new Register.Dynamic();
case "registerLambda": return new Register.Lambda();
case "registerStatic": return new Register.Static();
public static Consumer<Blackhole> getPostingBenchmark(String name, int multiplier) {
return switch (name) {
case "modLauncherMixed" -> ModLauncherBenchmarks.Post.Factory.MIXED.create().apply(multiplier);
case "modLauncherDynamic" -> ModLauncherBenchmarks.Post.Factory.DYNAMIC.create().apply(multiplier);
case "modLauncherLambda" -> ModLauncherBenchmarks.Post.Factory.LAMBDA.create().apply(multiplier);
case "modLauncherStatic" -> ModLauncherBenchmarks.Post.Factory.STATIC.create().apply(multiplier);

case "noLoaderMixed" -> NoLoaderBenchmarks.Post.Factory.MIXED.create().apply(multiplier);
case "noLoaderDynamic" -> NoLoaderBenchmarks.Post.Factory.DYNAMIC.create().apply(multiplier);
case "noLoaderLambda" -> NoLoaderBenchmarks.Post.Factory.LAMBDA.create().apply(multiplier);
case "noLoaderStatic" -> NoLoaderBenchmarks.Post.Factory.STATIC.create().apply(multiplier);

case "postMixed": return new Post.Mixed.Single();
case "postMixedDozen": return new Post.Mixed.Dozen();
case "postMixedHundred": return new Post.Mixed.Hundred();
default -> throw new IllegalArgumentException("Unknown benchmark: " + name);
};
}

case "postDynamic": return new Post.Dynamic.Single();
case "postDynamicDozen": return new Post.Dynamic.Dozen();
case "postDynamicHundred": return new Post.Dynamic.Hundred();
public static Runnable[] getRegistrationBenchmark(String name) {
return switch (name) {
case "modLauncherDynamic" -> new Runnable[] {
ModLauncherBenchmarks.Register.Dynamic::setupIteration,
ModLauncherBenchmarks.Register.Dynamic::run
};
case "modLauncherLambda" -> new Runnable[] {
ModLauncherBenchmarks.Register.Lambda::setupIteration,
ModLauncherBenchmarks.Register.Lambda::run
};
case "modLauncherStatic" -> new Runnable[] {
ModLauncherBenchmarks.Register.Static::setupIteration,
ModLauncherBenchmarks.Register.Static::run
};

case "postLambda": return new Post.Lambda.Single();
case "postLambdaDozen": return new Post.Lambda.Dozen();
case "postLambdaHundred": return new Post.Lambda.Hundred();
case "noLoaderDynamic" -> new Runnable[] {
NoLoaderBenchmarks.Register.Dynamic::setupIteration,
NoLoaderBenchmarks.Register.Dynamic::run
};
case "noLoaderLambda" -> new Runnable[] {
NoLoaderBenchmarks.Register.Lambda::setupIteration,
NoLoaderBenchmarks.Register.Lambda::run
};
case "noLoaderStatic" -> new Runnable[] {
NoLoaderBenchmarks.Register.Static::setupIteration,
NoLoaderBenchmarks.Register.Static::run
};

case "postStatic": return new Post.Static.Single();
case "postStaticDozen": return new Post.Static.Dozen();
case "postStaticHundred": return new Post.Static.Hundred();
}
throw new IllegalArgumentException("Invalid benchmark: " + name);
default -> throw new IllegalArgumentException("Unknown benchmark: " + name);
};
}

private void validate(boolean shouldTransform) {
public static Consumer<Blackhole> setupPostingBenchmark(MethodHandles.Lookup lookup, Class<?> cls, int multiplier, Supplier<Consumer<IEventBus>> registrar) {
try {
CancelableEvent.class.getDeclaredField("LISTENER_LIST");
if (!shouldTransform)
throw new RuntimeException("LISTENER_LIST field exists!");
} catch (Exception e) {
if (shouldTransform)
throw new RuntimeException("Transformer did not apply!", e);
}
}
// Register the requested multiplier of listeners
lookup.findStatic(cls, "setup", MethodType.methodType(void.class, int.class, Supplier.class))
.invokeExact(multiplier, registrar);

@SuppressWarnings("unchecked")
private static <E extends Throwable, R> R sneak(Throwable e) throws E {
throw (E)e;
}
// Find the static `post(Blackhole)` method and return it as a `Consumer<Blackhole>` using LambdaMetaFactory
var postMethod = lookup.findStatic(cls, "post", MethodType.methodType(void.class, Blackhole.class));
var lmf = LambdaMetafactory.metafactory(lookup, "accept", RETURNS_CONSUMER, CONSUMER_FI_TYPE, postMethod, postMethod.type());

public static String rename(String name) {
return renamer == null ? name : renamer.apply(name);
@SuppressWarnings("unchecked")
var consumer = (Consumer<Blackhole>) lmf.getTarget().invokeExact();
return consumer;
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -7,13 +7,13 @@

import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.function.Supplier;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.*;
import org.objectweb.asm.commons.ClassRemapper;
import org.objectweb.asm.commons.Remapper;

public final class ClassFactory<T> {
public final class ClassFactory<T> implements Supplier<T> {
private final Mapper<T> mapper;
private final String binaryName;
private final byte[] data;
@@ -48,29 +48,59 @@ public T create() {
@Override
public String map(String internalName) {
if (internalName.equals(binaryName)) return binaryName + "$New" + count;
return BenchmarkManager.rename(internalName);
return internalName;
}
};

var remapStaticAccess = new ClassVisitor(Opcodes.ASM9) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
return new MethodVisitor(Opcodes.ASM9) {
@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
if ((opcode == Opcodes.GETSTATIC || opcode == Opcodes.PUTSTATIC) && owner.equals(binaryName))
owner = binaryName + "$New" + count;

super.visitFieldInsn(opcode, owner, name, descriptor);
}

@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
if (opcode == Opcodes.INVOKESTATIC && owner.equals(binaryName))
owner = binaryName + "$New" + count;

super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
};
}
};

var reader = new ClassReader(data);
var writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);

reader.accept(remapStaticAccess, 0);
reader.accept(new ClassRemapper(writer, renamer), 0);
try {
var newData = writer.toByteArray();
var newCls = (Class<?>) lookup.defineClass(newData);
return mapper.apply(newCls);
return mapper.apply(lookup, newCls);
} catch (Exception e) {
return sneak(e);
}
}

@Override
public T get() {
return create();
}

@SuppressWarnings("unchecked")
private static <E extends Throwable, R> R sneak(Throwable e) throws E {
throw (E) e;
}

@FunctionalInterface
public interface Mapper<T> {
T apply(Class<?> cls) throws Exception;
T apply(MethodHandles.Lookup lookup, Class<?> cls) throws Exception;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.minecraftforge.eventbus.testjar.benchmarks;

import net.minecraftforge.eventbus.api.BusBuilder;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.testjar.events.CancelableEvent;
import net.minecraftforge.eventbus.testjar.events.EventWithData;
import net.minecraftforge.eventbus.testjar.events.ResultEvent;
import net.minecraftforge.eventbus.testjar.subscribers.SubscriberDynamic;
import net.minecraftforge.eventbus.testjar.subscribers.SubscriberLambda;
import net.minecraftforge.eventbus.testjar.subscribers.SubscriberMixed;
import net.minecraftforge.eventbus.testjar.subscribers.SubscriberStatic;
import org.openjdk.jmh.infra.Blackhole;

import java.lang.invoke.MethodHandles;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.function.Consumer;
import java.util.function.IntFunction;
import java.util.function.Supplier;

public final class ModLauncherBenchmarks {
private ModLauncherBenchmarks() {}

public static final class Post {
private Post() {}
private static final IEventBus EVENT_BUS = BusBuilder.builder().useModLauncher().build();

public static void setup(int multiplier, Supplier<Consumer<IEventBus>> registrar) {
for (int i = 0; i < multiplier; i++)
registrar.get().accept(EVENT_BUS);
}

public static void post(Blackhole bh) {
EVENT_BUS.post(new CancelableEvent());
EVENT_BUS.post(new ResultEvent());
EVENT_BUS.post(new EventWithData("Foo", 5, true));
}

public static final class Factory {
private Factory() {}
public static final ClassFactory<IntFunction<Consumer<Blackhole>>> MIXED = new ClassFactory<>(
Post.class,
MethodHandles.lookup(),
(lookup, cls) -> multiplier ->
BenchmarkManager.setupPostingBenchmark(lookup, cls, multiplier, SubscriberMixed.Factory.REGISTER)
);

public static final ClassFactory<IntFunction<Consumer<Blackhole>>> DYNAMIC = new ClassFactory<>(
Post.class,
MethodHandles.lookup(),
(lookup, cls) -> multiplier ->
BenchmarkManager.setupPostingBenchmark(lookup, cls, multiplier, SubscriberDynamic.Factory.REGISTER)
);

public static final ClassFactory<IntFunction<Consumer<Blackhole>>> LAMBDA = new ClassFactory<>(
Post.class,
MethodHandles.lookup(),
(lookup, cls) -> multiplier ->
BenchmarkManager.setupPostingBenchmark(lookup, cls, multiplier, SubscriberLambda.Factory.REGISTER)
);

public static final ClassFactory<IntFunction<Consumer<Blackhole>>> STATIC = new ClassFactory<>(
Post.class,
MethodHandles.lookup(),
(lookup, cls) -> multiplier ->
BenchmarkManager.setupPostingBenchmark(lookup, cls, multiplier, SubscriberStatic.Factory.REGISTER)
);
}
}

public static final class Register {
private Register() {}

public static final class Dynamic extends RegistrationBenchmark {
private static final IEventBus EVENT_BUS = BusBuilder.builder().useModLauncher().build();
private static final Deque<Consumer<IEventBus>> REGISTRARS = new ArrayDeque<>(BATCH_COUNT);

public static void setupIteration() {
setupIteration(REGISTRARS, SubscriberDynamic.Factory.REGISTER, EVENT_BUS);
}

public static void run() {
REGISTRARS.pop().accept(EVENT_BUS);
}
}

public static final class Lambda extends RegistrationBenchmark {
private static final IEventBus EVENT_BUS = BusBuilder.builder().useModLauncher().build();
private static final Deque<Consumer<IEventBus>> REGISTRARS = new ArrayDeque<>(BATCH_COUNT);

public static void setupIteration() {
setupIteration(REGISTRARS, SubscriberLambda.Factory.REGISTER, EVENT_BUS);
}

public static void run() {
REGISTRARS.pop().accept(EVENT_BUS);
}
}

public static final class Static extends RegistrationBenchmark {
private static final IEventBus EVENT_BUS = BusBuilder.builder().useModLauncher().build();
private static final Deque<Consumer<IEventBus>> REGISTRARS = new ArrayDeque<>(BATCH_COUNT);

public static void setupIteration() {
setupIteration(REGISTRARS, SubscriberStatic.Factory.REGISTER, EVENT_BUS);
}

public static void run() {
REGISTRARS.pop().accept(EVENT_BUS);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.minecraftforge.eventbus.testjar.benchmarks;

import net.minecraftforge.eventbus.api.BusBuilder;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.testjar.events.CancelableEvent;
import net.minecraftforge.eventbus.testjar.events.EventWithData;
import net.minecraftforge.eventbus.testjar.events.ResultEvent;
import net.minecraftforge.eventbus.testjar.subscribers.SubscriberDynamic;
import net.minecraftforge.eventbus.testjar.subscribers.SubscriberLambda;
import net.minecraftforge.eventbus.testjar.subscribers.SubscriberMixed;
import net.minecraftforge.eventbus.testjar.subscribers.SubscriberStatic;
import org.openjdk.jmh.infra.Blackhole;

import java.lang.invoke.MethodHandles;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.function.Consumer;
import java.util.function.IntFunction;
import java.util.function.Supplier;

public final class NoLoaderBenchmarks {
private NoLoaderBenchmarks() {}

public static final class Post {
private Post() {}
private static final IEventBus EVENT_BUS = BusBuilder.builder().build();

public static void setup(int multiplier, Supplier<Consumer<IEventBus>> registrar) {
for (int i = 0; i < multiplier; i++)
registrar.get().accept(EVENT_BUS);
}

public static void post(Blackhole bh) {
EVENT_BUS.post(new CancelableEvent());
EVENT_BUS.post(new ResultEvent());
EVENT_BUS.post(new EventWithData("Foo", 5, true));
}

public static final class Factory {
private Factory() {}
public static final ClassFactory<IntFunction<Consumer<Blackhole>>> MIXED = new ClassFactory<>(
Post.class,
MethodHandles.lookup(),
(lookup, cls) -> multiplier ->
BenchmarkManager.setupPostingBenchmark(lookup, cls, multiplier, SubscriberMixed.Factory.REGISTER)
);

public static final ClassFactory<IntFunction<Consumer<Blackhole>>> DYNAMIC = new ClassFactory<>(
Post.class,
MethodHandles.lookup(),
(lookup, cls) -> multiplier ->
BenchmarkManager.setupPostingBenchmark(lookup, cls, multiplier, SubscriberDynamic.Factory.REGISTER)
);

public static final ClassFactory<IntFunction<Consumer<Blackhole>>> LAMBDA = new ClassFactory<>(
Post.class,
MethodHandles.lookup(),
(lookup, cls) -> multiplier ->
BenchmarkManager.setupPostingBenchmark(lookup, cls, multiplier, SubscriberLambda.Factory.REGISTER)
);

public static final ClassFactory<IntFunction<Consumer<Blackhole>>> STATIC = new ClassFactory<>(
Post.class,
MethodHandles.lookup(),
(lookup, cls) -> multiplier ->
BenchmarkManager.setupPostingBenchmark(lookup, cls, multiplier, SubscriberStatic.Factory.REGISTER)
);
}
}

public static final class Register {
private Register() {}

public static final class Dynamic extends RegistrationBenchmark {
private static final IEventBus EVENT_BUS = BusBuilder.builder().build();
private static final Deque<Consumer<IEventBus>> REGISTRARS = new ArrayDeque<>(BATCH_COUNT);

public static void setupIteration() {
setupIteration(REGISTRARS, SubscriberDynamic.Factory.REGISTER, EVENT_BUS);
}

public static void run() {
REGISTRARS.pop().accept(EVENT_BUS);
}
}

public static final class Lambda extends RegistrationBenchmark {
private static final IEventBus EVENT_BUS = BusBuilder.builder().build();
private static final Deque<Consumer<IEventBus>> REGISTRARS = new ArrayDeque<>(BATCH_COUNT);

public static void setupIteration() {
setupIteration(REGISTRARS, SubscriberLambda.Factory.REGISTER, EVENT_BUS);
}

public static void run() {
REGISTRARS.pop().accept(EVENT_BUS);
}
}

public static final class Static extends RegistrationBenchmark {
private static final IEventBus EVENT_BUS = BusBuilder.builder().build();
private static final Deque<Consumer<IEventBus>> REGISTRARS = new ArrayDeque<>(BATCH_COUNT);

public static void setupIteration() {
setupIteration(REGISTRARS, SubscriberStatic.Factory.REGISTER, EVENT_BUS);
}

public static void run() {
REGISTRARS.pop().accept(EVENT_BUS);
}
}
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.minecraftforge.eventbus.testjar.benchmarks;

import net.minecraftforge.eventbus.api.IEventBus;

import java.util.Deque;
import java.util.function.Consumer;

public abstract class RegistrationBenchmark {
// This is the number of dynamic classes to make in the setup method.
// This number just needs to be high enough that we don't get a EmptyStackException
// It takes a lot of memory, but it prevents optimizations in the EventBus from
// invalidating out test results. Such optimizations like the duplicate prevention
// on normal register(Object) calls but not on lambda calls
protected static final int BATCH_COUNT = 100_000;

protected static void setupIteration(Deque<Consumer<IEventBus>> registrars,
ClassFactory<Consumer<IEventBus>> registrarFactory,
IEventBus eventBus) {
// Clear the Deque and EventBus
registrars.clear();

try {
var mtd = eventBus.getClass().getDeclaredMethod("clearInternalData");
mtd.setAccessible(true);
mtd.invoke(eventBus);
} catch (Exception e) {
sneak(e);
}

// Fill the Deque
while (registrars.size() < BATCH_COUNT)
registrars.push(registrarFactory.create());

System.gc();
}

@SuppressWarnings("unchecked")
private static <E extends Throwable> void sneak(Throwable e) throws E {
throw (E) e;
}
}
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ public static class Factory {
public static final ClassFactory<Consumer<IEventBus>> REGISTER = new ClassFactory<>(
SubscriberDynamic.class,
MethodHandles.lookup(),
cls -> {
(lookup, cls) -> {
var inst = cls.getConstructor().newInstance();
return bus -> bus.register(inst);
}
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ public static class Factory {
public static final ClassFactory<Consumer<IEventBus>> REGISTER = new ClassFactory<>(
SubscriberLambda.class,
MethodHandles.lookup(),
cls -> (Consumer<IEventBus>) cls.getDeclaredField("register").get(null)
(lookup, cls) -> (Consumer<IEventBus>) cls.getDeclaredField("register").get(null)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.minecraftforge.eventbus.testjar.subscribers;

import net.minecraftforge.eventbus.api.IEventBus;

import java.util.function.Consumer;
import java.util.function.Supplier;

public class SubscriberMixed {
public static class Factory {
public static final Supplier<Consumer<IEventBus>> REGISTER = Factory::create;

private static Consumer<IEventBus> create() {
return new Consumer<>() {
private int idx = 0;

@Override
public void accept(IEventBus eventBus) {
var registrar = switch (idx++ % 3) {
case 0 -> SubscriberDynamic.Factory.REGISTER.create();
case 1 -> SubscriberStatic.Factory.REGISTER.create();
case 2 -> SubscriberLambda.Factory.REGISTER.create();
default -> throw new AssertionError();
};
registrar.accept(eventBus);
}
};
}
}
}
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@

package net.minecraftforge.eventbus.testjar.subscribers;


import java.lang.invoke.MethodHandles;
import java.util.function.Consumer;

@@ -30,7 +29,7 @@ public static class Factory {
public static final ClassFactory<Consumer<IEventBus>> REGISTER = new ClassFactory<>(
SubscriberStatic.class,
MethodHandles.lookup(),
cls -> bus -> bus.register(cls)
(lookup, cls) -> bus -> bus.register(cls)
);
}
}
4 changes: 2 additions & 2 deletions eventbus-test/build.gradle
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ license {

dependencies {
testImplementation(rootProject)
testImplementation(project(':eventbus-test-jar'))
testImplementation(projects.eventbusTestJar)
testImplementation(libs.junit.api)
testImplementation(libs.securemodules)
testImplementation(libs.modlauncher)
@@ -69,4 +69,4 @@ VALID_VMS.each { javaVendor, javaVersions ->
eclipse.classpath {
containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
file.whenMerged { entries.findAll { it.kind == 'lib' || it.path == 'org.eclipse.buildship.core.gradleclasspathcontainer' }.each { it.entryAttributes['module'] = 'true' } }
}
}
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
14 changes: 10 additions & 4 deletions settings.gradle
Original file line number Diff line number Diff line change
@@ -6,15 +6,15 @@ pluginManagement {
}

plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.9.0'
}

dependencyResolutionManagement {
versionCatalogs {
libs {
// ModLauncher stack
library('modlauncher', 'net.minecraftforge:modlauncher:10.1.1')
library('securemodules', 'net.minecraftforge:securemodules:2.2.2')
library('securemodules', 'net.minecraftforge:securemodules:2.2.21')
library('unsafe', 'net.minecraftforge:unsafe:0.9.2')

// Generics from lambdas
@@ -30,18 +30,24 @@ dependencyResolutionManagement {
library('junit-platform-launcher', 'org.junit.platform:junit-platform-launcher:1.10.1')
bundle('junit-runtime', ['junit-engine', 'junit-platform-launcher'])

version('asm', '9.6')
version('asm', '9.7.1')
library('asm', 'org.ow2.asm', 'asm' ).versionRef('asm')
library('asm-tree', 'org.ow2.asm', 'asm-tree' ).versionRef('asm')
library('asm-commons', 'org.ow2.asm', 'asm-commons').versionRef('asm')
bundle('asm', ['asm', 'asm-tree', 'asm-commons'])

version('jmh', '1.37')
library('jmh-core', 'org.openjdk.jmh', 'jmh-core') versionRef 'jmh'
library('jmh-annotationProcessor', 'org.openjdk.jmh', 'jmh-generator-annprocess') versionRef 'jmh'
}
}
}

enableFeaturePreview 'TYPESAFE_PROJECT_ACCESSORS'

rootProject.name = 'EventBus'
include 'eventbus-jmh'
include 'eventbus-test'
include 'eventbus-test-jar'
if (file('eventbus-wrapper').exists())
include 'eventbus-wrapper'
include 'eventbus-wrapper'

0 comments on commit da2bba6

Please sign in to comment.