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

Reply via email to