diff options
author | Eric Eilebrecht <ericeil@users.noreply.github.com> | 2015-09-26 10:54:11 -0700 |
---|---|---|
committer | Eric Eilebrecht <ericeil@users.noreply.github.com> | 2015-09-26 10:54:11 -0700 |
commit | 795786f0ac0e5ea82f288dfeb5b673845e1ef075 (patch) | |
tree | 40966913e314482763d53bea035bf1aa0e1adb08 | |
parent | 4f25a0f321d7d75af994223771c12d461602b6c9 (diff) | |
parent | afb78b146b5429f11817688c5277875ff5071021 (diff) | |
download | coreclr-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.cs | 41 |
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); } } |