#pragma once #ifdef USE_MEASURE # include "../ds/flaglock.h" # include # include # include # define MEASURE_TIME_MARKERS(id, minbits, maxbits, markers) \ static constexpr const char* const id##_time_markers[] = markers; \ static histogram::Global> \ id##_time_global(#id, __FILE__, __LINE__, id##_time_markers); \ static thread_local histogram::Histogram \ id##_time_local(id##_time_global); \ histogram::MeasureTime> \ 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 Global; template< class V, size_t LOW_BITS, size_t HIGH_BITS, size_t INTERMEDIATE_BITS = LOW_BITS> class Histogram { public: using This = Histogram; friend Global; 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::min)(); size_t overflow; size_t count[BUCKETS]; Global* global; public: Histogram() : global(nullptr) {} Histogram(Global& 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 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( value); } static V get_value(size_t index) { return bits:: from_exp_mant(index); } }; template 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 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