Skip to content

Commit

Permalink
Optimize: only map trace indices for allocation infos
Browse files Browse the repository at this point in the history
Previously, we used to run the binary search to map a trace index
to an allocation object on every (de)allocation. These events are
occurring extremely often, of course - usually orders of magnitudes
more often than the allocation info events.

Now, we only map the trace index to an allocation when a new
allocation info is parsed. This way, we don't need to run the
slow binary search and can access the allocation object directly
through the mapped index in the allocation index.

For a large data file (~13GB uncompressed) the results are quite
impressive: Before this patch, heaptrack_print took ca. 3min to
parse the zstd compressed data. With this patch applied, we are
down to 2min6s!

Before:

 Performance counter stats for 'heaptrack_print heaptrack.Application.19285.zst':

     178798,164042      task-clock:u (msec)       #    0,998 CPUs utilized
                 0      context-switches:u        #    0,000 K/sec
                 0      cpu-migrations:u          #    0,000 K/sec
            30.570      page-faults:u             #    0,171 K/sec
   551.902.999.436      cycles:u                  #    3,087 GHz
 1.540.185.452.300      instructions:u            #    2,79  insn per cycle
   332.833.340.539      branches:u                # 1861,503 M/sec
     1.350.342.839      branch-misses:u           #    0,41% of all branches

     179,193276255 seconds time elapsed

After:

 Performance counter stats for 'heaptrack_print heaptrack.Application.19285.zst':

     125579,754384      task-clock:u (msec)       #    0,999 CPUs utilized
                 0      context-switches:u        #    0,000 K/sec
                 0      cpu-migrations:u          #    0,000 K/sec
            33.982      page-faults:u             #    0,271 K/sec
   393.084.840.177      cycles:u                  #    3,130 GHz
 1.127.147.336.034      instructions:u            #    2,87  insn per cycle
   238.225.815.121      branches:u                # 1897,008 M/sec
       998.456.200      branch-misses:u           #    0,42% of all branches

     125,663808724 seconds time elapsed

CCBUG: 386256
  • Loading branch information
