250 lines
5.3 KiB
C++
250 lines
5.3 KiB
C++
#pragma once
|
|
|
|
#ifdef USE_MEASURE
|
|
# include "../ds/flaglock.h"
|
|
|
|
# include <algorithm>
|
|
# include <iomanip>
|
|
# include <iostream>
|
|
# define MEASURE_TIME_MARKERS(id, minbits, maxbits, markers) \
|
|
static constexpr const char* const id##_time_markers[] = markers; \
|
|
static histogram::Global<histogram::Histogram<uint64_t, minbits, maxbits>> \
|
|
id##_time_global(#id, __FILE__, __LINE__, id##_time_markers); \
|
|
static thread_local histogram::Histogram<uint64_t, minbits, maxbits> \
|
|
id##_time_local(id##_time_global); \
|
|
histogram::MeasureTime<histogram::Histogram<uint64_t, minbits, maxbits>> \
|
|
id##_time(id##_time_local);
|
|
|
|
# define MEASURE_TIME(id, minbits, maxbits) \
|
|
MEASURE_TIME_MARKERS(id, minbits, maxbits, {nullptr})
|
|
|
|
# define MARKERS(...) \
|
|
{ \
|
|
__VA_ARGS__, nullptr \
|
|
}
|
|
|
|
namespace histogram
|
|
{
|
|
using namespace snmalloc;
|
|
|
|
template<class H>
|
|
class Global;
|
|
|
|
template<
|
|
class V,
|
|
size_t LOW_BITS,
|
|
size_t HIGH_BITS,
|
|
size_t INTERMEDIATE_BITS = LOW_BITS>
|
|
class Histogram
|
|
{
|
|
public:
|
|
using This = Histogram<V, LOW_BITS, HIGH_BITS, INTERMEDIATE_BITS>;
|
|
friend Global<This>;
|
|
|
|
static_assert(LOW_BITS < HIGH_BITS, "LOW_BITS must be less than HIGH_BITS");
|
|
|
|
static constexpr V LOW = (V)((size_t)1 << LOW_BITS);
|
|
static constexpr V HIGH = (V)((size_t)1 << HIGH_BITS);
|
|
static constexpr size_t BUCKETS =
|
|
((HIGH_BITS - LOW_BITS) << INTERMEDIATE_BITS) + 2;
|
|
|
|
private:
|
|
V high = (std::numeric_limits<V>::min)();
|
|
size_t overflow;
|
|
size_t count[BUCKETS];
|
|
|
|
Global<This>* global;
|
|
|
|
public:
|
|
Histogram() : global(nullptr) {}
|
|
Histogram(Global<This>& g) : global(&g) {}
|
|
|
|
~Histogram()
|
|
{
|
|
if (global != nullptr)
|
|
global->add(*this);
|
|
}
|
|
|
|
void record(V value)
|
|
{
|
|
if (value > high)
|
|
high = value;
|
|
|
|
if (value >= HIGH)
|
|
{
|
|
overflow++;
|
|
}
|
|
else
|
|
{
|
|
auto i = get_index(value);
|
|
SNMALLOC_ASSERT(i < BUCKETS);
|
|
count[i]++;
|
|
}
|
|
}
|
|
|
|
V get_high()
|
|
{
|
|
return high;
|
|
}
|
|
|
|
size_t get_overflow()
|
|
{
|
|
return overflow;
|
|
}
|
|
|
|
size_t get_buckets()
|
|
{
|
|
return BUCKETS;
|
|
}
|
|
|
|
size_t get_count(size_t index)
|
|
{
|
|
if (index >= BUCKETS)
|
|
return 0;
|
|
|
|
return count[index];
|
|
}
|
|
|
|
static std::pair<V, V> get_range(size_t index)
|
|
{
|
|
if (index >= BUCKETS)
|
|
return std::make_pair(HIGH, HIGH);
|
|
|
|
if (index == 0)
|
|
return std::make_pair(0, get_value(index));
|
|
|
|
return std::make_pair(get_value(index - 1) + 1, get_value(index));
|
|
}
|
|
|
|
void add(This& that)
|
|
{
|
|
high = (std::max)(high, that.high);
|
|
overflow += that.overflow;
|
|
|
|
for (size_t i = 0; i < BUCKETS; i++)
|
|
count[i] += that.count[i];
|
|
}
|
|
|
|
void print(std::ostream& o)
|
|
{
|
|
o << "\tHigh: " << high << std::endl
|
|
<< "\tOverflow: " << overflow << std::endl;
|
|
|
|
size_t grand_total = overflow;
|
|
for (size_t i = 0; i < BUCKETS; i++)
|
|
grand_total += count[i];
|
|
|
|
size_t old_percentage = 0;
|
|
size_t cumulative_total = 0;
|
|
for (size_t i = 0; i < BUCKETS; i++)
|
|
{
|
|
auto r = get_range(i);
|
|
|
|
cumulative_total += count[i];
|
|
|
|
o << "\t" << std::setfill(' ') << std::setw(6) << std::get<0>(r) << ".."
|
|
<< std::setfill(' ') << std::setw(6) << std::get<1>(r) << ": "
|
|
<< std::setfill(' ') << std::setw(10) << count[i];
|
|
|
|
auto percentage = (cumulative_total * 100 / grand_total);
|
|
if (percentage != old_percentage)
|
|
{
|
|
old_percentage = percentage;
|
|
o << std::setfill(' ') << std::setw(20)
|
|
<< (cumulative_total * 100 / grand_total) << "%";
|
|
}
|
|
|
|
o << std::endl;
|
|
}
|
|
}
|
|
|
|
static size_t get_index(V value)
|
|
{
|
|
return bits::to_exp_mant<INTERMEDIATE_BITS, LOW_BITS - INTERMEDIATE_BITS>(
|
|
value);
|
|
}
|
|
|
|
static V get_value(size_t index)
|
|
{
|
|
return bits::
|
|
from_exp_mant<INTERMEDIATE_BITS, LOW_BITS - INTERMEDIATE_BITS>(index);
|
|
}
|
|
};
|
|
|
|
template<class H>
|
|
class Global
|
|
{
|
|
private:
|
|
const char* name;
|
|
const char* file;
|
|
size_t line;
|
|
const char* const* markers;
|
|
|
|
std::atomic_flag lock = ATOMIC_FLAG_INIT;
|
|
H aggregate;
|
|
|
|
public:
|
|
Global(
|
|
const char* name_,
|
|
const char* file_,
|
|
size_t line_,
|
|
const char* const* markers)
|
|
: name(name_), file(file_), line(line_), markers(markers)
|
|
{}
|
|
|
|
~Global()
|
|
{
|
|
print();
|
|
}
|
|
|
|
void add(H& histogram)
|
|
{
|
|
FlagLock f(lock);
|
|
aggregate.add(histogram);
|
|
}
|
|
|
|
private:
|
|
void print()
|
|
{
|
|
std::cout << name;
|
|
|
|
if (markers != nullptr)
|
|
{
|
|
std::cout << ": ";
|
|
size_t i = 0;
|
|
|
|
while (markers[i] != nullptr)
|
|
std::cout << markers[i++] << " ";
|
|
}
|
|
|
|
std::cout << std::endl << file << ":" << line << std::endl;
|
|
|
|
aggregate.print(std::cout);
|
|
}
|
|
};
|
|
|
|
template<class H>
|
|
class MeasureTime
|
|
{
|
|
private:
|
|
H& histogram;
|
|
uint64_t t;
|
|
|
|
public:
|
|
MeasureTime(H& histogram_) : histogram(histogram_)
|
|
{
|
|
t = bits::benchmark_time_start();
|
|
}
|
|
|
|
~MeasureTime()
|
|
{
|
|
histogram.record(bits::benchmark_time_end() - t);
|
|
}
|
|
};
|
|
}
|
|
|
|
#else
|
|
# define MEASURE_TIME(id, minbits, maxbits)
|
|
# define MEASURE_TIME_MARKERS(id, minbits, maxbits, markers)
|
|
#endif
|