set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE)
endif()
-set(HEAPTRACK_VERSION_MAJOR 0)
-set(HEAPTRACK_VERSION_MINOR 1)
+set(HEAPTRACK_VERSION_MAJOR 1)
+set(HEAPTRACK_VERSION_MINOR 0)
set(HEAPTRACK_VERSION_PATCH 0)
set(HEAPTRACK_LIB_VERSION 1.0.0)
set(HEAPTRACK_LIB_SOVERSION 1)
find_package(ZLIB REQUIRED)
include(FeatureSummary)
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
+
if(Qt5_FOUND AND ECM_FOUND)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR})
find_package(KF5 OPTIONAL_COMPONENTS CoreAddons I18n ItemModels ThreadWeaver ConfigWidgets)
allocations.clear();
sizeHistogram.clear();
uint64_t lastAllocationPtr = 0;
+ uint fileVersion = 0;
while (reader.getLine(in)) {
if (reader.mode() == 's') {
opNewIpIndices.push_back(index);
}
} else if (reader.mode() == '+') {
- uint64_t size = 0;
- TraceIndex traceId;
- uint64_t ptr = 0;
- if (!(reader >> size) || !(reader >> traceId) || !(reader >> ptr)) {
- cerr << "failed to parse line: " << reader.line() << endl;
- continue;
- }
-
- if (size <= std::numeric_limits<uint32_t>::max()) {
- // save memory by storing this allocation in the list of small allocations
- activeSmallAllocations[ptr] = {traceId, static_cast<uint32_t>(size)};
+ BigAllocationInfo info;
+ if (fileVersion >= 0x010000) {
+ uint32_t allocationInfoIndex = 0;
+ if (!(reader >> allocationInfoIndex)) {
+ cerr << "failed to parse line: " << reader.line() << endl;
+ continue;
+ }
+ info = allocationInfos[allocationInfoIndex];
} else {
- // these rare allocations consume more memory to track, but that's fine
- activeBigAllocations[ptr] = {traceId, size};
+ uint64_t ptr = 0;
+ if (!(reader >> info.size) || !(reader >> info.traceIndex) || !(reader >> ptr)) {
+ cerr << "failed to parse line: " << reader.line() << endl;
+ continue;
+ }
+
+ if (info.size <= std::numeric_limits<uint32_t>::max()) {
+ // save memory by storing this allocation in the list of small allocations
+ activeSmallAllocations[ptr] = {static_cast<uint32_t>(info.size), info.traceIndex};
+ } else {
+ // these rare allocations consume more memory to track, but that's fine
+ activeBigAllocations[ptr] = info;
+ }
+ lastAllocationPtr = ptr;
}
- auto& allocation = findAllocation(traceId);
- allocation.leaked += size;
- allocation.allocated += size;
+ auto& allocation = findAllocation(info.traceIndex);
+ allocation.leaked += info.size;
+ allocation.allocated += info.size;
++allocation.allocations;
if (allocation.leaked > allocation.peak) {
allocation.peak = allocation.leaked;
}
- totalAllocated += size;
+
+ totalAllocated += info.size;
++totalAllocations;
- leaked += size;
+ leaked += info.size;
if (leaked > peak) {
peak = leaked;
}
- lastAllocationPtr = ptr;
- handleAllocation();
+
if (printHistogram) {
- ++sizeHistogram[size];
+ ++sizeHistogram[info.size];
}
+
+ handleAllocation();
} else if (reader.mode() == '-') {
- uint64_t ptr = 0;
- if (!(reader >> ptr)) {
- cerr << "failed to parse line: " << reader.line() << endl;
- continue;
- }
- const auto info = takeActiveAllocation(ptr);
- if (!info.traceIndex) {
- // happens when we attached to a running application
- continue;
+ BigAllocationInfo info;
+ bool temporary = false;
+ if (fileVersion >= 0x010000) {
+ uint32_t allocationInfoIndex = 0;
+ if (!(reader >> allocationInfoIndex)) {
+ cerr << "failed to parse line: " << reader.line() << endl;
+ continue;
+ }
+ info = allocationInfos[allocationInfoIndex];
+ // optional, and thus may fail.
+ // but that's OK since we default to false anyways
+ reader >> temporary;
+ } else {
+ uint64_t ptr = 0;
+ if (!(reader >> ptr)) {
+ cerr << "failed to parse line: " << reader.line() << endl;
+ continue;
+ }
+ info = takeActiveAllocation(ptr);
+ if (!info.traceIndex) {
+ // happens when we attached to a running application
+ continue;
+ }
+ temporary = lastAllocationPtr == ptr;
+ lastAllocationPtr = 0;
}
auto& allocation = findAllocation(info.traceIndex);
allocation.leaked -= info.size;
}
leaked -= info.size;
- if (lastAllocationPtr == ptr) {
+ if (temporary) {
++allocation.temporary;
++totalTemporary;
}
- lastAllocationPtr = 0;
+ } else if (reader.mode() == 'a') {
+ BigAllocationInfo info;
+ if (!(reader >> info.size) || !(reader >> info.traceIndex)) {
+ cerr << "failed to parse line: " << reader.line() << endl;
+ continue;
+ }
+ allocationInfos.push_back(info);
} else if (reader.mode() == '#') {
// comment or empty line
continue;
peak = 0;
fromAttached = true;
} else if (reader.mode() == 'v') {
- // the version, we ignore it for now
+ reader >> fileVersion;
} else {
cerr << "failed to parse line: " << reader.line() << endl;
}
return find(stopIndices.begin(), stopIndices.end(), index) != stopIndices.end();
}
+// only used for backwards compatibility with older data files
+// newer files do this in heaptrack_interpret already with some more magic
BigAllocationInfo AccumulatedTraceData::takeActiveAllocation(uint64_t ptr)
{
auto small = activeSmallAllocations.find(ptr);
if (small != activeSmallAllocations.end()) {
auto ret = small->second;
activeSmallAllocations.erase(small);
- return {ret.traceIndex, ret.size};
+ return {ret.size, ret.traceIndex};
}
// rare
auto big = activeBigAllocations.find(ptr);
#include <fstream>
#include <unordered_map>
+#include <unordered_set>
#include <map>
+#include <boost/functional/hash.hpp>
-// sadly, C++ doesn't yet have opaque typedefs
-template<typename Base>
-struct Index
-{
- uint32_t index = 0;
-
- explicit operator bool() const
- {
- return index;
- }
-
- bool operator<(Index o) const
- {
- return index < o.index;
- }
-
- bool operator!=(Index o) const
- {
- return index != o.index;
- }
-
- bool operator==(Index o) const
- {
- return index == o.index;
- }
-};
-
-template<typename Base>
-uint qHash(const Index<Base> index, uint seed = 0) noexcept
-{
- return qHash(index.index, seed);
-}
-
-struct StringIndex : public Index<StringIndex> {};
-struct ModuleIndex : public StringIndex {};
-struct FunctionIndex : public StringIndex {};
-struct FileIndex : public StringIndex {};
-struct IpIndex : public Index<IpIndex> {};
-struct TraceIndex : public Index<TraceIndex> {};
+#include "indices.h"
struct InstructionPointer
{
*/
struct SmallAllocationInfo
{
- TraceIndex traceIndex;
uint32_t size;
+ TraceIndex traceIndex;
};
/**
*/
struct BigAllocationInfo
{
- TraceIndex traceIndex;
uint64_t size;
+ TraceIndex traceIndex;
+ bool operator==(const BigAllocationInfo& rhs) const
+ {
+ return rhs.traceIndex == traceIndex && rhs.size == size;
+ }
};
struct AccumulatedTraceData
// indices of functions that should stop the backtrace, e.g. main or static initialization
std::vector<StringIndex> stopIndices;
- std::unordered_map<uint64_t, SmallAllocationInfo> activeSmallAllocations;
- std::unordered_map<uint64_t, BigAllocationInfo> activeBigAllocations;
std::vector<InstructionPointer> instructionPointers;
std::vector<TraceNode> traces;
std::vector<std::string> strings;
std::vector<IpIndex> opNewIpIndices;
+
+ std::vector<BigAllocationInfo> allocationInfos;
+
+ // backwards compatibility
+ std::unordered_map<uint64_t, SmallAllocationInfo> activeSmallAllocations;
+ std::unordered_map<uint64_t, BigAllocationInfo> activeBigAllocations;
};
#endif // ACCUMULATEDTRACEDATA_H
--- /dev/null
+# - Try to find SparseHash
+# Once done this will define
+# SPARSEHASH_FOUND - System has SparseHash
+# SPARSEHASH_INCLUDE_DIRS - The SparseHash include directories
+
+find_path(SPARSEHASH_INCLUDE_DIR NAMES sparse_hash_map
+ PATHS ${SPARSEHASH_ROOT} $ENV{SPARSEHASH_ROOT} /usr/local/ /usr/ /sw/ /opt/local /opt/csw/ /opt/ ENV CPLUS_INCLUDE_PATH
+ PATH_SUFFIXES include/sparsehash include/google include
+)
+
+set(SPARSEHASH_INCLUDE_DIRS ${SPARSEHASH_INCLUDE_DIR})
+
+# handle the QUIETLY and REQUIRED arguments and set SPARSEHASH_FOUND to TRUE if
+# all listed variables are TRUE
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(SparseHash DEFAULT_MSG SPARSEHASH_INCLUDE_DIR)
+
+mark_as_advanced(SPARSEHASH_INCLUDE_DIR)
\ No newline at end of file
#include <iostream>
#include <sstream>
#include <unordered_map>
+#include <unordered_set>
#include <vector>
#include <tuple>
#include <algorithm>
#include <cxxabi.h>
#include <boost/algorithm/string/predicate.hpp>
+#include <boost/functional/hash.hpp>
#include "libbacktrace/backtrace.h"
#include "libbacktrace/internal.h"
#include "linereader.h"
+#include "pointermap.h"
using namespace std;
backtrace_state* backtraceState;
};
-struct Allocation
-{
- // backtrace entry point
- size_t ipIndex;
- // number of allocations
- size_t allocations;
- // amount of bytes leaked
- size_t leaked;
-};
-
-/**
- * Information for a single call to an allocation function
- */
-struct AllocationInfo
-{
- size_t ipIndex;
- size_t size;
-};
-
struct ResolvedIP
{
size_t moduleIndex = 0;
unordered_map<uintptr_t, size_t> m_encounteredIps;
};
+/**
+ * Information for a single call to an allocation function for big allocations.
+ */
+struct AllocationInfo
+{
+ uint64_t size;
+ TraceIndex traceIndex;
+ AllocationIndex allocationInfoIndex;
+ bool operator==(const AllocationInfo& rhs) const
+ {
+ return rhs.traceIndex == traceIndex
+ && rhs.size == size;
+ // allocationInfoIndex not compared to allow to look it up
+ }
+};
+
+}
+
+namespace std {
+template<>
+struct hash<AllocationInfo> {
+ size_t operator()(const AllocationInfo &info) const
+ {
+ size_t seed = 0;
+ boost::hash_combine(seed, info.size);
+ boost::hash_combine(seed, info.traceIndex.index);
+ // allocationInfoIndex not hashed to allow to look it up
+ return seed;
+ }
+};
}
int main(int /*argc*/, char** /*argv*/)
string exe;
+ unordered_set<AllocationInfo> allocationInfos;
+ PointerMap ptrToIndex;
+ uint64_t lastPtr = 0;
+ allocationInfos.reserve(625000);
+
while (reader.getLine(cin)) {
if (reader.mode() == 'x') {
reader >> exe;
const auto ipId = data.addIp(instructionPointer);
// trace point, map current output index to parent index
fprintf(stdout, "t %zx %zx\n", ipId, parentIndex);
+ } else if (reader.mode() == '+') {
+ uint64_t size = 0;
+ TraceIndex traceId;
+ uint64_t ptr = 0;
+ if (!(reader >> size) || !(reader >> traceId.index) || !(reader >> ptr)) {
+ cerr << "failed to parse line: " << reader.line() << endl;
+ continue;
+ }
+ AllocationIndex index;
+ index.index = allocationInfos.size();
+ AllocationInfo info = {size, traceId, index};
+ auto it = allocationInfos.find(info);
+ if (it != allocationInfos.end()) {
+ info = *it;
+ } else {
+ allocationInfos.insert(it, info);
+ fprintf(stdout, "a %zx %x\n", info.size, info.traceIndex.index);
+ }
+ ptrToIndex.addPointer(ptr, info.allocationInfoIndex);
+ lastPtr = ptr;
+ fprintf(stdout, "+ %x\n", info.allocationInfoIndex.index);
+ } else if (reader.mode() == '-') {
+ uint64_t ptr = 0;
+ if (!(reader >> ptr)) {
+ cerr << "failed to parse line: " << reader.line() << endl;
+ continue;
+ }
+ bool temporary = lastPtr == ptr;
+ lastPtr = 0;
+ auto allocation = ptrToIndex.takePointer(ptr);
+ if (!allocation.second) {
+ continue;
+ }
+ fprintf(stdout, "- %x", allocation.first.index);
+ if (temporary) {
+ fputs(" 1\n", stdout);
+ } else {
+ fputc('\n', stdout);
+ }
} else {
fputs(reader.line().c_str(), stdout);
fputc('\n', stdout);
--- /dev/null
+/*
+ * Copyright 2015 Milian Wolff <mail@milianw.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef INDICES_H
+#define INDICES_H
+
+#include <unordered_map>
+
+// sadly, C++ doesn't yet have opaque typedefs
+template<typename Base>
+struct Index
+{
+ uint32_t index = 0;
+
+ explicit operator bool() const
+ {
+ return index;
+ }
+
+ bool operator<(Index o) const
+ {
+ return index < o.index;
+ }
+
+ bool operator!=(Index o) const
+ {
+ return index != o.index;
+ }
+
+ bool operator==(Index o) const
+ {
+ return index == o.index;
+ }
+};
+
+template<typename Base>
+uint qHash(const Index<Base> index, uint seed = 0) noexcept
+{
+ return qHash(index.index, seed);
+}
+
+namespace std {
+template<typename Base>
+struct hash<Index<Base>> {
+ std::size_t operator()(const Index<Base> index) const
+ {
+ return std::hash<uint32_t>()(index.index);
+ }
+};
+}
+
+struct StringIndex : public Index<StringIndex> {};
+struct ModuleIndex : public StringIndex {};
+struct FunctionIndex : public StringIndex {};
+struct FileIndex : public StringIndex {};
+struct IpIndex : public Index<IpIndex> {};
+struct TraceIndex : public Index<TraceIndex> {};
+struct AllocationIndex : public Index<AllocationIndex> {};
+
+#endif // INDICES_H
--- /dev/null
+/*
+ * Copyright 2015 Milian Wolff <mail@milianw.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef POINTERMAP_H
+#define POINTERMAP_H
+
+#include <vector>
+#include <unordered_map>
+#include <map>
+#include <limits>
+#include <iostream>
+#include <algorithm>
+
+#include "indices.h"
+
+/**
+ * A low-memory-overhead map of 64bit pointer addresses to 32bit allocation indices.
+ *
+ * We leverage the fact that pointers are allocated in pages, i.e. close to each
+ * other. We split the 64bit address into a common large part and an individual
+ * 16bit small part by dividing the address by some number (PageSize below) and
+ * keeping the result as the big part and the residue as the small part.
+ *
+ * The big part of the address is used for a hash map to lookup the Indices
+ * structure where we aggregate common pointers in two memory-efficient vectors,
+ * one for the 16bit small pointer pairs, and one for the 32bit allocation indices.
+ */
+class PointerMap
+{
+ struct SplitPointer
+ {
+ enum {
+ PageSize = std::numeric_limits<uint16_t>::max() / 4
+ };
+ SplitPointer(uint64_t ptr)
+ : big(ptr / PageSize)
+ , small(ptr % PageSize)
+ {}
+ uint64_t big;
+ uint16_t small;
+ };
+
+public:
+ PointerMap()
+ {
+ map.reserve(1024);
+ }
+
+ void addPointer(const uint64_t ptr, const AllocationIndex allocationIndex)
+ {
+ const SplitPointer pointer(ptr);
+
+ auto mapIt = map.find(pointer.big);
+ if (mapIt == map.end()) {
+ mapIt = map.insert(mapIt, std::make_pair(pointer.big, Indices()));
+ }
+ auto& indices = mapIt->second;
+ auto pageIt = std::lower_bound(indices.smallPtrParts.begin(), indices.smallPtrParts.end(), pointer.small);
+ auto allocationIt = indices.allocationIndices.begin() + distance(indices.smallPtrParts.begin(), pageIt);
+ if (pageIt == indices.smallPtrParts.end() || *pageIt != pointer.small) {
+ indices.smallPtrParts.insert(pageIt, pointer.small);
+ indices.allocationIndices.insert(allocationIt, allocationIndex);
+ } else {
+ *allocationIt = allocationIndex;
+ }
+ }
+
+ std::pair<AllocationIndex, bool> takePointer(const uint64_t ptr)
+ {
+ const SplitPointer pointer(ptr);
+
+ auto mapIt = map.find(pointer.big);
+ if (mapIt == map.end()) {
+ return {{}, false};
+ }
+ auto& indices = mapIt->second;
+ auto pageIt = std::lower_bound(indices.smallPtrParts.begin(), indices.smallPtrParts.end(), pointer.small);
+ if (pageIt == indices.smallPtrParts.end() || *pageIt != pointer.small) {
+ return {{}, false};
+ }
+ auto allocationIt = indices.allocationIndices.begin() + distance(indices.smallPtrParts.begin(), pageIt);
+ auto index = *allocationIt;
+ indices.allocationIndices.erase(allocationIt);
+ indices.smallPtrParts.erase(pageIt);
+ if (indices.allocationIndices.empty()) {
+ map.erase(mapIt);
+ }
+ return {index, true};
+ }
+
+private:
+ struct Indices
+ {
+ std::vector<uint16_t> smallPtrParts;
+ std::vector<AllocationIndex> allocationIndices;
+ };
+ std::unordered_map<uint64_t, Indices> map;
+};
+
+#endif // POINTERMAP_H
add_subdirectory(manual)
add_subdirectory(auto)
+add_subdirectory(benchmarks)
\ No newline at end of file
--- /dev/null
+include_directories(../..)
+
+include (CheckCXXSourceCompiles)
+check_cxx_source_compiles(
+ "#include <malloc.h>
+ int main() { return mallinfo().uordblks > 0; }"
+ HAVE_MALLOC_H)
+
+if (HAVE_MALLOC_H)
+ add_executable(bench_pointermap bench_pointermap.cpp)
+ add_executable(bench_pointerhash bench_pointerhash.cpp)
+
+ find_package(SparseHash)
+ if(SPARSEHASH_FOUND)
+ include_directories(${SPARSEHASH_INCLUDE_DIRS})
+ add_executable(bench_pointersparsehash bench_pointersparsehash.cpp)
+ endif()
+endif()
--- /dev/null
+/*
+ * Copyright 2015 Milian Wolff <mail@milianw.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <unordered_map>
+
+#include "bench_pointers.h"
+#include "indices.h"
+
+struct PointerHashMap
+{
+ PointerHashMap()
+ {
+ map.reserve(65536);
+ }
+
+ void addPointer(const uint64_t ptr, const AllocationIndex index)
+ {
+ map[ptr] = index;
+ }
+
+ std::pair<AllocationIndex, bool> takePointer(const uint64_t ptr)
+ {
+ auto it = map.find(ptr);
+ if (it == map.end()) {
+ return {{}, false};
+ }
+ auto ret = std::make_pair(it->second, true);
+ map.erase(it);
+ return ret;
+ }
+
+ std::unordered_map<uint64_t, AllocationIndex> map;
+};
+
+int main()
+{
+ benchPointers<PointerHashMap>();
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright 2015 Milian Wolff <mail@milianw.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "pointermap.h"
+#include "bench_pointers.h"
+
+int main()
+{
+ benchPointers<PointerMap>();
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright 2015 Milian Wolff <mail@milianw.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef BENCH_POINTERS
+#define BENCH_POINTERS
+
+#include <cstdint>
+#include <vector>
+#include <algorithm>
+#include <iostream>
+
+#include <malloc.h>
+
+#include "indices.h"
+
+template<typename Map>
+void benchPointers()
+{
+ uint32_t matches = 0;
+ constexpr uint32_t NUM_POINTERS = 10000000;
+ {
+ std::vector<uint64_t> pointers(NUM_POINTERS);
+ const auto baseline = mallinfo().uordblks;
+ std::cerr << "allocated vector: \t" << baseline << std::endl;
+ for (uint32_t i = 0; i < NUM_POINTERS; ++i) {
+ pointers[i] = reinterpret_cast<uint64_t>(malloc(1));
+ }
+ const auto allocated = (mallinfo().uordblks - baseline);
+ std::cerr << "allocated input pointers:\t" << allocated << std::endl;
+ for (auto ptr : pointers) {
+ free(reinterpret_cast<void*>(ptr));
+ }
+ std::cerr << "freed input pointers: \t" << (mallinfo().uordblks - baseline) << std::endl;
+ srand(0);
+ std::random_shuffle(pointers.begin(), pointers.end());
+ malloc_trim(0);
+ std::cerr << "begin actual benchmark: \t" << (mallinfo().uordblks - baseline) << std::endl;
+
+ {
+ Map map;
+ for (auto ptr : pointers) {
+ AllocationIndex index;
+ index.index = static_cast<uint32_t>(ptr);
+ map.addPointer(ptr, index);
+ }
+
+ const auto added = mallinfo().uordblks - baseline;
+ std::cerr << "pointers added: \t" << added << " (" << (float(added) * 100.f / allocated) << "% overhead)" << std::endl;
+
+ std::random_shuffle(pointers.begin(), pointers.end());
+ for (auto ptr : pointers) {
+ AllocationIndex index;
+ index.index = static_cast<uint32_t>(ptr);
+ auto allocation = map.takePointer(ptr);
+ if (allocation.second && allocation.first == index) {
+ ++matches;
+ }
+ }
+
+ std::cerr << "pointers removed: \t" << mallinfo().uordblks << std::endl;
+ malloc_trim(0);
+ std::cerr << "trimmed: \t" << mallinfo().uordblks << std::endl;
+ }
+ }
+ if (matches != NUM_POINTERS) {
+ std::cerr << "FAILED!";
+ abort();
+ }
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright 2015 Milian Wolff <mail@milianw.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <sparse_hash_map>
+#include "bench_pointers.h"
+#include "indices.h"
+
+struct PointerHashMap
+{
+ void addPointer(const uint64_t ptr, const AllocationIndex index)
+ {
+ map[ptr] = index;
+ }
+
+ std::pair<AllocationIndex, bool> takePointer(const uint64_t ptr)
+ {
+ auto it = map.find(ptr);
+ if (it == map.end()) {
+ return {{}, false};
+ }
+ auto ret = std::make_pair(it->second, true);
+ map.erase(it);
+ return ret;
+ }
+
+ google::sparse_hash_map<uint64_t, AllocationIndex> map;
+};
+
+int main()
+{
+ benchPointers<PointerHashMap>();
+ return 0;
+}
\ No newline at end of file