From da91db8862aaae61292d2fb36ad499d6100bba02 Mon Sep 17 00:00:00 2001 From: Igor Labutin Date: Wed, 12 Jul 2017 01:07:55 +0300 Subject: [PATCH] Revert "Replace manually sorted list with SortedDictionary" This reverts commit 8a1773699ce42f9e1fe274968b4705dbf5f7ace9. --- .../TraceUtilities/HistoryDictionary.cs | 117 +++++++++++++----- 1 file changed, 86 insertions(+), 31 deletions(-) diff --git a/src/TraceEvent/TraceUtilities/HistoryDictionary.cs b/src/TraceEvent/TraceUtilities/HistoryDictionary.cs index 5b4207fae..6c423e941 100644 --- a/src/TraceEvent/TraceUtilities/HistoryDictionary.cs +++ b/src/TraceEvent/TraceUtilities/HistoryDictionary.cs @@ -15,7 +15,7 @@ internal class HistoryDictionary { public HistoryDictionary(int initialSize) { - entries = new Dictionary>(initialSize); + entries = new Dictionary(initialSize); } /// /// Adds the association that 'id' has the value 'value' from 'startTime100ns' ONWARD until @@ -25,52 +25,89 @@ public HistoryDictionary(int initialSize) /// public void Add(Address id, long startTime, T value, bool isEndRundown = false) { - SortedDictionary entry; + HistoryValue entry; if (!entries.TryGetValue((long)id, out entry)) { // rundown events are 'last chance' events that we only add if we don't already have an entry for it. if (isEndRundown) startTime = 0; - var newEntry = new SortedDictionary(); - newEntry.Add(startTime, value); - entries.Add((long)id, newEntry); - count++; + entries.Add((long)id, new HistoryValue(startTime, id, value)); } else { Debug.Assert(entry != null); - T val; - if (!entry.TryGetValue(startTime, out val)) - { - entry.Add(startTime, value); - count++; - } - else + var firstEntry = entry; + + // See if we can jump ahead. Currently we only do this of the first entry, + // But you could imagine using some of the other nodes's skipAhead entries. + if (firstEntry.skipAhead != null && firstEntry.skipAhead.startTime <= startTime) + entry = firstEntry.skipAhead; + + for (; ; ) { - entry[startTime] = value; + // We found exact match + if (startTime == entry.StartTime) + { + // Just update the value and exit immediately as there is no need to + // update skipAhead or increment count + entry.value = value; + return; + } + + if (entry.next == null) + { + entry.next = new HistoryValue(startTime, id, value); + break; + } + + // We sort the entries from smallest to largest time. + if (startTime < entry.startTime) + { + // This entry belongs in front of this entry. + // Insert it before the current entry by moving the current entry after it. + HistoryValue newEntry = new HistoryValue(entry); + entry.startTime = startTime; + entry.value = value; + entry.next = newEntry; + Debug.Assert(entry.startTime <= entry.next.startTime); + break; + } + entry = entry.next; } + firstEntry.skipAhead = entry.next; } + count++; } // TryGetValue will return the value associated with an id that was placed in the stream // at time100ns OR BEFORE. public bool TryGetValue(Address id, long time, out T value) { - SortedDictionary entry; + HistoryValue entry; if (entries.TryGetValue((long)id, out entry)) { // The entries are sorted smallest to largest. // We want the last entry that is smaller (or equal) to the target time) - long? lastStart = null; - var keys = entry.Keys; - foreach (var t in keys) + + var firstEntry = entry; + // See if we can jump ahead. Currently we only do this of the first entry, + // But you could imagine using some of the other nodes's skipAhead entries. + if (firstEntry.skipAhead != null && firstEntry.skipAhead.startTime < time) + entry = firstEntry.skipAhead; + + HistoryValue last = null; + for (; ; ) { - if (time < t) + if (time < entry.startTime) + break; + last = entry; + entry = entry.next; + if (entry == null) break; - lastStart = t; } - if (lastStart != null) + if (last != null) { - value = entry[lastStart.Value]; + value = last.value; + firstEntry.skipAhead = last; return true; } } @@ -84,16 +121,16 @@ public IEnumerable Entries #if DEBUG int ctr = 0; #endif - foreach (var entry in entries) + foreach (HistoryValue entry in entries.Values) { - SortedDictionary e = entry.Value; - long id = entry.Key; - foreach (var v in e) + HistoryValue list = entry; + while (list != null) { #if DEBUG - ctr++; + ctr++; #endif - yield return new HistoryValue(v.Key, (ulong)id, v.Value); + yield return list; + list = list.next; } } #if DEBUG @@ -107,10 +144,16 @@ public IEnumerable Entries /// public void Remove(Address id) { - SortedDictionary entry; + HistoryValue entry; if (entries.TryGetValue((long)id, out entry)) { - count -= entry.Count; + // Fix up the count by the number of entries we remove. + while (entry != null) + { + entry.skipAhead = null; // Throw away optimization data. + --count; + entry = entry.next; + } entries.Remove((long)id); } } @@ -121,6 +164,13 @@ public class HistoryValue public long StartTime { get { return startTime; } } public T Value { get { return value; } } #region private + internal HistoryValue(HistoryValue entry) + { + this.key = entry.key; + this.startTime = entry.startTime; + this.value = entry.value; + this.next = entry.next; + } internal HistoryValue(long startTime100ns, Address key, T value) { this.key = key; @@ -131,10 +181,15 @@ internal HistoryValue(long startTime100ns, Address key, T value) internal Address key; internal long startTime; internal T value; + internal HistoryValue next; + // To improve getting to the end quickly, we allow nodes to store values that 'skip ahead'. + // Today we only use this field for the first node to skip to the end (for fast append) + // The only strong invarient for this field is that it point further up the same list. + internal HistoryValue skipAhead; #endregion } #region private - Dictionary> entries; + Dictionary entries; int count; #endregion }