summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Eilebrecht <ericeil@users.noreply.github.com>2015-09-26 10:54:11 -0700
committerEric Eilebrecht <ericeil@users.noreply.github.com>2015-09-26 10:54:11 -0700
commit795786f0ac0e5ea82f288dfeb5b673845e1ef075 (patch)
tree40966913e314482763d53bea035bf1aa0e1adb08
parent4f25a0f321d7d75af994223771c12d461602b6c9 (diff)
parentafb78b146b5429f11817688c5277875ff5071021 (diff)
downloadcoreclr-795786f0ac0e5ea82f288dfeb5b673845e1ef075.tar.gz
coreclr-795786f0ac0e5ea82f288dfeb5b673845e1ef075.tar.bz2
coreclr-795786f0ac0e5ea82f288dfeb5b673845e1ef075.zip
Merge pull request #1629 from ericeil/AsyncLocalPerf
Reduce allocations in AsyncLocal/ExecutionContext 1: Allocate a new AsyncLocal change notification list only if we need to add an item to the list. 2: Pre-size any collections we allocate, and manually copy into them to avoid IEnumerator allocations, etc. 3: Store the change notification list in an array, rather than a List, to avoid the extra List allocation.
-rw-r--r--src/mscorlib/src/System/Threading/ExecutionContext.cs41
1 files changed, 31 insertions, 10 deletions
diff --git a/src/mscorlib/src/System/Threading/ExecutionContext.cs b/src/mscorlib/src/System/Threading/ExecutionContext.cs
index 3fc1e14908..94308118bc 100644
--- a/src/mscorlib/src/System/Threading/ExecutionContext.cs
+++ b/src/mscorlib/src/System/Threading/ExecutionContext.cs
@@ -59,18 +59,18 @@ namespace System.Threading
static ExecutionContext t_currentMaybeNull;
private readonly Dictionary<IAsyncLocal, object> m_localValues;
- private readonly List<IAsyncLocal> m_localChangeNotifications;
+ private readonly IAsyncLocal[] m_localChangeNotifications;
private ExecutionContext()
{
m_localValues = new Dictionary<IAsyncLocal, object>();
- m_localChangeNotifications = new List<IAsyncLocal>();
+ m_localChangeNotifications = Array.Empty<IAsyncLocal>();
}
- private ExecutionContext(ExecutionContext other)
+ private ExecutionContext(Dictionary<IAsyncLocal, object> localValues, IAsyncLocal[] localChangeNotifications)
{
- m_localValues = new Dictionary<IAsyncLocal, object>(other.m_localValues);
- m_localChangeNotifications = new List<IAsyncLocal>(other.m_localChangeNotifications);
+ m_localValues = localValues;
+ m_localChangeNotifications = localChangeNotifications;
}
[SecuritySafeCritical]
@@ -191,18 +191,39 @@ namespace System.Threading
if (previousValue == newValue)
return;
- current = new ExecutionContext(current);
- current.m_localValues[local] = newValue;
+ //
+ // Allocate a new Dictionary containing a copy of the old values, plus the new value. We have to do this manually to
+ // minimize allocations of IEnumerators, etc.
+ //
+ Dictionary<IAsyncLocal, object> newValues = new Dictionary<IAsyncLocal, object>(current.m_localValues.Count + (hadPreviousValue ? 0 : 1));
- t_currentMaybeNull = current;
+ foreach (KeyValuePair<IAsyncLocal, object> pair in current.m_localValues)
+ newValues.Add(pair.Key, pair.Value);
+ newValues[local] = newValue;
+
+ //
+ // Either copy the change notification array, or create a new one, depending on whether we need to add a new item.
+ //
+ IAsyncLocal[] newChangeNotifications = current.m_localChangeNotifications;
if (needChangeNotifications)
{
if (hadPreviousValue)
- Contract.Assert(current.m_localChangeNotifications.Contains(local));
+ {
+ Contract.Assert(Array.IndexOf(newChangeNotifications, local) >= 0);
+ }
else
- current.m_localChangeNotifications.Add(local);
+ {
+ int newNotificationIndex = newChangeNotifications.Length;
+ Array.Resize(ref newChangeNotifications, newNotificationIndex + 1);
+ newChangeNotifications[newNotificationIndex] = local;
+ }
+ }
+ t_currentMaybeNull = new ExecutionContext(newValues, newChangeNotifications);
+
+ if (needChangeNotifications)
+ {
local.OnValueChanged(previousValue, newValue, false);
}
}