WIP - getting system to arrays instead of lists

This commit is contained in:
nathan 2016-01-20 01:56:39 +01:00
parent 0401a6a164
commit 9e06d16855
4 changed files with 132 additions and 134 deletions

View File

@ -27,7 +27,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Permits subscriptions with a varying length of parameters as the signature, which must be match by the publisher for it to be accepted
@ -67,7 +66,7 @@ class SubscriptionManager {
// all subscriptions per message type. We perpetually KEEP the types, as this lowers the amount of locking required
// this is the primary list for dispatching a specific message
// write access is synchronized and happens only when a listener of a specific class is registered the first time
final ConcurrentMap<Class<?>, List<Subscription>> subscriptionsPerMessageSingle;
final ConcurrentMap<Class<?>, Subscription[]> subscriptionsPerMessageSingle;
private final HashMapTree<Class<?>, ArrayList<Subscription>> subscriptionsPerMessageMulti;
// shortcut publication if we know there is no possibility of varArg (ie: a method that has an array as arguments)
@ -89,7 +88,7 @@ class SubscriptionManager {
this.nonListeners = new ConcurrentHashMap<Class<?>, Boolean>(4, LOAD_FACTOR, numberOfThreads);
subscriptionsPerListener = new ConcurrentHashMap<Class<?>, Subscription[]>(32, LOAD_FACTOR, numberOfThreads);
subscriptionsPerMessageSingle = new ConcurrentHashMap<Class<?>, List<Subscription>>(32, LOAD_FACTOR, numberOfThreads);
subscriptionsPerMessageSingle = new ConcurrentHashMap<Class<?>, Subscription[]>(32, LOAD_FACTOR, numberOfThreads);
this.subscriptionsPerMessageMulti = new HashMapTree<Class<?>, ArrayList<Subscription>>();
@ -180,7 +179,7 @@ class SubscriptionManager {
Class<?> handlerType;
// create the subscriptions
final ConcurrentMap<Class<?>, List<Subscription>> subsPerMessageSingle = this.subscriptionsPerMessageSingle;
final ConcurrentMap<Class<?>, Subscription[]> subsPerMessageSingle = this.subscriptionsPerMessageSingle;
subscriptions = new Subscription[handlersSize];
for (int i = 0; i < handlersSize; i++) {
@ -198,7 +197,7 @@ class SubscriptionManager {
handlerType = messageHandlerTypes[0];
if (!subsPerMessageSingle.containsKey(handlerType)) {
subsPerMessageSingle.put(handlerType, new CopyOnWriteArrayList<Subscription>());
subsPerMessageSingle.put(handlerType, new Subscription[0]);
}
@ -220,6 +219,9 @@ class SubscriptionManager {
} else {
// we can now safely add for publication AND subscribe since the data structures are consistent
for (int i = 0; i < handlersSize; i++) {
// register the super types/varity types
subUtils.register(listenerClass, this);
subscription = subscriptions[i];
subscription.subscribe(listener); // register this callback listener to this subscription
@ -231,7 +233,14 @@ class SubscriptionManager {
handlerType = messageHandlerTypes[0];
// makes this subscription visible for publication
subsPerMessageSingle.get(handlerType).add(subscription);
final Subscription[] currentSubs = subsPerMessageSingle.get(handlerType);
final int currentLength = currentSubs.length;
// add the new subscription to the beginning of the array
final Subscription[] newSubs = new Subscription[currentLength + 1];
newSubs[0] = subscription;
System.arraycopy(currentSubs, 0, newSubs, 1, currentLength);
subsPerMessageSingle.put(handlerType, newSubs);
}
return;
@ -362,7 +371,7 @@ class SubscriptionManager {
public
List<Subscription> getExactAsArray(final Class<?> messageClass) {
Subscription[] getExactAsArray(final Class<?> messageClass) {
return subscriptionsPerMessageSingle.get(messageClass);
}
@ -379,17 +388,7 @@ class SubscriptionManager {
// can return null
public
Subscription[] getExact(final Class<?> messageClass) {
final List<Subscription> collection = getExactAsArray(messageClass);
if (collection != null) {
// convert to Array because the subscriptions can change and we want safe iteration over the list
final Subscription[] subscriptions = new Subscription[collection.size()];
collection.toArray(subscriptions);
return subscriptions;
}
return null;
return getExactAsArray(messageClass);
}
// can return null
@ -423,32 +422,26 @@ class SubscriptionManager {
return null;
}
// can return null
// can NOT return null
public
Subscription[] getExactAndSuper(final Class<?> messageClass) {
List<Subscription> collection = getExactAsArray(messageClass); // can return null
Subscription[] collection = getExactAsArray(messageClass); // can return null
// now publish superClasses
final ArrayList<Subscription> superSubscriptions = this.subUtils.getSuperSubscriptions(messageClass, this); // NOT return null
final Subscription[] superSubscriptions = this.subUtils.getSuperSubscriptions(messageClass, this); // NOT return null
if (collection != null) {
collection = new ArrayList<Subscription>(collection);
final int length = collection.length;
final int lengthSuper = superSubscriptions.length;
if (!superSubscriptions.isEmpty()) {
collection.addAll(superSubscriptions);
}
}
else if (!superSubscriptions.isEmpty()) {
collection = superSubscriptions;
}
final Subscription[] newSubs = new Subscription[length + lengthSuper];
System.arraycopy(collection, 0, newSubs, 0, length);
System.arraycopy(superSubscriptions, 0, newSubs, length, lengthSuper);
if (collection != null) {
final Subscription[] subscriptions = new Subscription[collection.size()];
collection.toArray(subscriptions);
return subscriptions;
return newSubs;
}
else {
return null;
return superSubscriptions;
}
}

View File

@ -95,15 +95,18 @@ class ClassUtils {
* race conditions will result in duplicate answers, which we don't care if happens
* never returns null
* never resets
*
* https://bugs.openjdk.java.net/browse/JDK-6525802 (fixed this in 2007, so Array.newInstance is just as fast (via intrinsics) new [])
*/
public
Class<?> getArrayClass(final Class<?> c) {
// this is never reset, since it never needs to be.
final Map<Class<?>, Class<?>> cache = this.arrayCache;
Class<?> clazz = cache.get(c);
if (clazz == null) {
// messy, but the ONLY way to do it. Array super types are also arrays
final Object[] newInstance = (Object[]) Array.newInstance(c, 1);
final Object[] newInstance = (Object[]) Array.newInstance(c, 0);
clazz = newInstance.getClass();
cache.put(c, clazz);
}

View File

@ -20,7 +20,6 @@ import dorkbox.util.messagebus.subscription.Subscription;
import dorkbox.util.messagebus.subscription.SubscriptionManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ -31,7 +30,7 @@ class SubscriptionUtils {
// superClassSubscriptions keeps track of all subscriptions of super classes. SUB/UNSUB dumps it, so it is recreated dynamically.
// it's a hit on SUB/UNSUB, but REALLY improves performance on handlers
// it's faster to create a new one for SUB/UNSUB than it is to shutdown() on the original one
private final Map<Class<?>, ArrayList<Subscription>> superClassSubscriptions;
private final Map<Class<?>, Subscription[]> superClassSubscriptions;
private final HashMapTree<Class<?>, ArrayList<Subscription>> superClassSubscriptionsMulti;
@ -42,7 +41,7 @@ class SubscriptionUtils {
// superClassSubscriptions keeps track of all subscriptions of super classes. SUB/UNSUB dumps it, so it is recreated dynamically.
// it's a hit on SUB/UNSUB, but improves performance of handlers
this.superClassSubscriptions = new ConcurrentHashMap<Class<?>, ArrayList<Subscription>>(8, loadFactor, numberOfThreads);
this.superClassSubscriptions = new ConcurrentHashMap<Class<?>, Subscription[]>(8, loadFactor, numberOfThreads);
this.superClassSubscriptionsMulti = new HashMapTree<Class<?>, ArrayList<Subscription>>();
}
@ -52,6 +51,46 @@ class SubscriptionUtils {
this.superClassSubscriptionsMulti.clear();
}
// ALWAYS register and create a cached version of the requested class + superClasses
public
Subscription[] register(final Class<?> clazz, final SubscriptionManager subManager) {
final Map<Class<?>, Subscription[]> local = this.superClassSubscriptions;
// types was not empty, so collect subscriptions for each type and collate them
// save the subscriptions
final Class<?>[] superClasses = this.superClass.getSuperClasses(clazz); // never returns null, cached response
Class<?> superClass;
Subscription[] superSubs;
Subscription sub;
final int length = superClasses.length;
int superSubLength;
final ArrayList<Subscription> subsAsList = new ArrayList<Subscription>(length);
for (int i = 0; i < length; i++) {
superClass = superClasses[i];
superSubs = subManager.getExactAsArray(superClass);
if (superSubs != null) {
superSubLength = superSubs.length;
for (int j = 0; j < superSubLength; j++) {
sub = superSubs[j];
if (sub.getHandler().acceptsSubtypes()) {
subsAsList.add(sub);
}
}
}
}
Subscription[] subs = new Subscription[subsAsList.size()];
subsAsList.toArray(subs);
local.put(clazz, subs);
return subs;
}
/**
* Returns an array COPY of the super subscriptions for the specified type.
@ -61,47 +100,16 @@ class SubscriptionUtils {
* @return CAN NOT RETURN NULL
*/
public
ArrayList<Subscription> getSuperSubscriptions(final Class<?> clazz, final SubscriptionManager subManager) {
Subscription[] getSuperSubscriptions(final Class<?> clazz, final SubscriptionManager subManager) {
// whenever our subscriptions change, this map is cleared.
final Map<Class<?>, ArrayList<Subscription>> local = this.superClassSubscriptions;
final Map<Class<?>, Subscription[]> local = this.superClassSubscriptions;
Subscription[] subs = local.get(clazz);
ArrayList<Subscription> subs = local.get(clazz);
if (subs == null) {
// types was not empty, so collect subscriptions for each type and collate them
// save the subscriptions
final Class<?>[] superClasses = this.superClass.getSuperClasses(clazz); // never returns null, cached response
Class<?> superClass;
List<Subscription> superSubs;
Subscription sub;
final int length = superClasses.length;
int superSubLength;
subs = new ArrayList<Subscription>(length);
for (int i = 0; i < length; i++) {
superClass = superClasses[i];
superSubs = subManager.getExactAsArray(superClass);
if (superSubs != null) {
superSubLength = superSubs.size();
for (int j = 0; j < superSubLength; j++) {
sub = superSubs.get(j);
if (sub.getHandler().acceptsSubtypes()) {
subs.add(sub);
}
}
}
}
subs.trimToSize();
local.put(clazz, subs);
if (subs != null) {
return subs;
}
return subs;
return register(clazz, subManager);
}
/**

View File

@ -21,16 +21,15 @@ import dorkbox.util.messagebus.subscription.Subscription;
import dorkbox.util.messagebus.subscription.SubscriptionManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public final
class VarArgUtils {
private final Map<Class<?>, ArrayList<Subscription>> varArgSubscriptionsSingle;
private final Map<Class<?>, Subscription[]> varArgSubscriptionsSingle;
private final HashMapTree<Class<?>, ArrayList<Subscription>> varArgSubscriptionsMulti;
private final Map<Class<?>, ArrayList<Subscription>> varArgSuperSubscriptionsSingle;
private final Map<Class<?>, Subscription[]> varArgSuperSubscriptionsSingle;
private final HashMapTree<Class<?>, ArrayList<Subscription>> varArgSuperSubscriptionsMulti;
private final ClassUtils superClassUtils;
@ -41,10 +40,10 @@ class VarArgUtils {
this.superClassUtils = superClassUtils;
this.varArgSubscriptionsSingle = new ConcurrentHashMap<Class<?>, ArrayList<Subscription>>(16, loadFactor, numberOfThreads);
this.varArgSubscriptionsSingle = new ConcurrentHashMap<Class<?>, Subscription[]>(16, loadFactor, numberOfThreads);
this.varArgSubscriptionsMulti = new HashMapTree<Class<?>, ArrayList<Subscription>>();
this.varArgSuperSubscriptionsSingle = new ConcurrentHashMap<Class<?>, ArrayList<Subscription>>(16, loadFactor, numberOfThreads);
this.varArgSuperSubscriptionsSingle = new ConcurrentHashMap<Class<?>, Subscription[]>(16, loadFactor, numberOfThreads);
this.varArgSuperSubscriptionsMulti = new HashMapTree<Class<?>, ArrayList<Subscription>>();
}
@ -65,38 +64,39 @@ class VarArgUtils {
public
Subscription[] getVarArgSubscriptions(final Class<?> messageClass, final SubscriptionManager subManager) {
// whenever our subscriptions change, this map is cleared.
final Map<Class<?>, ArrayList<Subscription>> local = this.varArgSubscriptionsSingle;
final Map<Class<?>, Subscription[]> local = this.varArgSubscriptionsSingle;
ArrayList<Subscription> varArgSubs = local.get(messageClass);
Subscription[] varArgSubs = local.get(messageClass);
if (varArgSubs == null) {
// this gets (and caches) our array type. This is never cleared.
final Class<?> arrayVersion = this.superClassUtils.getArrayClass(messageClass);
final List<Subscription> subs = subManager.getExactAsArray(arrayVersion);
final Subscription[] subs = subManager.getExactAsArray(arrayVersion);
if (subs != null) {
final int length = subs.size();
varArgSubs = new ArrayList<Subscription>(length);
final int length = subs.length;
final ArrayList<Subscription> varArgSubsAsList = new ArrayList<Subscription>(length);
Subscription sub;
for (int i = 0; i < length; i++) {
sub = subs.get(i);
sub = subs[i];
if (sub.getHandler().acceptsVarArgs()) {
varArgSubs.add(sub);
varArgSubsAsList.add(sub);
}
}
varArgSubs = new Subscription[varArgSubsAsList.size()];
varArgSubsAsList.toArray(varArgSubs);
local.put(messageClass, varArgSubs);
}
else {
varArgSubs = new ArrayList<Subscription>(0);
varArgSubs = new Subscription[0];
}
}
final Subscription[] subscriptions = new Subscription[varArgSubs.size()];
varArgSubs.toArray(subscriptions);
return subscriptions;
return varArgSubs;
}
@ -106,20 +106,10 @@ class VarArgUtils {
// and then, returns the array'd version superclass subscriptions
public
Subscription[] getVarArgSuperSubscriptions(final Class<?> messageClass, final SubscriptionManager subManager) {
final ArrayList<Subscription> subs = getVarArgSuperSubscriptions_List(messageClass, subManager);
final Subscription[] returnedSubscriptions = new Subscription[subs.size()];
subs.toArray(returnedSubscriptions);
return returnedSubscriptions;
}
// CAN NOT RETURN NULL
private
ArrayList<Subscription> getVarArgSuperSubscriptions_List(final Class<?> messageClass, final SubscriptionManager subManager) {
// whenever our subscriptions change, this map is cleared.
final Map<Class<?>, ArrayList<Subscription>> local = this.varArgSuperSubscriptionsSingle;
final Map<Class<?>, Subscription[]> local = this.varArgSuperSubscriptionsSingle;
ArrayList<Subscription> varArgSuperSubs = local.get(messageClass);
Subscription[] varArgSuperSubs = local.get(messageClass);
if (varArgSuperSubs == null) {
// this gets (and caches) our array type. This is never cleared.
@ -127,7 +117,7 @@ class VarArgUtils {
final Class<?>[] types = this.superClassUtils.getSuperClasses(arrayVersion);
final int typesLength = types.length;
varArgSuperSubs = new ArrayList<Subscription>(typesLength);
varArgSuperSubs = new Subscription[typesLength];
if (typesLength == 0) {
local.put(messageClass, varArgSuperSubs);
@ -135,9 +125,10 @@ class VarArgUtils {
}
// there are varArgs super classes for this messageClass
Class<?> type;
Subscription sub;
List<Subscription> subs;
Subscription[] subs;
int length;
MessageHandler handlerMetadata;
@ -146,20 +137,23 @@ class VarArgUtils {
subs = subManager.getExactAsArray(type);
if (subs != null) {
length = subs.size();
varArgSuperSubs = new ArrayList<Subscription>(length);
length = subs.length;
final ArrayList<Subscription> varArgSuperSubsAsList = new ArrayList<Subscription>(length);
for (int j = 0; j < length; j++) {
sub = subs.get(j);
sub = subs[j];
handlerMetadata = sub.getHandler();
if (handlerMetadata.acceptsSubtypes() && handlerMetadata.acceptsVarArgs()) {
varArgSuperSubs.add(sub);
varArgSuperSubsAsList.add(sub);
}
}
varArgSuperSubs = new Subscription[varArgSuperSubsAsList.size()];
varArgSuperSubsAsList.toArray(varArgSuperSubs);
}
else {
varArgSuperSubs = new ArrayList<Subscription>(0);
varArgSuperSubs = new Subscription[0];
}
}
@ -180,17 +174,17 @@ class VarArgUtils {
ArrayList<Subscription> subs = local.get(messageClass1, messageClass2);
if (subs == null) {
// the message class types are not the same, so look for a common superClass varArg subscription.
// this is to publish to object[] (or any class[]) handler that is common among all superTypes of the messages
final ArrayList<Subscription> varargSuperSubscriptions1 = getVarArgSuperSubscriptions_List(messageClass1, subManager);
final ArrayList<Subscription> varargSuperSubscriptions2 = getVarArgSuperSubscriptions_List(messageClass2, subManager);
subs = ClassUtils.findCommon(varargSuperSubscriptions1, varargSuperSubscriptions2);
subs.trimToSize();
local.put(subs, messageClass1, messageClass2);
}
// if (subs == null) {
// // the message class types are not the same, so look for a common superClass varArg subscription.
// // this is to publish to object[] (or any class[]) handler that is common among all superTypes of the messages
// final ArrayList<Subscription> varargSuperSubscriptions1 = getVarArgSuperSubscriptions_List(messageClass1, subManager);
// final ArrayList<Subscription> varargSuperSubscriptions2 = getVarArgSuperSubscriptions_List(messageClass2, subManager);
//
// subs = ClassUtils.findCommon(varargSuperSubscriptions1, varargSuperSubscriptions2);
//
// subs.trimToSize();
// local.put(subs, messageClass1, messageClass2);
// }
final Subscription[] returnedSubscriptions = new Subscription[subs.size()];
subs.toArray(returnedSubscriptions);
@ -209,19 +203,19 @@ class VarArgUtils {
ArrayList<Subscription> subs = local.get(messageClass1, messageClass2, messageClass3);
if (subs == null) {
// the message class types are not the same, so look for a common superClass varArg subscription.
// this is to publish to object[] (or any class[]) handler that is common among all superTypes of the messages
final ArrayList<Subscription> varargSuperSubscriptions1 = getVarArgSuperSubscriptions_List(messageClass1, subManager);
final ArrayList<Subscription> varargSuperSubscriptions2 = getVarArgSuperSubscriptions_List(messageClass2, subManager);
final ArrayList<Subscription> varargSuperSubscriptions3 = getVarArgSuperSubscriptions_List(messageClass3, subManager);
subs = ClassUtils.findCommon(varargSuperSubscriptions1, varargSuperSubscriptions2);
subs = ClassUtils.findCommon(subs, varargSuperSubscriptions3);
subs.trimToSize();
local.put(subs, messageClass1, messageClass2, messageClass3);
}
// if (subs == null) {
// // the message class types are not the same, so look for a common superClass varArg subscription.
// // this is to publish to object[] (or any class[]) handler that is common among all superTypes of the messages
// final ArrayList<Subscription> varargSuperSubscriptions1 = getVarArgSuperSubscriptions_List(messageClass1, subManager);
// final ArrayList<Subscription> varargSuperSubscriptions2 = getVarArgSuperSubscriptions_List(messageClass2, subManager);
// final ArrayList<Subscription> varargSuperSubscriptions3 = getVarArgSuperSubscriptions_List(messageClass3, subManager);
//
// subs = ClassUtils.findCommon(varargSuperSubscriptions1, varargSuperSubscriptions2);
// subs = ClassUtils.findCommon(subs, varargSuperSubscriptions3);
//
// subs.trimToSize();
// local.put(subs, messageClass1, messageClass2, messageClass3);
// }
final Subscription[] returnedSubscriptions = new Subscription[subs.size()];
subs.toArray(returnedSubscriptions);