milianw committed Mar 16, 2018
1 parent e7feb7c commit a189ad4
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 13 deletions.
30 changes: 20 additions & 10 deletions src/analyze/accumulatedtracedata.cpp
Expand Up @@ -268,19 +268,21 @@ bool AccumulatedTraceData::read(istream& in, const ParsePass pass)
lastAllocationPtr = allocationIndex.index;
} else { // backwards compatibility
uint64_t ptr = 0;
if (!(reader >> info.size) || !(reader >> info.traceIndex) || !(reader >> ptr)) {
TraceIndex traceIndex;
if (!(reader >> info.size) || !(reader >> traceIndex) || !(reader >> ptr)) {
cerr << "failed to parse line: " << reader.line() << endl;
continue;
}
if (allocationInfoSet.add(info.size, info.traceIndex, &allocationIndex)) {
info.allocationIndex = mapToAllocationIndex(traceIndex);
if (allocationInfoSet.add(info.size, traceIndex, &allocationIndex)) {
allocationInfos.push_back(info);
}
pointers.addPointer(ptr, allocationIndex);
lastAllocationPtr = ptr;
}

if (pass != FirstPass) {
auto& allocation = findAllocation(info.traceIndex);
auto& allocation = allocations[info.allocationIndex.index];
allocation.leaked += info.size;
allocation.allocated += info.size;
++allocation.allocations;
Expand Down Expand Up @@ -333,7 +335,7 @@ bool AccumulatedTraceData::read(istream& in, const ParsePass pass)
}

if (pass != FirstPass) {
auto& allocation = findAllocation(info.traceIndex);
auto& allocation = allocations[info.allocationIndex.index];
allocation.leaked -= info.size;
if (temporary) {
++allocation.temporary;
Expand All @@ -344,11 +346,14 @@ bool AccumulatedTraceData::read(istream& in, const ParsePass pass)
continue;
}
AllocationInfo info;
if (!(reader >> info.size) || !(reader >> info.traceIndex)) {
TraceIndex traceIndex;
if (!(reader >> info.size) || !(reader >> traceIndex)) {
cerr << "failed to parse line: " << reader.line() << endl;
continue;
}
info.allocationIndex = mapToAllocationIndex(traceIndex);
allocationInfos.push_back(info);

} else if (reader.mode() == '#') {
// comment or empty line
continue;
Expand Down Expand Up @@ -658,19 +663,19 @@ void AccumulatedTraceData::diff(const AccumulatedTraceData& base)
allocations.end());
}

Allocation& AccumulatedTraceData::findAllocation(const TraceIndex traceIndex)
AllocationIndex AccumulatedTraceData::mapToAllocationIndex(const TraceIndex traceIndex)
{
AllocationIndex allocationIndex;
if (traceIndex < m_maxAllocationTraceIndex) {
// only need to search when the trace index is previously known
auto it = lower_bound(traceIndexToAllocationIndex.begin(), traceIndexToAllocationIndex.end(), traceIndex,
[](const pair<TraceIndex, AllocationIndex>& indexMap, const TraceIndex traceIndex) -> bool {
return indexMap.first < traceIndex;
});
if (it != traceIndexToAllocationIndex.end() && it->first == traceIndex) {
return allocations[it->second.index];
return it->second;
}
// new allocation
AllocationIndex allocationIndex;
allocationIndex.index = allocations.size();
traceIndexToAllocationIndex.insert(it, make_pair(traceIndex, allocationIndex));
Allocation allocation;
Expand All @@ -679,17 +684,22 @@ Allocation& AccumulatedTraceData::findAllocation(const TraceIndex traceIndex)
} else if (traceIndex == m_maxAllocationTraceIndex && !allocations.empty()) {
// reuse the last allocation
assert(allocations.back().traceIndex == traceIndex);
allocationIndex.index = allocations.size() - 1;
} else {
// new allocation
AllocationIndex allocationIndex;
allocationIndex.index = allocations.size();
traceIndexToAllocationIndex.push_back(make_pair(traceIndex, allocationIndex));
Allocation allocation;
allocation.traceIndex = traceIndex;
allocations.push_back(allocation);
m_maxAllocationTraceIndex = traceIndex;
}
return allocations.back();
return allocationIndex;
}

Allocation& AccumulatedTraceData::findAllocation(const TraceIndex traceIndex)
{
return allocations[mapToAllocationIndex(traceIndex).index];
}

InstructionPointer AccumulatedTraceData::findIp(const IpIndex ipIndex) const
Expand Down
10 changes: 8 additions & 2 deletions src/analyze/accumulatedtracedata.h
Expand Up @@ -90,10 +90,11 @@ struct Allocation : public AllocationData
struct AllocationInfo
{
uint64_t size = 0;
TraceIndex traceIndex;
// index into AccumulatedTraceData::allocations
AllocationIndex allocationIndex;
bool operator==(const AllocationInfo& rhs) const
{
return rhs.traceIndex == traceIndex && rhs.size == size;
return rhs.allocationIndex == allocationIndex && rhs.size == size;
}
};

Expand Down Expand Up @@ -148,6 +149,11 @@ struct AccumulatedTraceData
// vector around for efficient index lookup
std::vector<std::pair<TraceIndex, AllocationIndex>> traceIndexToAllocationIndex;

/// find and return the index into the @c allocations vector for the given trace index.
/// if the trace index wasn't mapped before, an empty Allocation will be added
/// and its index returned.
AllocationIndex mapToAllocationIndex(const TraceIndex traceIndex);
/// map the trace index to an allocation and return a reference to it
Allocation& findAllocation(const TraceIndex traceIndex);

InstructionPointer findIp(const IpIndex ipIndex) const;
Expand Down
3 changes: 2 additions & 1 deletion src/analyze/gui/parser.cpp
Expand Up @@ -519,7 +519,8 @@ HistogramData buildSizeHistogram(ParserData& data)
} else {
row.columns[0].allocations += info.allocations;
}
const auto ipIndex = data.findTrace(info.info.traceIndex).ipIndex;
const auto& allocation = data.allocations[info.info.allocationIndex.index];
const auto ipIndex = data.findTrace(allocation.traceIndex).ipIndex;
const auto ip = data.findIp(ipIndex);
const auto location = data.stringCache.location(ipIndex, ip);
auto it = lower_bound(columnData.begin(), columnData.end(), location);
Expand Down

0 comments on commit a189ad4

Please sign in to comment.