Hi,
the attached patch modifies ThreadLocal to use an array of Objects hung
off Thread rather than a weak hash map hung off Thread. On DaCapo's
Jython benchmark this can improve the performance of the Jikes RVM by
more than 10%. It also improves other DaCapo benchmark performance. GCJ
already has a similar mechanism to use pthread thread locals.
Regards,
Ian
diff -u java/lang/InheritableThreadLocal.java
java/lang/InheritableThreadLocal.java
--- java/lang/InheritableThreadLocal.java 2006-12-10 20:25:44.000000000
+0000
+++ java/lang/InheritableThreadLocal.java 2007-08-20 16:41:23.000000000
+0100
@@ -37,9 +37,7 @@
package java.lang;
-import gnu.java.util.WeakIdentityHashMap;
-
-import java.util.Iterator;
+import java.util.Arrays;
/**
* A ThreadLocal whose value is inherited by child Threads. The value of the
@@ -56,6 +54,7 @@
* @author Eric Blake ([EMAIL PROTECTED])
* @author Tom Tromey ([EMAIL PROTECTED])
* @author Andrew John Hughes ([EMAIL PROTECTED])
+ * @author Ian Rogers ([EMAIL PROTECTED])
* @see ThreadLocal
* @since 1.2
* @status updated to 1.4
@@ -93,29 +92,28 @@
* @param childThread the newly created thread, to inherit from this thread
* @see Thread#Thread(ThreadGroup, Runnable, String)
*/
- static void newChildThread(Thread childThread)
+ static <T> void newChildThread(Thread childThread)
{
// The currentThread is the parent of the new thread.
Thread parentThread = Thread.currentThread();
- if (parentThread.locals != null)
- {
- Iterator keys = parentThread.locals.keySet().iterator();
- while (keys.hasNext())
- {
- Object key = keys.next();
- if (key instanceof InheritableThreadLocal)
- {
- InheritableThreadLocal local = (InheritableThreadLocal)key;
- Object parentValue = parentThread.locals.get(key);
- Object childValue = local.childValue(parentValue == sentinel
- ? null : parentValue);
- if (childThread.locals == null)
- childThread.locals = new WeakIdentityHashMap();
- childThread.locals.put(key, (childValue == null
- ? sentinel : childValue));
- }
+ if (parentThread.locals != null) {
+ childThread.locals = new Object[parentThread.locals.length];
+ Arrays.fill(childThread.locals, sentinel);
+ for (int i=0; i < parentThread.locals.length; i++) {
+ ThreadLocal<?> tl = owners.get(i).get();
+ if (tl != null && tl instanceof InheritableThreadLocal) {
+ InheritableThreadLocal local = (InheritableThreadLocal)tl;
+ Object parentValue = parentThread.locals[i];
+ Object childValue;
+ if (parentValue == sentinel) {
+ childValue = sentinel;
+ } else {
+ childValue = local.childValue(parentValue);
}
+ childThread.locals[i] = childValue;
+ }
}
+ }
}
}
diff -u java/lang/Thread.java java/lang/Thread.java
--- java/lang/Thread.java 2006-12-10 23:06:55.000000000 +0000
+++ java/lang/Thread.java 2007-08-20 16:59:52.000000000 +0100
@@ -39,14 +39,11 @@
package java.lang;
import gnu.classpath.VMStackWalker;
-import gnu.java.util.WeakIdentityHashMap;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
-
import java.security.Permission;
-
import java.util.HashMap;
import java.util.Map;
@@ -90,6 +87,7 @@
* @author John Keiser
* @author Eric Blake ([EMAIL PROTECTED])
* @author Andrew John Hughes ([EMAIL PROTECTED])
+ * @author Ian Rogers ([EMAIL PROTECTED])
* @see Runnable
* @see Runtime#exit(int)
* @see #run()
@@ -156,10 +154,11 @@
/** The default exception handler. */
private static UncaughtExceptionHandler defaultHandler;
- /** Thread local storage. Package accessible for use by
- * InheritableThreadLocal.
- */
- WeakIdentityHashMap locals;
+ /** Default value for a thread with no locals. */
+ private static final Object[] noLocals = new Object[0];
+
+ /** Where thread local storage is hung by ThreadLocal. */
+ Object[] locals = noLocals;
/** The uncaught exception handler. */
UncaughtExceptionHandler exceptionHandler;
@@ -1066,20 +1065,6 @@
locals = null;
}
- /**
- * Returns the map used by ThreadLocal to store the thread local values.
- */
- static Map getThreadLocals()
- {
- Thread thread = currentThread();
- Map locals = thread.locals;
- if (locals == null)
- {
- locals = thread.locals = new WeakIdentityHashMap();
- }
- return locals;
- }
-
/**
* Assigns the given <code>UncaughtExceptionHandler</code> to this
* thread. This will then be called if the thread terminates due
diff -u java/lang/ThreadLocal.java java/lang/ThreadLocal.java
--- java/lang/ThreadLocal.java 2006-12-10 20:25:44.000000000 +0000
+++ java/lang/ThreadLocal.java 2007-08-20 17:19:18.000000000 +0100
@@ -37,8 +37,9 @@
package java.lang;
-import java.util.Map;
-
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
/**
* ThreadLocal objects have a different state associated with every
@@ -83,6 +84,7 @@
*
* @author Mark Wielaard ([EMAIL PROTECTED])
* @author Eric Blake ([EMAIL PROTECTED])
+ * @author Ian Rogers ([EMAIL PROTECTED])
* @since 1.2
* @status updated to 1.5
*/
@@ -94,12 +96,46 @@
* InheritableThreadLocal
*/
static final Object sentinel = new Object();
-
+
+ /**
+ * List of owners of thread local variables. Weak references allow thread
+ * locals to be collected when no longer in use. Package visible for use by
+ * InheritableThreadLocal
+ */
+ static final ArrayList<WeakReference<ThreadLocal<?>>> owners =
+ new ArrayList<WeakReference<ThreadLocal<?>>>();
+
+ /**
+ * Index of thread local within Thread's pool
+ */
+ private final int localIndex;
+
+ /**
+ * Allocate space within the owners for this thread local.
+ * @param the thread local we're allocating space for
+ * @return the slot to hold the thread local
+ */
+ private static int allocateThreadLocal(ThreadLocal<?> tl) {
+ synchronized(owners) {
+ int size = owners.size();
+ // Look if any slot is now available
+ for (int i=0; i < size; i++) {
+ if(owners.get(i).get() == null) {
+ return i;
+ }
+ }
+ // Add slot at end of array
+ owners.add(new WeakReference<ThreadLocal<?>>(tl));
+ return size;
+ }
+ }
+
/**
* Creates a ThreadLocal object without associating any value to it yet.
*/
public ThreadLocal()
{
+ localIndex = allocateThreadLocal(this);
}
/**
@@ -125,16 +161,21 @@
*/
public T get()
{
- Map<ThreadLocal<T>,T> map = (Map<ThreadLocal<T>,T>)
Thread.getThreadLocals();
- // Note that we don't have to synchronize, as only this thread will
- // ever modify the map.
- T value = map.get(this);
- if (value == null)
- {
- value = initialValue();
- map.put(this, (T) (value == null ? sentinel : value));
- }
- return value == (T) sentinel ? null : value;
+ Thread thread = Thread.currentThread();
+ T value;
+ try {
+ value = (T)thread.locals[localIndex];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ final int oldLength = thread.locals.length;
+ thread.locals = Arrays.copyOf(thread.locals, owners.size());
+ Arrays.fill(thread.locals, oldLength, thread.locals.length, sentinel);
+ value = (T)sentinel;
+ }
+ if (value == sentinel) {
+ value = initialValue();
+ thread.locals[localIndex] = value;
+ }
+ return value;
}
/**
@@ -147,10 +188,17 @@
*/
public void set(T value)
{
- Map map = Thread.getThreadLocals();
// Note that we don't have to synchronize, as only this thread will
// ever modify the map.
- map.put(this, value == null ? sentinel : value);
+ Thread thread = Thread.currentThread();
+ try {
+ thread.locals[localIndex] = value;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ final int oldLength = thread.locals.length;
+ thread.locals = Arrays.copyOf(thread.locals, owners.size());
+ Arrays.fill(thread.locals, oldLength, thread.locals.length, sentinel);
+ thread.locals[localIndex] = value;
+ }
}
/**
@@ -160,7 +208,6 @@
*/
public void remove()
{
- Map map = Thread.getThreadLocals();
- map.remove(this);
+ set((T)sentinel);
}
}