Optimize findAllocation calls by leveraging monotonicity of indices.
authorMilian Wolff <mail@milianw.de>
Tue, 18 Nov 2014 17:44:58 +0000 (18:44 +0100)
committerMilian Wolff <mail@milianw.de>
Tue, 18 Nov 2014 17:55:01 +0000 (18:55 +0100)
By comparing the incoming trace index to the largest index we so
far encountered, we can already decide whether:

a) if the index is equal to the largest one, the same location is used
repeatedly, i.e. in a loop, and we can directly return the last item.
b) if the index is larger than the last known one, we'll definitely
not find it and can thus insert directly and return
c) only if the index is lower than the last index do we actually need
to do a binary search.

This yields a small but noticeable speedup from ~14.5s to 13.4s for
one of my files.

heaptrack_print.cpp

index 444191d..a1b4b8f 100644 (file)
@@ -33,6 +33,7 @@
 #include <memory>
 #include <tuple>
 #include <algorithm>
+#include <cassert>
 
 #include <boost/iostreams/filtering_stream.hpp>
 #include <boost/iostreams/filter/gzip.hpp>
@@ -635,18 +636,33 @@ struct AccumulatedTraceData
     size_t leaked = 0;
 
 private:
+    // our indices are sequentially increasing thus a new allocation can only ever
+    // occur with an index larger than any other we encountered so far
+    // this can be used to our advantage in speeding up the findAllocation calls.
+    TraceIndex m_maxAllocationTraceIndex;
+
     Allocation& findAllocation(const TraceIndex traceIndex)
     {
-        auto it = lower_bound(allocations.begin(), allocations.end(), traceIndex,
+        if (traceIndex < m_maxAllocationTraceIndex) {
+            // only need to search when the trace index is previously known
+            auto it = lower_bound(allocations.begin(), allocations.end(), traceIndex,
                                 [] (const Allocation& allocation, const TraceIndex traceIndex) -> bool {
                                     return allocation.traceIndex < traceIndex;
                                 });
-        if (it == allocations.end() || it->traceIndex != traceIndex) {
+            assert(it != allocations.end());
+            assert(it->traceIndex == traceIndex);
+            return *it;
+        } else if (traceIndex == m_maxAllocationTraceIndex) {
+            // reuse the last allocation
+            assert(allocations.back().traceIndex == traceIndex);
+        } else {
+            // actually a new allocation
             Allocation allocation;
             allocation.traceIndex = traceIndex;
-            it = allocations.insert(it, allocation);
+            allocations.push_back(allocation);
+            m_maxAllocationTraceIndex = traceIndex;
         }
-        return *it;
+        return allocations.back();
     }
 
     void mergeAllocation(const Allocation& allocation)