]> granicus.if.org Git - gc/commitdiff
Eliminate most of collisions in GC_threads on Linux/x64
authorIvan Maidanski <ivmai@mail.ru>
Thu, 24 Aug 2017 08:34:05 +0000 (11:34 +0300)
committerIvan Maidanski <ivmai@mail.ru>
Thu, 28 Sep 2017 21:53:48 +0000 (00:53 +0300)
(Cherry-pick commits 85fce54e897b41 from 'master' branch.)

On some targets (e.g., Linux/x64) pthread_t values always have zero
in the lowest byte (e.g. if pthread_t is actually a page-aligned
pointer) thus causing all the created thread descriptors to have
a collision at GC_threads[0].  This commit mixes 3 lowest bytes
of thread id with a XOR operation (thus greatly reduces the
probability of collisions in GC_threads).
Windows thread Id rarely has non-zero highest half of DWORD, so
it is OK to use only the lowest one for hash value computation.

* include/private/pthread_support.h (THREAD_TABLE_SZ): Do not
define if already defined; refine comment.
* pthread_support.c (THREAD_TABLE_INDEX): New macro (similar to that
in win32_threads.c).
* pthread_support.c (GC_new_thread, GC_delete_thread,
GC_delete_gc_thread, GC_lookup_thread): Replace
NUMERIC_THREAD_ID(id)%THREAD_TABLE_SZ to THREAD_TABLE_INDEX(id).
* pthread_support.c (GC_lookup_thread): Remove hv local variable.
* win32_threads.c (THREAD_TABLE_INDEX): Replace (id>>2) to
((id>>8)^id).
* win32_threads.c [GC_PTHREADS] (GC_lookup_pthread): Define win32_id
local variable to pass it to THREAD_TABLE_INDEX() instead of passing
GET_PTHREAD_MAP_CACHE(id) directly.

include/private/pthread_support.h
pthread_support.c
win32_threads.c

index f835fa3c92384ef66dc5c3b0eeecd66afbc8149a..4505453e264b90f4b5102e1b34a3f84644adf283 100644 (file)
@@ -118,7 +118,9 @@ typedef struct GC_Thread_Rep {
 #   endif
 } * GC_thread;
 
-# define THREAD_TABLE_SZ 256    /* Must be power of 2   */
+#ifndef THREAD_TABLE_SZ
+# define THREAD_TABLE_SZ 256    /* Power of 2 (for speed). */
+#endif
 GC_EXTERN volatile GC_thread GC_threads[THREAD_TABLE_SZ];
 
 GC_EXTERN GC_bool GC_thr_initialized;
index c9ef9599dec58c8ecc14e6411f9bffe8e339a251..c4d5a3f6b958f5d8ba31d0a3d00acf4876251b7c 100644 (file)
@@ -528,17 +528,22 @@ void GC_push_thread_structures(void)
 /* It may not be safe to allocate when we register the first thread.    */
 static struct GC_Thread_Rep first_thread;
 
+#define THREAD_TABLE_INDEX(id) \
+                (int)(((NUMERIC_THREAD_ID(id) >> 16) \
+                       ^ (NUMERIC_THREAD_ID(id) >> 8) \
+                       ^ NUMERIC_THREAD_ID(id)) % THREAD_TABLE_SZ)
+
 /* Add a thread to GC_threads.  We assume it wasn't already there.      */
 /* Caller holds allocation lock.                                        */
 STATIC GC_thread GC_new_thread(pthread_t id)
 {
-    int hv = NUMERIC_THREAD_ID(id) % THREAD_TABLE_SZ;
+    int hv = THREAD_TABLE_INDEX(id);
     GC_thread result;
     static GC_bool first_thread_used = FALSE;
+
 #   ifdef DEBUG_THREADS
         GC_log_printf("Creating thread %p\n", (void *)id);
 #   endif
-
     GC_ASSERT(I_HOLD_LOCK());
     if (!EXPECT(first_thread_used, TRUE)) {
         result = &first_thread;
@@ -567,7 +572,7 @@ STATIC GC_thread GC_new_thread(pthread_t id)
 /* It is safe to delete the main thread.                        */
 STATIC void GC_delete_thread(pthread_t id)
 {
-    int hv = NUMERIC_THREAD_ID(id) % THREAD_TABLE_SZ;
+    int hv = THREAD_TABLE_INDEX(id);
     register GC_thread p = GC_threads[hv];
     register GC_thread prev = 0;
 
@@ -606,7 +611,7 @@ STATIC void GC_delete_thread(pthread_t id)
 STATIC void GC_delete_gc_thread(GC_thread t)
 {
     pthread_t id = t -> id;
-    int hv = NUMERIC_THREAD_ID(id) % THREAD_TABLE_SZ;
+    int hv = THREAD_TABLE_INDEX(id);
     register GC_thread p = GC_threads[hv];
     register GC_thread prev = 0;
 
@@ -639,8 +644,7 @@ STATIC void GC_delete_gc_thread(GC_thread t)
 /* return the most recent one.                                  */
 GC_INNER GC_thread GC_lookup_thread(pthread_t id)
 {
-    int hv = NUMERIC_THREAD_ID(id) % THREAD_TABLE_SZ;
-    register GC_thread p = GC_threads[hv];
+    GC_thread p = GC_threads[THREAD_TABLE_INDEX(id)];
 
     while (p != 0 && !THREAD_EQUAL(p -> id, id)) p = p -> next;
     return(p);
index 4f2ae0728398afaa9f481d76ef415db4f0c6b1e0..e234a19cc6bd4d1c892bb3cbba3c88d8c1f12348 100644 (file)
@@ -321,7 +321,7 @@ STATIC volatile LONG GC_max_thread_index = 0;
 # define THREAD_TABLE_SZ 256    /* Power of 2 (for speed). */
 #endif
 #define THREAD_TABLE_INDEX(id) /* id is of DWORD type */ \
-                (int)(((id) >> 2) % THREAD_TABLE_SZ)
+                (int)((((id) >> 8) ^ (id)) % THREAD_TABLE_SZ)
 STATIC GC_thread GC_threads[THREAD_TABLE_SZ];
 
 /* It may not be safe to allocate when we register the first thread.    */
@@ -987,7 +987,8 @@ GC_API void * GC_CALL GC_call_with_gc_active(GC_fn_type fn,
     /* else */ {
       /* We first try the cache.  If that fails, we use a very slow     */
       /* approach.                                                      */
-      int hv_guess = THREAD_TABLE_INDEX(GET_PTHREAD_MAP_CACHE(id));
+      DWORD win32_id = GET_PTHREAD_MAP_CACHE(id);
+      int hv_guess = THREAD_TABLE_INDEX(win32_id);
       int hv;
       GC_thread p;
       DCL_LOCK_STATE;