GREAT performance. Added IdentityMap to ReflectionUtils annotations collector

This commit is contained in:
nathan 2016-01-20 13:55:39 +01:00
parent 61756547bb
commit 61f7cd26b1
3 changed files with 106 additions and 66 deletions

View File

@ -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);
} }

View File

@ -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);
} }
/** /**

View File

@ -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;
} }