Make the EventBus faster by avoiding Map#entrySet

This commit is contained in:
Vankka 2022-01-25 16:24:17 +02:00
parent 9662d9515f
commit 9cfaae7464
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
2 changed files with 49 additions and 40 deletions

View File

@ -53,6 +53,7 @@ public class EventBusImpl implements EventBus {
); );
private final Map<Object, List<EventListenerImpl>> listeners = new ConcurrentHashMap<>(); private final Map<Object, List<EventListenerImpl>> listeners = new ConcurrentHashMap<>();
private final List<EventListenerImpl> allListeners = new CopyOnWriteArrayList<>();
private final DiscordSRV discordSRV; private final DiscordSRV discordSRV;
private final Logger logger; private final Logger logger;
@ -76,7 +77,7 @@ public class EventBusImpl implements EventBus {
Class<?> currentClass = listenerClass; Class<?> currentClass = listenerClass;
do { do {
for (Method method : currentClass.getDeclaredMethods()) { for (Method method : currentClass.getDeclaredMethods()) {
checkMethod(listenerClass, method, suppressedMethods, methods, methodsByPriority); checkMethod(eventListener, listenerClass, method, suppressedMethods, methods, methodsByPriority);
} }
} while ((currentClass = currentClass.getSuperclass()) != null); } while ((currentClass = currentClass.getSuperclass()) != null);
@ -88,10 +89,11 @@ public class EventBusImpl implements EventBus {
} }
listeners.put(eventListener, methods); listeners.put(eventListener, methods);
allListeners.addAll(methods);
logger.debug("Listener " + eventListener.getClass().getName() + " subscribed"); logger.debug("Listener " + eventListener.getClass().getName() + " subscribed");
} }
private void checkMethod(Class<?> listenerClass, Method method, private void checkMethod(Object eventListener, Class<?> listenerClass, Method method,
List<Throwable> suppressedMethods, List<EventListenerImpl> methods, List<Throwable> suppressedMethods, List<EventListenerImpl> methods,
EnumMap<EventPriority, List<EventListenerImpl>> methodsByPriority) { EnumMap<EventPriority, List<EventListenerImpl>> methodsByPriority) {
Subscribe annotation = method.getAnnotation(Subscribe.class); Subscribe annotation = method.getAnnotation(Subscribe.class);
@ -140,7 +142,7 @@ public class EventBusImpl implements EventBus {
} }
EventPriority eventPriority = annotation.priority(); EventPriority eventPriority = annotation.priority();
EventListenerImpl listener = new EventListenerImpl(listenerClass, annotation, firstParameter, method); EventListenerImpl listener = new EventListenerImpl(eventListener, listenerClass, annotation, firstParameter, method);
methods.add(listener); methods.add(listener);
methodsByPriority.computeIfAbsent(eventPriority, key -> new CopyOnWriteArrayList<>()) methodsByPriority.computeIfAbsent(eventPriority, key -> new CopyOnWriteArrayList<>())
@ -154,8 +156,11 @@ public class EventBusImpl implements EventBus {
@Override @Override
public void unsubscribe(@NotNull Object eventListener) { public void unsubscribe(@NotNull Object eventListener) {
listeners.remove(eventListener); List<EventListenerImpl> removed = listeners.remove(eventListener);
logger.debug("Listener " + eventListener.getClass().getName() + " unsubscribed"); if (removed != null) {
allListeners.removeAll(removed);
logger.debug("Listener " + eventListener.getClass().getName() + " unsubscribed");
}
} }
@Override @Override
@ -182,44 +187,42 @@ public class EventBusImpl implements EventBus {
Class<?> eventClass = event.getClass(); Class<?> eventClass = event.getClass();
for (EventPriority priority : EventPriority.values()) { for (EventPriority priority : EventPriority.values()) {
for (Map.Entry<Object, List<EventListenerImpl>> entry : listeners.entrySet()) { for (EventListenerImpl eventListener : allListeners) {
Object listener = entry.getKey(); if (eventListener.isIgnoringCancelled() && event instanceof Cancellable && ((Cancellable) event).isCancelled()) {
for (EventListenerImpl eventListener : entry.getValue()) { continue;
if (eventListener.isIgnoringCancelled() && event instanceof Cancellable && ((Cancellable) event).isCancelled()) { }
continue; if (eventListener.priority() != priority) {
} continue;
if (eventListener.priority() != priority) { }
continue; if (!eventListener.eventClass().isAssignableFrom(eventClass)) {
} continue;
if (!eventListener.eventClass().isAssignableFrom(eventClass)) { }
continue;
}
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
try { try {
eventListener.method().invoke(listener, event); Object listener = eventListener.listener();
} catch (IllegalAccessException e) { eventListener.method().invoke(listener, event);
discordSRV.logger().error("Failed to access listener method: " + eventListener.methodName(), e); } catch (IllegalAccessException e) {
} catch (InvocationTargetException e) { discordSRV.logger().error("Failed to access listener method: " + eventListener.methodName(), e);
discordSRV.logger().error("Failed to pass " + event.getClass().getSimpleName() + " to " + eventListener, e.getCause()); } catch (InvocationTargetException e) {
} discordSRV.logger().error("Failed to pass " + event.getClass().getSimpleName() + " to " + eventListener, e.getCause());
long timeTaken = System.currentTimeMillis() - startTime; }
logger.trace(eventListener + " took " + timeTaken + "ms to execute"); long timeTaken = System.currentTimeMillis() - startTime;
logger.trace(eventListener + " took " + timeTaken + "ms to execute");
for (int index = 0; index < STATES.size(); index++) { for (int index = 0; index < STATES.size(); index++) {
Pair<Function<Object, Boolean>, ThreadLocal<EventListener>> state = STATES.get(index); Pair<Function<Object, Boolean>, ThreadLocal<EventListener>> state = STATES.get(index);
boolean current = states.get(index); boolean current = states.get(index);
boolean updated = state.getKey().apply(event); boolean updated = state.getKey().apply(event);
states.set(index, updated); states.set(index, updated);
ThreadLocal<EventListener> stateHolder = state.getValue(); ThreadLocal<EventListener> stateHolder = state.getValue();
if (current != updated) { if (current != updated) {
if (updated) { if (updated) {
stateHolder.set(eventListener); stateHolder.set(eventListener);
} else { } else {
stateHolder.remove(); stateHolder.remove();
}
} }
} }
} }

View File

@ -27,12 +27,14 @@ import java.lang.reflect.Method;
public class EventListenerImpl implements EventListener { public class EventListenerImpl implements EventListener {
private final Object listener;
private final Class<?> listenerClass; private final Class<?> listenerClass;
private final Subscribe annotation; private final Subscribe annotation;
private final Class<?> eventClass; private final Class<?> eventClass;
private final Method method; private final Method method;
public EventListenerImpl(Class<?> listenerClass, Subscribe annotation, Class<?> eventClass, Method method) { public EventListenerImpl(Object listener, Class<?> listenerClass, Subscribe annotation, Class<?> eventClass, Method method) {
this.listener = listener;
this.listenerClass = listenerClass; this.listenerClass = listenerClass;
this.annotation = annotation; this.annotation = annotation;
this.eventClass = eventClass; this.eventClass = eventClass;
@ -47,6 +49,10 @@ public class EventListenerImpl implements EventListener {
return annotation.priority(); return annotation.priority();
} }
public Object listener() {
return listener;
}
public Class<?> eventClass() { public Class<?> eventClass() {
return eventClass; return eventClass;
} }