GREAT performance. Added IdentityMap to ReflectionUtils annotations collector
This commit is contained in:
parent
61756547bb
commit
61f7cd26b1
@ -59,11 +59,19 @@ class SubscriptionManager {
|
|||||||
private final IdentityMap<Class<?>, Subscription[]> subsPerListener;
|
private final IdentityMap<Class<?>, Subscription[]> subsPerListener;
|
||||||
|
|
||||||
|
|
||||||
// all subscriptions per message type. We perpetually KEEP the types
|
// We perpetually KEEP the types registered here, and just change what is sub/unsub
|
||||||
private volatile IdentityMap<Class<?>, Subscription[]> subsPerMessageSingle;
|
|
||||||
|
// all subscriptions of a message type.
|
||||||
|
private volatile IdentityMap<Class<?>, Subscription[]> subsSingle;
|
||||||
|
|
||||||
// keeps track of all subscriptions of the super classes of a message type.
|
// keeps track of all subscriptions of the super classes of a message type.
|
||||||
private volatile IdentityMap<Class<?>, Subscription[]> subsPerSuperMessageSingle;
|
private volatile IdentityMap<Class<?>, Subscription[]> subsSuperSingle;
|
||||||
|
|
||||||
|
// keeps track of all subscriptions of varity (var-arg) classes of a message type
|
||||||
|
private volatile IdentityMap<Class<?>, Subscription[]> subsVaritySingle;
|
||||||
|
|
||||||
|
// keeps track of all subscriptions of super-class varity (var-arg) classes of a message type
|
||||||
|
private volatile IdentityMap<Class<?>, Subscription[]> subsSuperVaritySingle;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -83,15 +91,25 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
|
|
||||||
// Recommended for best performance while adhering to the "single writer principle"
|
// Recommended for best performance while adhering to the "single writer principle"
|
||||||
private final AtomicReferenceFieldUpdater<SubscriptionManager, IdentityMap> subsPerMessageSingleREF =
|
private final AtomicReferenceFieldUpdater<SubscriptionManager, IdentityMap> subsSingleREF =
|
||||||
AtomicReferenceFieldUpdater.newUpdater(SubscriptionManager.class,
|
AtomicReferenceFieldUpdater.newUpdater(SubscriptionManager.class,
|
||||||
IdentityMap.class,
|
IdentityMap.class,
|
||||||
"subsPerMessageSingle");
|
"subsSingle");
|
||||||
|
|
||||||
private final AtomicReferenceFieldUpdater<SubscriptionManager, IdentityMap> subsPerSuperMessageSingleREF =
|
private final AtomicReferenceFieldUpdater<SubscriptionManager, IdentityMap> subsSuperSingleREF =
|
||||||
AtomicReferenceFieldUpdater.newUpdater(SubscriptionManager.class,
|
AtomicReferenceFieldUpdater.newUpdater(SubscriptionManager.class,
|
||||||
IdentityMap.class,
|
IdentityMap.class,
|
||||||
"subsPerSuperMessageSingle");
|
"subsSuperSingle");
|
||||||
|
|
||||||
|
private final AtomicReferenceFieldUpdater<SubscriptionManager, IdentityMap> subsVaritySingleREF =
|
||||||
|
AtomicReferenceFieldUpdater.newUpdater(SubscriptionManager.class,
|
||||||
|
IdentityMap.class,
|
||||||
|
"subsVaritySingle");
|
||||||
|
|
||||||
|
private final AtomicReferenceFieldUpdater<SubscriptionManager, IdentityMap> subsSuperVaritySingleREF =
|
||||||
|
AtomicReferenceFieldUpdater.newUpdater(SubscriptionManager.class,
|
||||||
|
IdentityMap.class,
|
||||||
|
"subsSuperVaritySingle");
|
||||||
|
|
||||||
//NOTE for multiArg, can use the memory address concatenated with other ones and then just put it in the 'single" map (convert single to
|
//NOTE for multiArg, can use the memory address concatenated with other ones and then just put it in the 'single" map (convert single to
|
||||||
// use this too). it would likely have to be longs no idea what to do for arrays?? (arrays should verify all the elements are the
|
// use this too). it would likely have to be longs no idea what to do for arrays?? (arrays should verify all the elements are the
|
||||||
@ -106,8 +124,10 @@ class SubscriptionManager {
|
|||||||
// modified ONLY during SUB/UNSUB
|
// modified ONLY during SUB/UNSUB
|
||||||
nonListeners = new IdentityMap<Class<?>, Boolean>(16, LOAD_FACTOR);
|
nonListeners = new IdentityMap<Class<?>, Boolean>(16, LOAD_FACTOR);
|
||||||
subsPerListener = new IdentityMap<>(32, LOAD_FACTOR);
|
subsPerListener = new IdentityMap<>(32, LOAD_FACTOR);
|
||||||
subsPerMessageSingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
subsSingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
||||||
subsPerSuperMessageSingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
subsSuperSingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
||||||
|
subsVaritySingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
||||||
|
subsSuperVaritySingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -127,13 +147,17 @@ class SubscriptionManager {
|
|||||||
void shutdown() {
|
void shutdown() {
|
||||||
this.nonListeners.clear();
|
this.nonListeners.clear();
|
||||||
|
|
||||||
this.subsPerMessageSingle.clear();
|
this.subsPerListener.clear();
|
||||||
this.subsPerSuperMessageSingle.clear();
|
|
||||||
|
this.subsSingle.clear();
|
||||||
|
this.subsSuperSingle.clear();
|
||||||
|
this.subsVaritySingle.clear();
|
||||||
|
this.subsSuperVaritySingle.clear();
|
||||||
|
|
||||||
|
|
||||||
this.subscriptionsPerMessageMulti.clear();
|
this.subscriptionsPerMessageMulti.clear();
|
||||||
|
|
||||||
this.subsPerListener.clear();
|
|
||||||
this.classUtils.shutdown();
|
this.classUtils.shutdown();
|
||||||
clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
public
|
||||||
@ -169,17 +193,15 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
final Class<?> listenerClass = listener.getClass();
|
final Class<?> listenerClass = listener.getClass();
|
||||||
|
|
||||||
if (this.nonListeners.containsKey(listenerClass)) {
|
final IdentityMap<Class<?>, Boolean> nonListeners = this.nonListeners;
|
||||||
|
if (nonListeners.containsKey(listenerClass)) {
|
||||||
// early reject of known classes that do not define message handlers
|
// early reject of known classes that do not define message handlers
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// these are concurrent collections
|
|
||||||
clear();
|
|
||||||
|
|
||||||
// this is an array, because subscriptions for a specific listener CANNOT change, either they exist or do not exist.
|
// this is an array, because subscriptions for a specific listener CANNOT change, either they exist or do not exist.
|
||||||
// ONCE subscriptions are in THIS map, they are considered AVAILABLE.
|
// ONCE subscriptions are in THIS map, they are considered AVAILABLE.
|
||||||
Subscription[] subscriptions = this.subsPerListener.get(listenerClass);
|
Subscription[] subscriptions = subsPerListener.get(listenerClass);
|
||||||
|
|
||||||
// the subscriptions from the map were null, so create them
|
// the subscriptions from the map were null, so create them
|
||||||
if (subscriptions == null) {
|
if (subscriptions == null) {
|
||||||
@ -196,7 +218,10 @@ class SubscriptionManager {
|
|||||||
subscriptions = new Subscription[handlersSize];
|
subscriptions = new Subscription[handlersSize];
|
||||||
|
|
||||||
// access a snapshot of the subscriptions (single-writer-principle)
|
// access a snapshot of the subscriptions (single-writer-principle)
|
||||||
final IdentityMap<Class<?>, Subscription[]> local = subsPerMessageSingleREF.get(this);
|
final IdentityMap<Class<?>, Subscription[]> localSubs = subsSingleREF.get(this);
|
||||||
|
final IdentityMap<Class<?>, Subscription[]> localSuperSubs = subsSuperSingleREF.get(this);
|
||||||
|
final IdentityMap<Class<?>, Subscription[]> localVaritySubs = subsVaritySingleREF.get(this);
|
||||||
|
final IdentityMap<Class<?>, Subscription[]> localSuperVaritySubs = subsSuperVaritySingleREF.get(this);
|
||||||
|
|
||||||
Subscription subscription;
|
Subscription subscription;
|
||||||
|
|
||||||
@ -218,8 +243,9 @@ class SubscriptionManager {
|
|||||||
messageHandlerTypes = messageHandler.getHandledMessages();
|
messageHandlerTypes = messageHandler.getHandledMessages();
|
||||||
handlerType = messageHandlerTypes[0];
|
handlerType = messageHandlerTypes[0];
|
||||||
|
|
||||||
if (!local.containsKey(handlerType)) {
|
if (!localSubs.containsKey(handlerType)) {
|
||||||
local.put(handlerType, SUBSCRIPTIONS); // this is copied to a larger array if necessary
|
// this is copied to a larger array if necessary, but needs to be SOMETHING before subsPerListener is added
|
||||||
|
localSubs.put(handlerType, SUBSCRIPTIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the subscription. This can be thrown away if the subscription succeeds in another thread
|
// create the subscription. This can be thrown away if the subscription succeeds in another thread
|
||||||
@ -246,21 +272,25 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
|
|
||||||
// makes this subscription visible for publication
|
// makes this subscription visible for publication
|
||||||
final Subscription[] currentSubs = local.get(handlerType);
|
final Subscription[] currentSubs = localSubs.get(handlerType);
|
||||||
final int currentLength = currentSubs.length;
|
final int currentLength = currentSubs.length;
|
||||||
|
|
||||||
// add the new subscription to the beginning of the array
|
// add the new subscription to the beginning of the array
|
||||||
final Subscription[] newSubs = new Subscription[currentLength + 1];
|
final Subscription[] newSubs = new Subscription[currentLength + 1];
|
||||||
newSubs[0] = subscription;
|
newSubs[0] = subscription;
|
||||||
System.arraycopy(currentSubs, 0, newSubs, 1, currentLength);
|
System.arraycopy(currentSubs, 0, newSubs, 1, currentLength);
|
||||||
local.put(handlerType, newSubs);
|
localSubs.put(handlerType, newSubs);
|
||||||
|
|
||||||
// update the super types
|
// update the varity/super types
|
||||||
registerSuperSubs(handlerType, local);
|
registerExtraSubs(handlerType, localSubs, localSuperSubs, localVaritySubs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// save this snapshot back to the original (single writer principle)
|
// save this snapshot back to the original (single writer principle)
|
||||||
subsPerMessageSingleREF.lazySet(this, local);
|
subsSingleREF.lazySet(this, localSubs);
|
||||||
|
subsSuperSingleREF.lazySet(this, localSuperSubs);
|
||||||
|
subsVaritySingleREF.lazySet(this, localVaritySubs);
|
||||||
|
subsSuperVaritySingleREF.lazySet(this, localSuperVaritySubs);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// subscriptions already exist and must only be updated
|
// subscriptions already exist and must only be updated
|
||||||
@ -276,15 +306,12 @@ class SubscriptionManager {
|
|||||||
void unsubscribe(final Object listener) {
|
void unsubscribe(final Object listener) {
|
||||||
final Class<?> listenerClass = listener.getClass();
|
final Class<?> listenerClass = listener.getClass();
|
||||||
|
|
||||||
if (this.nonListeners.containsKey(listenerClass)) {
|
if (nonListeners.containsKey(listenerClass)) {
|
||||||
// early reject of known classes that do not define message handlers
|
// early reject of known classes that do not define message handlers
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// these are concurrent collections
|
final Subscription[] subscriptions = subsPerListener.get(listenerClass);
|
||||||
clear();
|
|
||||||
|
|
||||||
final Subscription[] subscriptions = this.subsPerListener.get(listenerClass);
|
|
||||||
if (subscriptions != null) {
|
if (subscriptions != null) {
|
||||||
Subscription subscription;
|
Subscription subscription;
|
||||||
|
|
||||||
@ -296,30 +323,51 @@ class SubscriptionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private
|
private
|
||||||
void registerSuperSubs(final Class<?> clazz, final IdentityMap<Class<?>, Subscription[]> subsPerMessageSingle) {
|
void registerExtraSubs(final Class<?> clazz,
|
||||||
|
final IdentityMap<Class<?>, Subscription[]> subsPerMessageSingle,
|
||||||
|
final IdentityMap<Class<?>, Subscription[]> subsPerSuperMessageSingle,
|
||||||
|
final IdentityMap<Class<?>, Subscription[]> subsPerVarityMessageSingle) {
|
||||||
|
|
||||||
|
final Class<?> arrayVersion = this.classUtils.getArrayClass(clazz); // never returns null, cached response
|
||||||
final Class<?>[] superClasses = this.classUtils.getSuperClasses(clazz); // never returns null, cached response
|
final Class<?>[] superClasses = this.classUtils.getSuperClasses(clazz); // never returns null, cached response
|
||||||
|
|
||||||
// access a snapshot of the subscriptions (single-writer-principle)
|
|
||||||
final IdentityMap<Class<?>, Subscription[]> local = subsPerSuperMessageSingleREF.get(this);
|
|
||||||
|
|
||||||
// types was not empty, so collect subscriptions for each type and collate them
|
|
||||||
// save the subscriptions
|
|
||||||
|
|
||||||
Class<?> superClass;
|
|
||||||
Subscription[] superSubs;
|
|
||||||
Subscription sub;
|
Subscription sub;
|
||||||
|
|
||||||
|
// Register Varity (Var-Arg) subscriptions
|
||||||
|
final Subscription[] arraySubs = subsPerMessageSingle.get(arrayVersion);
|
||||||
|
if (arraySubs != null) {
|
||||||
|
final int length = arraySubs.length;
|
||||||
|
final ArrayList<Subscription> varArgSubsAsList = new ArrayList<Subscription>(length);
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
sub = arraySubs[i];
|
||||||
|
|
||||||
|
if (sub.getHandler().acceptsVarArgs()) {
|
||||||
|
varArgSubsAsList.add(sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final int size = varArgSubsAsList.size();
|
||||||
|
if (size > 0) {
|
||||||
|
Subscription[] varArgSubs = new Subscription[size];
|
||||||
|
varArgSubsAsList.toArray(varArgSubs);
|
||||||
|
subsPerVarityMessageSingle.put(clazz, varArgSubs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Register SuperClass subscriptions
|
||||||
final int length = superClasses.length;
|
final int length = superClasses.length;
|
||||||
int superSubLength;
|
|
||||||
final ArrayList<Subscription> subsAsList = new ArrayList<Subscription>(length);
|
final ArrayList<Subscription> subsAsList = new ArrayList<Subscription>(length);
|
||||||
|
|
||||||
// walks through all of the subscriptions that might exist for super types, and if applicable, save them
|
// walks through all of the subscriptions that might exist for super types, and if applicable, save them
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
superClass = superClasses[i];
|
final Class<?> superClass = superClasses[i];
|
||||||
superSubs = subsPerMessageSingle.get(superClass);
|
final Subscription[] superSubs = subsPerMessageSingle.get(superClass);
|
||||||
|
|
||||||
if (superSubs != null) {
|
if (superSubs != null) {
|
||||||
superSubLength = superSubs.length;
|
int superSubLength = superSubs.length;
|
||||||
for (int j = 0; j < superSubLength; j++) {
|
for (int j = 0; j < superSubLength; j++) {
|
||||||
sub = superSubs[j];
|
sub = superSubs[j];
|
||||||
|
|
||||||
@ -332,13 +380,14 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
final int size = subsAsList.size();
|
final int size = subsAsList.size();
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
|
// save the subscriptions
|
||||||
Subscription[] subs = new Subscription[size];
|
Subscription[] subs = new Subscription[size];
|
||||||
subsAsList.toArray(subs);
|
subsAsList.toArray(subs);
|
||||||
local.put(clazz, subs);
|
subsPerSuperMessageSingle.put(clazz, subs);
|
||||||
|
|
||||||
// save this snapshot back to the original (single writer principle)
|
|
||||||
subsPerSuperMessageSingleREF.lazySet(this, local);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -366,12 +415,6 @@ class SubscriptionManager {
|
|||||||
return varArgUtils;
|
return varArgUtils;
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
|
||||||
void clear() {
|
|
||||||
// this.subUtils.clear();
|
|
||||||
// this.varArgUtils.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// inside a write lock
|
// inside a write lock
|
||||||
// add this subscription to each of the handled types
|
// add this subscription to each of the handled types
|
||||||
// to activate this sub for publication
|
// to activate this sub for publication
|
||||||
@ -448,16 +491,14 @@ class SubscriptionManager {
|
|||||||
// can return null
|
// can return null
|
||||||
public
|
public
|
||||||
Subscription[] getExact(final Class<?> messageClass) {
|
Subscription[] getExact(final Class<?> messageClass) {
|
||||||
return (Subscription[]) subsPerMessageSingleREF.get(this).get(messageClass);
|
return (Subscription[]) subsSingleREF.get(this).get(messageClass);
|
||||||
// return subsPerMessageSingle.get(messageClass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// can return null
|
// can return null
|
||||||
public
|
public
|
||||||
Subscription[] getSuperExactAsArray(final Class<?> messageClass) {
|
Subscription[] getSuperExactAsArray(final Class<?> messageClass) {
|
||||||
// whenever our subscriptions change, this map is cleared.
|
// whenever our subscriptions change, this map is cleared.
|
||||||
return (Subscription[]) subsPerSuperMessageSingleREF.get(this).get(messageClass);
|
return (Subscription[]) subsSuperSingleREF.get(this).get(messageClass);
|
||||||
// return this.subsPerMessageSuperSingle.get(messageClass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,8 +32,8 @@ class ClassUtils {
|
|||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
ClassUtils(final float loadFactor) {
|
ClassUtils(final float loadFactor) {
|
||||||
this.arrayCache = new IdentityMap<Class<?>, Class<?>>(32);
|
this.arrayCache = new IdentityMap<Class<?>, Class<?>>(32, loadFactor);
|
||||||
this.superClassesCache = new IdentityMap<Class<?>, Class<?>[]>(32);
|
this.superClassesCache = new IdentityMap<Class<?>, Class<?>[]>(32, loadFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
*/
|
*/
|
||||||
package dorkbox.util.messagebus.utils;
|
package dorkbox.util.messagebus.utils;
|
||||||
|
|
||||||
|
import com.esotericsoftware.kryo.util.IdentityMap;
|
||||||
import dorkbox.util.messagebus.annotations.Handler;
|
import dorkbox.util.messagebus.annotations.Handler;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
@ -44,7 +45,6 @@ import java.lang.reflect.AnnotatedElement;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author bennidi
|
* @author bennidi
|
||||||
@ -153,7 +153,6 @@ class ReflectionUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches for an Annotation of the given type on the class. Supports meta annotations.
|
* Searches for an Annotation of the given type on the class. Supports meta annotations.
|
||||||
*
|
*
|
||||||
@ -163,11 +162,11 @@ class ReflectionUtils {
|
|||||||
* @return Annotation instance or null
|
* @return Annotation instance or null
|
||||||
*/
|
*/
|
||||||
private static
|
private static
|
||||||
<A extends Annotation> A getAnnotation(AnnotatedElement from, Class<A> annotationType, Collection<AnnotatedElement> visited) {
|
<A extends Annotation> A getAnnotation(AnnotatedElement from, Class<A> annotationType, IdentityMap<AnnotatedElement, Boolean> visited) {
|
||||||
if (visited.contains(from)) {
|
if (visited.containsKey(from)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visited.add(from);
|
visited.put(from, Boolean.TRUE);
|
||||||
A ann = from.getAnnotation(annotationType);
|
A ann = from.getAnnotation(annotationType);
|
||||||
if (ann != null) {
|
if (ann != null) {
|
||||||
return ann;
|
return ann;
|
||||||
@ -183,7 +182,7 @@ class ReflectionUtils {
|
|||||||
|
|
||||||
public static
|
public static
|
||||||
<A extends Annotation> A getAnnotation(AnnotatedElement from, Class<A> annotationType) {
|
<A extends Annotation> A getAnnotation(AnnotatedElement from, Class<A> annotationType) {
|
||||||
A annotation = getAnnotation(from, annotationType, new HashSet<AnnotatedElement>(16));
|
A annotation = getAnnotation(from, annotationType, new IdentityMap<AnnotatedElement, Boolean>());
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user