Skip to content

Commit

Permalink
test softcounter (#46)
Browse files Browse the repository at this point in the history
* lit support softcounter feature for tests

* script parse and stack update, fix #43 and #44

* test accessrecorder with leaky code, fixes #45

* separate softcounter printer and use getter for fields

* add pseudo targets for tests, this helps clion ide

* fix lit keyword for required; CI use softcounter always
  • Loading branch information
ahueck authored Jan 4, 2021
1 parent 11c5b82 commit c45cd64
Show file tree
Hide file tree
Showing 16 changed files with 597 additions and 110 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/basic-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ jobs:
- name: Build TypeART
run: |
cmake -B build -DTEST_CONFIG=ON -DENABLE_CODE_COVERAGE=ON -DLLVM_EXTERNAL_LIT=${EXTERNAL_LIT}
cmake -B build -DTEST_CONFIG=ON -DENABLE_CODE_COVERAGE=ON -DSOFTCOUNTERS=ON -DLLVM_EXTERNAL_LIT=${EXTERNAL_LIT}
cmake --build build --parallel
- name: Test TypeART with coverage (exludes lulesh)
- name: Test TypeART with coverage
run: |
cmake --build build --target lcov-clean
cmake --build build --target test -- ARGS=-VV
Expand All @@ -51,7 +51,7 @@ jobs:

- name: Build TypeART release
run: |
cmake -B build_lulesh -DCMAKE_BUILD_TYPE=Release -DMPI_INTERCEPT_LIB=ON -DSHOW_STATS=ON
cmake -B build_lulesh -DCMAKE_BUILD_TYPE=Release -DMPI_INTERCEPT_LIB=ON -DSHOW_STATS=ON -DSOFTCOUNTERS=ON
cmake --build build_lulesh --parallel
- name: Test TypeART release on lulesh
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ext-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
- name: Build & install TypeART
run: |
cmake -B build -DCMAKE_BUILD_TYPE=Release -DMPI_INTERCEPT_LIB=ON -DSHOW_STATS=ON
cmake -B build -DCMAKE_BUILD_TYPE=Release -DMPI_INTERCEPT_LIB=ON -DSHOW_STATS=ON -DSOFTCOUNTERS=ON
cmake --build build --parallel --target install
echo "TYPEART_PATH=${GITHUB_WORKSPACE}/install/typeart" >> $GITHUB_ENV
Expand Down
2 changes: 2 additions & 0 deletions cmake/ToolchainOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ option(SOFTCOUNTERS "Enable software tracking of #tracked addrs. / #distinct che
option(TEST_CONFIG "Set logging levels to appropriate levels for test runner to succeed" OFF)
option(ENABLE_CODE_COVERAGE "Enable code coverage statistics" OFF)
option(ENABLE_LLVM_CODE_COVERAGE "Enable llvm-cov code coverage statistics" OFF)
option(TEST_CONFIGURE_IDE "Add targets so the IDE (e.g., Clion) can interpret test files better" ON)
mark_as_advanced(TEST_CONFIGURE_IDE)

include(AddLLVM)
include(llvm-lit)
Expand Down
114 changes: 114 additions & 0 deletions lib/runtime/AccessCountPrinter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//
// Created by ahueck on 30.12.20.
//

#ifndef TYPEART_ACCESSCOUNTPRINTER_H
#define TYPEART_ACCESSCOUNTPRINTER_H

#include "AccessCounter.h"
#include "support/Logger.h"
#include "support/Table.h"

#include <map>
#include <set>
#include <sstream>
#include <string>
#include <string_view>
#include <unordered_map>
#include <unordered_set>

namespace typeart::softcounter {
namespace memory {
struct MemOverhead {
static constexpr auto pointerMapSize = sizeof(RuntimeT::PointerMap); // Map overhead
static constexpr auto perNodeSizeMap =
sizeof(std::remove_pointer<std::map<MemAddr, PointerInfo>::iterator::_Link_type>::type) +
sizeof(RuntimeT::MapEntry); // not applicable to btree
static constexpr auto stackVectorSize = sizeof(RuntimeT::Stack); // Stack overhead
static constexpr auto perNodeSizeStack = sizeof(RuntimeT::StackEntry); // Stack allocs
double stack{0};
double map{0};
};
inline MemOverhead estimate(Counter stack_max, Counter heap_max, Counter global_max, const double scale = 1024.0) {
MemOverhead mem;
mem.stack = double(MemOverhead::stackVectorSize +
MemOverhead::perNodeSizeStack * std::max<size_t>(RuntimeT::StackReserve, stack_max)) /
scale;
mem.map =
double(MemOverhead::pointerMapSize + MemOverhead::perNodeSizeMap * (stack_max + heap_max + global_max)) / scale;
return mem;
}
} // namespace memory

template <typename Recorder>
void serialise(const Recorder& r, llvm::raw_ostream& buf) {
if constexpr (std::is_same_v<Recorder, NoneRecorder>) {
return;
} else {
const auto memory_use = memory::estimate(r.getMaxStackAllocs(), r.getMaxHeapAllocs(), r.getGlobalAllocs());

Table t("Alloc Stats from softcounters");
t.wrap_length = true;
t.put(Row::make("Total heap", r.getHeapAllocs(), r.getHeapArray()));
t.put(Row::make("Total stack", r.getStackAllocs(), r.getStackArray()));
t.put(Row::make("Total global", r.getGlobalAllocs(), r.getGlobalArray()));
t.put(Row::make("Max. Heap Allocs", r.getMaxHeapAllocs()));
t.put(Row::make("Max. Stack Allocs", r.getMaxStackAllocs()));
t.put(Row::make("Addresses checked", r.getAddrChecked()));
t.put(Row::make("Distinct Addresses checked", r.getSeen().size()));
t.put(Row::make("Addresses re-used", r.getAddrReuses()));
t.put(Row::make("Addresses missed", r.getAddrMissing()));
t.put(Row::make("Distinct Addresses missed", r.getMissing().size()));
t.put(Row::make("Total free heap", r.getHeapAllocsFree(), r.getHeapArrayFree()));
t.put(Row::make("Total free stack", r.getStackAllocsFree(), r.getStackArrayFree()));
t.put(Row::make("Null/Zero/NullZero Addr", r.getNullAlloc(), r.getZeroAlloc(), r.getNullAndZeroAlloc()));
t.put(Row::make("User-def. types", r.getNumUDefTypes()));
t.put(Row::make("Estimated memory use (KiB)", size_t(std::round(memory_use.map + memory_use.stack))));
t.put(Row::make("Bytes per node map/stack", memory::MemOverhead::perNodeSizeMap,
memory::MemOverhead::perNodeSizeStack));

t.print(buf);

std::set<int> type_id_set;
const auto fill_set = [&type_id_set](const auto& map) {
for (const auto& [key, val] : map) {
type_id_set.insert(key);
}
};
fill_set(r.getHeapAlloc());
fill_set(r.getGlobalAlloc());
fill_set(r.getStackAlloc());
fill_set(r.getHeapFree());
fill_set(r.getStackFree());

const auto count = [](const auto& map, auto id) {
auto it = map.find(id);
if (it != map.end()) {
return it->second;
}
return 0ll;
};

Table type_table("Allocation type detail (heap, stack, global)");
type_table.table_header = '#';
for (auto type_id : type_id_set) {
type_table.put(Row::make(std::to_string(type_id), count(r.getHeapAlloc(), type_id),
count(r.getStackAlloc(), type_id), count(r.getGlobalAlloc(), type_id),
typeart_get_type_name(type_id)));
}

type_table.print(buf);

Table type_table_free("Free allocation type detail (heap, stack)");
type_table_free.table_header = '#';
for (auto type_id : type_id_set) {
type_table_free.put(Row::make(std::to_string(type_id), count(r.getHeapFree(), type_id),
count(r.getStackFree(), type_id), typeart_get_type_name(type_id)));
}

type_table_free.print(buf);
}
}
} // namespace typeart::softcounter

#endif // TYPEART_ACCESSCOUNTPRINTER_H
195 changes: 96 additions & 99 deletions lib/runtime/AccessCounter.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@

#include "RuntimeData.h"
#include "RuntimeInterface.h"
#include "support/Logger.h"
#include "support/Table.h"

#include <map>
#include <set>
Expand All @@ -31,6 +29,13 @@ class AccessRecorder {

inline void incHeapAlloc(int typeId, size_t count) {
++curHeapAllocs;

// Always check here for max
// A program without free would otherwise never update maxHeap (see test 20_softcounter_max)
if (curHeapAllocs > maxHeapAllocs) {
maxHeapAllocs = curHeapAllocs;
}

++heapAllocs;
if (count > 1) {
++heapArray;
Expand Down Expand Up @@ -72,9 +77,10 @@ class AccessRecorder {
}

inline void decHeapAlloc() {
if (curHeapAllocs > maxHeapAllocs) {
maxHeapAllocs = curHeapAllocs;
}
// Removed, since we already increment maxHeapAllocs just in time:
// if (curHeapAllocs > maxHeapAllocs) {
// maxHeapAllocs = curHeapAllocs;
// }
--curHeapAllocs;
}

Expand Down Expand Up @@ -120,6 +126,91 @@ class AccessRecorder {
return instance;
}

Counter getHeapAllocs() const {
return heapAllocs;
}
Counter getStackAllocs() const {
return stackAllocs;
}
Counter getGlobalAllocs() const {
return globalAllocs;
}
Counter getMaxHeapAllocs() const {
return maxHeapAllocs;
}
Counter getMaxStackAllocs() const {
return maxStackAllocs;
}
Counter getCurHeapAllocs() const {
return curHeapAllocs;
}
Counter getCurStackAllocs() const {
return curStackAllocs;
}
Counter getAddrReuses() const {
return addrReuses;
}
Counter getAddrMissing() const {
return addrMissing;
}
Counter getAddrChecked() const {
return addrChecked;
}
Counter getStackArray() const {
return stackArray;
}
Counter getHeapArray() const {
return heapArray;
}
Counter getGlobalArray() const {
return globalArray;
}
Counter getStackAllocsFree() const {
return stackAllocsFree;
}
Counter getStackArrayFree() const {
return stackArrayFree;
}
Counter getHeapAllocsFree() const {
return heapAllocsFree;
}
Counter getHeapArrayFree() const {
return heapArrayFree;
}
Counter getNullAlloc() const {
return nullAlloc;
}
Counter getZeroAlloc() const {
return zeroAlloc;
}
Counter getNullAndZeroAlloc() const {
return nullAndZeroAlloc;
}
Counter getNumUDefTypes() const {
return numUDefTypes;
}
const std::unordered_set<MemAddr>& getMissing() const {
return missing;
}
const std::unordered_set<MemAddr>& getSeen() const {
return seen;
}
const TypeCountMap& getStackAlloc() const {
return stackAlloc;
}
const TypeCountMap& getHeapAlloc() const {
return heapAlloc;
}
const TypeCountMap& getGlobalAlloc() const {
return globalAlloc;
}
const TypeCountMap& getStackFree() const {
return stackFree;
}
const TypeCountMap& getHeapFree() const {
return heapFree;
}

private:
AccessRecorder() = default;
AccessRecorder(AccessRecorder& other) = default;
Expand Down Expand Up @@ -153,9 +244,6 @@ class AccessRecorder {
TypeCountMap globalAlloc;
TypeCountMap stackFree;
TypeCountMap heapFree;

template <typename Recorder>
friend void serialise(const Recorder& r, llvm::raw_ostream& buf);
};

/**
Expand Down Expand Up @@ -198,97 +286,6 @@ class NoneRecorder {
}
};

namespace memory {
struct MemOverhead {
static constexpr auto pointerMapSize = sizeof(RuntimeT::PointerMap); // Map overhead
static constexpr auto perNodeSizeMap =
sizeof(std::remove_pointer<std::map<MemAddr, PointerInfo>::iterator::_Link_type>::type) +
sizeof(RuntimeT::MapEntry); // not applicable to btree
static constexpr auto stackVectorSize = sizeof(RuntimeT::Stack); // Stack overhead
static constexpr auto perNodeSizeStack = sizeof(RuntimeT::StackEntry); // Stack allocs
double stack{0};
double map{0};
};
inline MemOverhead estimate(Counter stack_max, Counter heap_max, Counter global_max, const double scale = 1024.0) {
MemOverhead mem;
mem.stack = double(MemOverhead::stackVectorSize +
MemOverhead::perNodeSizeStack * std::max<size_t>(RuntimeT::StackReserve, stack_max)) /
scale;
mem.map =
double(MemOverhead::pointerMapSize + MemOverhead::perNodeSizeMap * (stack_max + heap_max + global_max)) / scale;
return mem;
}
} // namespace memory

template <typename Recorder>
void serialise(const Recorder& r, llvm::raw_ostream& buf) {
if constexpr (std::is_same_v<Recorder, NoneRecorder>) {
return;
} else {
const auto memory_use = memory::estimate(r.maxStackAllocs, r.maxHeapAllocs, r.globalAllocs);

Table t("Alloc Stats from softcounters");
t.wrap_length = true;
t.put(Row::make("Total heap", r.heapAllocs, r.heapArray));
t.put(Row::make("Total stack", r.stackAllocs, r.stackArray));
t.put(Row::make("Total global", r.globalAllocs, r.globalArray));
t.put(Row::make("Max. Heap Allocs", r.maxHeapAllocs));
t.put(Row::make("Max. Stack Allocs", r.maxStackAllocs));
t.put(Row::make("Addresses checked", r.addrChecked));
t.put(Row::make("Distinct Addresses checked", r.seen.size()));
t.put(Row::make("Addresses re-used", r.addrReuses));
t.put(Row::make("Addresses missed", r.addrMissing));
t.put(Row::make("Distinct Addresses missed", r.missing.size()));
t.put(Row::make("Total free heap", r.heapAllocsFree, r.heapArrayFree));
t.put(Row::make("Total free stack", r.stackAllocsFree, r.stackArrayFree));
t.put(Row::make("Null/Zero/NullZero Addr", r.nullAlloc, r.zeroAlloc, r.nullAndZeroAlloc));
t.put(Row::make("User-def. types", r.numUDefTypes));
t.put(Row::make("Estimated memory use (KiB)", size_t(std::round(memory_use.map + memory_use.stack))));
t.put(Row::make("Bytes per node map/stack", memory::MemOverhead::perNodeSizeMap,
memory::MemOverhead::perNodeSizeStack));

t.print(buf);

std::set<int> type_id_set;
const auto fill_set = [&type_id_set](const auto& map) {
for (const auto& [key, val] : map) {
type_id_set.insert(key);
}
};
fill_set(r.heapAlloc);
fill_set(r.globalAlloc);
fill_set(r.stackAlloc);
fill_set(r.heapFree);
fill_set(r.stackFree);

const auto count = [](const auto& map, auto id) {
auto it = map.find(id);
if (it != map.end()) {
return it->second;
}
return 0ll;
};

Table type_table("Allocation type detail (heap, stack, global)");
type_table.table_header = '#';
for (auto type_id : type_id_set) {
type_table.put(Row::make(std::to_string(type_id), count(r.heapAlloc, type_id), count(r.stackAlloc, type_id),
count(r.globalAlloc, type_id), typeart_get_type_name(type_id)));
}

type_table.print(buf);

Table type_table_free("Free allocation type detail (heap, stack)");
type_table_free.table_header = '#';
for (auto type_id : type_id_set) {
type_table_free.put(Row::make(std::to_string(type_id), count(r.heapFree, type_id), count(r.stackFree, type_id),
typeart_get_type_name(type_id)));
}

type_table_free.print(buf);
}
}

} // namespace softcounter

#if ENABLE_SOFTCOUNTER == 1
Expand Down
1 change: 1 addition & 0 deletions lib/runtime/Runtime.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "Runtime.h"

#include "AccessCountPrinter.h"
#include "AccessCounter.h"
#include "RuntimeInterface.h"
#include "support/Logger.h"
Expand Down
Loading

0 comments on commit c45cd64

Please sign in to comment.