Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hasListeners()/hasAnyListeners() to avoid unnecessary event object instantiation #51

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public class EventBusBenchmark {
private Consumer<Object> postDynamic;
private Consumer<Object> postLambda;
private Consumer<Object> postCombined;
private Consumer<Object> postCombinedHasListeners;
private Consumer<Object> postCombinedHasAnyListeners;

@SuppressWarnings("unchecked")
@Setup
Expand All @@ -37,6 +39,8 @@ public void setup() throws Exception {
postDynamic = (Consumer<Object>)cls.getField("postDynamic").get(null);
postLambda = (Consumer<Object>)cls.getField("postLambda").get(null);
postCombined = (Consumer<Object>)cls.getField("postCombined").get(null);
postCombinedHasListeners = (Consumer<Object>)cls.getField("postCombinedHasListeners").get(null);
postCombinedHasAnyListeners = (Consumer<Object>)cls.getField("postCombinedHasAnyListeners").get(null);
}

public static class TestCallback {
Expand Down Expand Up @@ -72,6 +76,18 @@ public int testModLauncherCombined() throws Throwable {

// ClassLoader ASM Factory
@Benchmark
public int testModLauncherCombinedHasListeners() throws Throwable {
postCombinedHasListeners.accept(ModLauncher);
return 0;
}

@Benchmark
public int testModLauncherCombinedHasAnyListeners() throws Throwable {
postCombinedHasAnyListeners.accept(ModLauncher);
return 0;
}

// ClassLoader ASM Factory
public int testClassLoaderDynamic() throws Throwable {
postDynamic.accept(ClassLoader);
return 0;
Expand All @@ -94,4 +110,16 @@ public int testClassLoaderCombined() throws Throwable {
postCombined.accept(ClassLoader);
return 0;
}

@Benchmark
public int testClassLoaderCombinedHasListeners() throws Throwable {
postCombinedHasListeners.accept(ClassLoader);
return 0;
}

@Benchmark
public int testClassLoaderCombinedHasAnyListeners() throws Throwable {
postCombinedHasAnyListeners.accept(ClassLoader);
return 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,16 @@ public int testNoLoaderCombined() {
BenchmarkArmsLength.postCombined(BenchmarkArmsLength.NoLoader);
return 0;
}

@Benchmark
public int testNoLoaderCombinedHasListeners() {
BenchmarkArmsLength.postCombinedHasListeners(BenchmarkArmsLength.NoLoader);
return 0;
}

@Benchmark
public int testNoLoaderCombinedHasAnyListeners() {
BenchmarkArmsLength.postCombinedHasAnyListeners(BenchmarkArmsLength.NoLoader);
return 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.function.Consumer;

import net.minecraftforge.eventbus.EventBus;
import net.minecraftforge.eventbus.api.BusBuilder;
import net.minecraftforge.eventbus.api.IEventBus;

Expand Down Expand Up @@ -60,6 +61,8 @@ public static Runnable supplier() {
public static final Consumer<Object> postDynamic = BenchmarkArmsLength::postDynamic;
public static final Consumer<Object> postLambda = BenchmarkArmsLength::postLambda;
public static final Consumer<Object> postCombined = BenchmarkArmsLength::postCombined;
public static final Consumer<Object> postCombinedHasListeners = BenchmarkArmsLength::postCombinedHasListeners;
public static final Consumer<Object> postCombinedHasAnyListeners = BenchmarkArmsLength::postCombinedHasAnyListeners;

public static void postStatic(Object bus)
{
Expand All @@ -81,10 +84,45 @@ public static void postCombined(Object bus)
postAll(((Bus)bus).combinedSubscriberBus);
}

public static void postCombinedHasListeners(Object bus)
{
postAllWithHasListeners((EventBus) ((Bus)bus).combinedSubscriberBus);
}

public static void postCombinedHasAnyListeners(Object bus)
{
postAllWithHasAnyListeners(((Bus)bus).combinedSubscriberBus);
}

private static void postAll(IEventBus bus)
{
bus.post(new CancelableEvent());
bus.post(new ResultEvent());
bus.post(new EventWithData("Foo", 5, true)); //Some example data
}

private static void postAllWithHasListeners(EventBus bus)
{
if (CancelableEvent.hasListeners(bus))
bus.post(new CancelableEvent());

if (ResultEvent.hasListeners(bus))
bus.post(new ResultEvent());

if (EventWithData.hasListeners(bus))
bus.post(new EventWithData("Foo", 5, true)); //Some example data
}

private static void postAllWithHasAnyListeners(IEventBus bus)
{
if (CancelableEvent.hasAnyListeners())
bus.post(new CancelableEvent());

if (ResultEvent.hasAnyListeners())
bus.post(new ResultEvent());

if (EventWithData.hasAnyListeners())
bus.post(new EventWithData("Foo", 5, true)); //Some example data
}

}
2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pluginManagement {
}
}

rootProject.name = 'eventbus'
rootProject.name = 'EventBus'
include 'eventbus-jmh'
include 'eventbus-test'
include 'eventbus-testjars'
4 changes: 4 additions & 0 deletions src/main/java/net/minecraftforge/eventbus/EventBus.java
Original file line number Diff line number Diff line change
Expand Up @@ -340,4 +340,8 @@ public void shutdown()
public void start() {
this.shutdown = false;
}

public int getBusID() {
return busID;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import net.minecraftforge.eventbus.api.Event;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;

Expand All @@ -38,7 +39,7 @@
public class EventSubclassTransformer
{
private static final Logger LOGGER = LogManager.getLogger();
private Optional<ClassLoader> gameClassLoader = null;
private @Nullable ClassLoader gameClassLoader = null;

public Optional<ClassNode> transform(final ClassNode classNode, final Type classType)
{
Expand Down Expand Up @@ -222,6 +223,7 @@ private boolean addListenerList(ClassNode classNode, boolean useSuper)
Type tList = getType(LISTENER_LIST);
Type tHelper = Type.getType(LISTENER_LIST_HELPER);
Type tThis = getObjectType(classNode.name);
Type tEventBus = getType(EVENT_BUS);

/* Add:
* private static final ListenerList LISTENER_LIST;
Expand Down Expand Up @@ -287,6 +289,68 @@ private boolean addListenerList(ClassNode classNode, boolean useSuper)
}
method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, LISTENER_LIST_F.name(), LISTENER_LIST_F.desc()));
method.instructions.add(new InsnNode(ARETURN));

/* Add or replace:
* public static ListenerList getListenerListStatically()
* {
* return LISTENER_LIST;
* }
*/
method = classNode.methods.stream().filter(LISTENER_LIST_GET_STATICALLY::equals).findFirst().orElse(null);
if (method == null)
{
method = new MethodNode(ACC_PUBLIC | ACC_STATIC, LISTENER_LIST_GET_STATICALLY.name(), LISTENER_LIST_GET_STATICALLY.desc(), null, null);
classNode.methods.add(method);
}
else
{
clear(method);
}
method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, LISTENER_LIST_F.name(), LISTENER_LIST_F.desc()));
method.instructions.add(new InsnNode(ARETURN));

/* Add or replace:
* public static boolean hasAnyListeners()
* {
* return LISTENER_LIST.hasAnyListeners();
* }
*/
method = classNode.methods.stream().filter(HAS_ANY_LISTENERS_M::equals).findFirst().orElse(null);
if (method == null)
{
method = new MethodNode(ACC_PUBLIC | ACC_STATIC, HAS_ANY_LISTENERS_M.name(), HAS_ANY_LISTENERS_M.desc(), null, null);
classNode.methods.add(method);
}
else
{
clear(method);
}
method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, LISTENER_LIST_F.name(), LISTENER_LIST_F.desc()));
method.instructions.add(new MethodInsnNode(INVOKEVIRTUAL, tList.getInternalName(), HAS_ANY_LISTENERS_M.name(), HAS_ANY_LISTENERS_M.desc(), false));
method.instructions.add(new InsnNode(IRETURN));

/* Add or replace:
* public static boolean hasListeners(EventBus bus)
* {
* return LISTENER_LIST.hasListeners(bus.getBusID());
* }
*/
method = classNode.methods.stream().filter(EVENT_HAS_LISTENERS_M::equals).findFirst().orElse(null);
if (method == null)
{
method = new MethodNode(ACC_PUBLIC | ACC_STATIC, EVENT_HAS_LISTENERS_M.name(), EVENT_HAS_LISTENERS_M.desc(), null, null);
classNode.methods.add(method);
}
else
{
clear(method);
}
method.instructions.add(new FieldInsnNode(GETSTATIC, classNode.name, LISTENER_LIST_F.name(), LISTENER_LIST_F.desc()));
method.instructions.add(new VarInsnNode(ALOAD, 0));
method.instructions.add(new MethodInsnNode(INVOKEVIRTUAL, tEventBus.getInternalName(), GET_BUS_ID_M.name(), GET_BUS_ID_M.desc(), false));
method.instructions.add(new MethodInsnNode(INVOKEVIRTUAL, tList.getInternalName(), LISTENER_LIST_HAS_LISTENERS_M.name(), LISTENER_LIST_HAS_LISTENERS_M.desc(), false));
method.instructions.add(new InsnNode(IRETURN));

LOGGER.debug(EVENTBUS, "Event transform complete: {}", classNode.name);
return true;
}
Expand All @@ -300,11 +364,12 @@ private ClassLoader getClassLoader() {
return loader;
}

@Nullable
private ClassLoader getGameClassLoader() {
if (this.gameClassLoader == null) {
var gameLayer = Launcher.INSTANCE.findLayerManager().flatMap(lm -> lm.getLayer(IModuleLayerManager.Layer.GAME)).orElseThrow();
this.gameClassLoader = gameLayer.modules().stream().findFirst().map(Module::getClassLoader);
this.gameClassLoader = gameLayer.modules().stream().findFirst().map(Module::getClassLoader).orElse(null);
}
return this.gameClassLoader.orElse(null);
return this.gameClassLoader;
}
}
16 changes: 16 additions & 0 deletions src/main/java/net/minecraftforge/eventbus/ListenerList.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,22 @@ public IEventListener[] getListeners(int id)
return lists[id].getListeners();
}

public boolean hasListeners(int id)
{
return lists[id].getListeners().length > 0;
}

public boolean hasAnyListeners()
{
for (int i = 0, listsLength = lists.length; i < listsLength; i++)
{
if (lists[i].getListeners().length > 0)
return true;
}

return false;
}

public void register(int id, EventPriority priority, IEventListener listener)
{
lists[id].register(priority, listener);
Expand Down
5 changes: 2 additions & 3 deletions src/main/java/net/minecraftforge/eventbus/LockHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
import java.util.function.Function;
import java.util.function.Supplier;

/*
*
* Helper class that should be faster then ConcurrentHashMap,
/**
* Helper class that should be faster than ConcurrentHashMap,
* yet still manages to properly deal with many threads.
*/
public class LockHelper<K,V> {
Expand Down
14 changes: 12 additions & 2 deletions src/main/java/net/minecraftforge/eventbus/Names.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import org.objectweb.asm.tree.MethodNode;

class Names {
final class Names {
static final String SUBSCRIBE_EVENT = "Lnet/minecraftforge/eventbus/api/SubscribeEvent;";
static final String HAS_RESULT = "Lnet/minecraftforge/eventbus/api/Event$HasResult;";
static final Method HAS_RESULT_M = new Method("hasResult", getMethodDescriptor(BOOLEAN_TYPE));
Expand All @@ -17,12 +17,22 @@ class Names {
static final Method LISTENER_LIST_GET = new Method("getListenerList", getMethodDescriptor(getType(LISTENER_LIST)));
static final Method LISTENER_LIST_F = new Method("LISTENER_LIST", LISTENER_LIST);
static final String LISTENER_LIST_HELPER = "Lnet/minecraftforge/eventbus/api/EventListenerHelper;";
static final Method LISTENER_LIST_HAS_LISTENERS_M = new Method("hasListeners", getMethodDescriptor(BOOLEAN_TYPE, INT_TYPE));

static final Method LISTENER_LIST_GET_STATICALLY = new Method("getListenerListStatically", getMethodDescriptor(getType(LISTENER_LIST)));

static final String EVENT_BUS = "Lnet/minecraftforge/eventbus/EventBus;";
static final Method GET_BUS_ID_M = new Method("getBusID", getMethodDescriptor(INT_TYPE));

static final Method HAS_ANY_LISTENERS_M = new Method("hasAnyListeners", getMethodDescriptor(BOOLEAN_TYPE));

static final String EVENT = "Lnet/minecraftforge/eventbus/api/Event;";
static final Method EVENT_HAS_LISTENERS_M = new Method("hasListeners", getMethodDescriptor(BOOLEAN_TYPE, getType(EVENT_BUS)));

static final Method INIT_M = new Method("<init>", getMethodDescriptor(VOID_TYPE));
static final Method STATIC_INIT_M = new Method("<clinit>", getMethodDescriptor(VOID_TYPE));

static record Method(String name, String desc) {
record Method(String name, String desc) {
boolean equals(MethodNode node) {
return this.name.equals(node.name) && this.desc.equals(node.desc);
}
Expand Down
Loading