From 8c7274199263754561eacaad9dd9eb8e1e0eb9c2 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 4 Feb 2016 21:35:13 +0000 Subject: [PATCH] Lower alloc KeyValueAccumulator for common path --- .../KeyValueAccumulator.cs | 62 ++++++++++++++----- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/src/Microsoft.AspNetCore.WebUtilities/KeyValueAccumulator.cs b/src/Microsoft.AspNetCore.WebUtilities/KeyValueAccumulator.cs index 25032bfcd0..207ff4c36b 100644 --- a/src/Microsoft.AspNetCore.WebUtilities/KeyValueAccumulator.cs +++ b/src/Microsoft.AspNetCore.WebUtilities/KeyValueAccumulator.cs @@ -9,24 +9,55 @@ namespace Microsoft.AspNetCore.WebUtilities { public struct KeyValueAccumulator { - private Dictionary> _accumulator; + private Dictionary _accumulator; + private Dictionary> _expandingAccumulator; public void Append(string key, string value) { if (_accumulator == null) { - _accumulator = new Dictionary>(StringComparer.OrdinalIgnoreCase); + _accumulator = new Dictionary(StringComparer.OrdinalIgnoreCase); } - List values; + + StringValues values; if (_accumulator.TryGetValue(key, out values)) { - values.Add(value); + if (values.Count == 0) + { + // Marker entry for this key to indicate entry already in expanding list dictionary + _expandingAccumulator[key].Add(value); + } + else if (values.Count == 1) + { + // Second value for this key + _accumulator[key] = new string[] { values[0], value }; + } + else + { + // Third value for this key + // Add zero count entry and move to data to expanding list dictionary + _accumulator[key] = default(StringValues); + + if (_expandingAccumulator == null) + { + _expandingAccumulator = new Dictionary>(StringComparer.OrdinalIgnoreCase); + } + + // Already 3 entries so use starting allocated as 8; then use List's expansion mechanism for more + var list = new List(8); + var array = values.ToArray(); + + list.Add(array[0]); + list.Add(array[1]); + list.Add(value); + + _expandingAccumulator[key] = list; + } } else { - values = new List(1); - values.Add(value); - _accumulator[key] = values; + // First value for this key + _accumulator[key] = new StringValues(value); } } @@ -34,19 +65,16 @@ namespace Microsoft.AspNetCore.WebUtilities public Dictionary GetResults() { - if (_accumulator == null) + if (_expandingAccumulator != null) { - return new Dictionary(StringComparer.OrdinalIgnoreCase); + // Coalesce count 3+ multi-value entries into _accumulator dictionary + foreach (var entry in _expandingAccumulator) + { + _accumulator[entry.Key] = new StringValues(entry.Value.ToArray()); + } } - var results = new Dictionary(_accumulator.Count, StringComparer.OrdinalIgnoreCase); - - foreach (var kv in _accumulator) - { - results.Add(kv.Key, kv.Value.Count == 1 ? new StringValues(kv.Value[0]) : new StringValues(kv.Value.ToArray())); - } - - return results; + return _accumulator ?? new Dictionary(0, StringComparer.OrdinalIgnoreCase); } } }