Commit 77a5154e authored by Pádraig Ó Conbhuí's avatar Pádraig Ó Conbhuí
Browse files

Add support for passing output options through the environment

parent 7e449d27
Pipeline #2535 failed with stages
in 1 minute and 24 seconds
......@@ -4,13 +4,62 @@
#include <catch2/catch.hpp>
#include <numeric>
#include <chrono>
#include <vector>
// Muck up optimization
volatile extern size_t v;
volatile size_t v = 10;
// Keep the result of t
template<typename T>
void use(const T& t) {
v = std::accumulate(t.begin(), t.end(), size_t(0));
v = 10;
}
// Generate a vector using v so it can't be
// optimized by the compiler (heap elision etc.)
static std::vector<size_t> to_measure() {
std::vector<size_t> value;
value.resize(v);
for(size_t i=0; i<v; i++) {
value[i] = v*i;
}
return value;
}
TEST_CASE("perforate::scoped_trace", "[scoped_trace][benchmark]") {
BENCHMARK("std::chrono::high_resolution_clock::now()") {
return std::chrono::high_resolution_clock::now();
};
BENCHMARK("scoped_trace overhead") { PERFORATE_SCOPED_TRACE(nullptr); };
// Measure the "PERFORATE_SCOPED_TRACE overhead to a loop with PERForate"
// Assume it's around 100ns.
{
PERFORATE_SCOPED_TRACE("perforate::scope_trace loop overhead x1e6");
for(size_t i=0; i<1000000; i++) {
PERFORATE_SCOPED_TRACE("perforate::scope_trace overhead measured");
}
}
// Measure difference between PERFORATE_SCOPE_TRACE inside and outside a loop
{
// Outside loop
{
PERFORATE_SCOPED_TRACE("external measurement x1e6");
for(size_t i=0; i<1000000; i++) {
use(to_measure());
}
}
// Inside loop
for(size_t i=0; i<1000000; i++) {
PERFORATE_SCOPED_TRACE("internal measurement");
use(to_measure());
}
}
}
......@@ -2,6 +2,7 @@
#define PERFORATE_TRACE_HPP
#include <algorithm>
#include <array>
#include <atomic>
#include <chrono>
#include <iostream>
......@@ -11,8 +12,17 @@
#include <vector>
#include <cassert>
#include <cctype>
#include <cinttypes>
#include <cstddef>
#include <cstdlib>
// Some configuration options
#ifndef PERFORATE_OPTIONS_ENABLED_DEFAULT
#define PERFORATE_OPTIONS_ENABLED_DEFAULT true
#endif // PERFORATE_OPTIONS_ENABLED_DEFAULT
// Optional C++ version specific bits
#if __cplusplus >= 201703L
......@@ -139,30 +149,124 @@ void print_range_stats(Stream &stream, const Stats &stats) {
<< " call(s)\n";
}
template <typename Stream, typename StatsPtrList>
template <typename Stream, typename StatsPtrIterator>
void print_range_stats_list(Stream &stream,
const StatsPtrList &stats_ptr_list) {
StatsPtrIterator stats_ptrs_begin,
StatsPtrIterator stats_ptrs_end) {
const auto empty = [](const auto &stats) {
return stats->range_name() == nullptr;
};
const auto registry_empty = std::all_of(std::cbegin(stats_ptr_list),
std::cend(stats_ptr_list), empty);
const auto registry_empty = std::all_of(stats_ptrs_begin,
stats_ptrs_end, empty);
if (!registry_empty) {
stream << "\n------------"
<< "\nRange Stats:"
<< "\n------------\n";
for (const auto &s : stats_ptr_list) {
if (s->range_name() == nullptr)
for(; stats_ptrs_begin != stats_ptrs_end; ++stats_ptrs_begin) {
if ((*stats_ptrs_begin)->range_name() == nullptr)
continue;
print_range_stats(stream, *s);
print_range_stats(stream, **stats_ptrs_begin);
}
stream << "\n------------\n";
}
}
template <typename StatsPtrIterator>
void sort_range_stats_list(StatsPtrIterator stats_ptrs_begin,
StatsPtrIterator stats_ptrs_end,
const std::string &sort_config) {
enum sort_fields { none = 0, title, total, per_call, calls };
// Get "config" strings from sort_config, separated by ","
std::array<sort_fields, 4> configs{};
// Start at first non-space character
size_t pos = sort_config.find_first_not_of(" ,", 0);
size_t next_pos = pos;
size_t configs_pos = 0;
while (pos != sort_config.npos && configs_pos != configs.size()) {
// Find the end of this word.
// If there are no more separators, next_pos is the end of the string
next_pos = sort_config.find_first_of(" ,", pos);
if (next_pos == sort_config.npos)
next_pos = sort_config.size();
// Get this word as a std::string
std::string config_str = sort_config.substr(pos, (next_pos - pos));
// Set the config enum from the parsed config
configs[configs_pos] = ([&] {
if (config_str == "none")
return none;
if (config_str == "title")
return title;
if (config_str == "total")
return total;
if (config_str == "per_call")
return per_call;
if (config_str == "calls")
return calls;
return none;
}());
configs_pos++;
// Find the start of the next word.
// If there are no more words, it will return npos and the loop breaks
pos = sort_config.find_first_not_of(" ,", next_pos);
}
const auto range_gt = [&](const range_stats *a, const range_stats *b) {
for (const auto c : configs) {
switch (c) {
case none:
continue;
case title:
if (a->range_name() > b->range_name())
return true;
if (a->range_name() < b->range_name())
return false;
break;
case total:
if (a->accumulated_time() > b->accumulated_time())
return true;
if (a->accumulated_time() < b->accumulated_time())
return false;
break;
case per_call: {
const auto a_call_count = std::max<int64_t>(a->call_count(), 1);
const auto b_call_count = std::max<int64_t>(b->call_count(), 1);
const auto a_per_call = a->accumulated_time().count() / a_call_count;
const auto b_per_call = b->accumulated_time().count() / b_call_count;
if (a_per_call > b_per_call)
return true;
if (a_per_call < b_per_call)
return false;
break;
}
case calls:
if (a->call_count() > b->call_count())
return true;
if (a->call_count() < b->call_count())
return false;
break;
}
}
return false;
};
std::sort(stats_ptrs_begin, stats_ptrs_end, range_gt);
}
class range_stats_print_on_exit_policy {
std::vector<range_stats *> m_range_stats_list{};
......@@ -179,8 +283,46 @@ public:
return m_tag_range_stats;
}
static std::string default_sort_config() {
return "total, title, per_call, calls";
}
~range_stats_print_on_exit_policy() {
print_range_stats_list(std::cout, m_range_stats_list);
const bool output_enabled = ([] {
const char *output_enabled_env = std::getenv("PERFORATE_ENABLE");
if (output_enabled_env == nullptr)
return PERFORATE_OPTIONS_ENABLED_DEFAULT;
// Get env as lowercase std::string
std::string output_enabled_str = output_enabled_env;
std::transform(output_enabled_str.begin(), output_enabled_str.end(),
output_enabled_str.begin(),
[](unsigned char c) { return std::tolower(c); });
// Any of these will enable perforate output, anything else
// it will remain silent
if (output_enabled_str == "1")
return true;
if (output_enabled_str == "true")
return true;
if (output_enabled_str == "t")
return true;
return false;
}());
if (!output_enabled)
return;
const std::string sort_config = ([] {
const char *sort_config_env = std::getenv("PERFORATE_STATS_SORT");
if (!sort_config_env)
return default_sort_config();
return std::string{sort_config_env};
}());
sort_range_stats_list(m_range_stats_list.begin(), m_range_stats_list.end(), sort_config);
print_range_stats_list(std::cout, m_range_stats_list.cbegin(), m_range_stats_list.cend());
}
};
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment