Cache result of GetCurrentProcessorNumber
authorStephen Toub <stoub@microsoft.com>
Wed, 23 Nov 2016 01:48:53 +0000 (20:48 -0500)
committerStephen Toub <stoub@microsoft.com>
Fri, 25 Nov 2016 03:09:54 +0000 (22:09 -0500)
src/mscorlib/corefx/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.Unix.cs
src/mscorlib/corefx/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.Windows.cs
src/mscorlib/corefx/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs

index aa729e8..2a0b1f7 100644 (file)
@@ -19,7 +19,7 @@ namespace System.Buffers
                 // doesn't exist on all platforms.  On those it doesn't exist on, GetCurrentProcessorNumber
                 // returns -1.  As a fallback in that case and to spread the threads across the buckets
                 // by default, we use the current managed thread ID as a proxy.
-                int id = Win32Native.GetCurrentProcessorNumber();
+                int id = GetCurrentProcessorNumber();
                 if (id < 0) id = Environment.CurrentManagedThreadId;
                 return id;
             }
index 0c4c464..4ec158d 100644 (file)
@@ -14,7 +14,7 @@ namespace System.Buffers
         private static int ExecutionId
         {
             [MethodImpl(MethodImplOptions.AggressiveInlining)]
-            get { return Win32Native.GetCurrentProcessorNumber(); }
+            get { return GetCurrentProcessorNumber(); }
         }
     }
 }
index a4bed13..3d2b1e2 100644 (file)
@@ -46,6 +46,11 @@ namespace System.Buffers
         /// <summary>A per-thread array of arrays, to cache one array per array size per thread.</summary>
         [ThreadStatic]
         private static T[][] t_tlsBuckets;
+        /// <summary>
+        /// Cached processor number used as a hint for which per-core stack to access.
+        /// </summary>
+        [ThreadStatic]
+        private static int? t_cachedProcessorNumber;
 
         /// <summary>Initialize the pool.</summary>
         public TlsOverPerCoreLockedStacksArrayPool()
@@ -68,6 +73,19 @@ namespace System.Buffers
         /// <summary>Gets an ID for the pool to use with events.</summary>
         private int Id => GetHashCode();
 
+        /// <summary>Gets the processor number associated with the current thread.</summary>
+        /// <remarks>Uses a cached value if one exists on the current thread.</remarks>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static int GetCurrentProcessorNumber()
+        {
+            int? num = t_cachedProcessorNumber;
+            if (!num.HasValue)
+            {
+                t_cachedProcessorNumber = num = Win32Native.GetCurrentProcessorNumber();
+            }
+            return num.GetValueOrDefault();
+        }
+
         public override T[] Rent(int minimumLength)
         {
             // Arrays can't be smaller than zero.  We allow requesting zero-length arrays (even though
@@ -265,7 +283,7 @@ namespace System.Buffers
             public bool TryPush(T[] array)
             {
                 bool enqueued = false;
-                Monitor.Enter(this);
+                MonitorEnterWithProcNumberFlush(this);
                 if (_count < MaxBuffersPerArraySizePerCore)
                 {
                     _arrays[_count++] = array;
@@ -279,7 +297,7 @@ namespace System.Buffers
             public T[] TryPop()
             {
                 T[] arr = null;
-                Monitor.Enter(this);
+                MonitorEnterWithProcNumberFlush(this);
                 if (_count > 0)
                 {
                     arr = _arrays[--_count];
@@ -288,6 +306,21 @@ namespace System.Buffers
                 Monitor.Exit(this);
                 return arr;
             }
+
+            /// <summary>
+            /// Enters the monitor on the object.  If there is any contention while trying
+            /// to acquire the monitor, it flushes the cached processor number so that subsequent
+            /// attempts to access the per-core stacks will use an updated processor number.
+            /// </summary>
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            private static void MonitorEnterWithProcNumberFlush(object obj)
+            {
+                if (!Monitor.TryEnter(obj))
+                {
+                    t_cachedProcessorNumber = null;
+                    Monitor.Enter(obj);
+                }
+            }
         }
     }
 }