OXYGENHUB
◆ main openmsx / src / utils / ProfileCounters.hh
4.0 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef PROFILECOUNTERS_HH
2
#define PROFILECOUNTERS_HH
3
4
#include "enumerate.hh"
5
#include <array>
6
#include <iostream>
7
8
//
9
// Quick and dirty reflection on C++ enums (in the future replace with c++23 reflexpr).
10
//
11
12
// Print an enum type as a string, for example:
13
//   os << EnumTypeName<SomeEnumType>() << '\n';
14
template<typename E> struct EnumTypeName {};
15
// You _must_ overload 'operator<<' for all enum types for which you want this to work.
16
template<typename E> std::ostream& operator<<(std::ostream& os, EnumTypeName<E>) = delete;
17
18
// Print an enum value as a string, for example:
19
//   os << EnumValueName{myEnumValue} << '\n';
20
template<typename E> struct EnumValueName
21
{
22
	explicit EnumValueName(E e_) : e(e_) {}
23
	E e;
24
};
25
// You _must_ overload 'operator<<' for all enum types for which you want this to work.
26
template<typename E> std::ostream& operator<<(std::ostream& os, EnumValueName<E> evn) = delete;
27
28
29
30
31
// A collection of (simple) profile counters:
32
// - Counters start at zero.
33
// - An individual counter can be incremented by 1 via 'tick(<counter-id>)'.
34
// - When this 'ProfileCounters' object is destroyed it prints the value of each
35
//   counter.
36
//
37
// Template parameters:
38
// - bool ENABLED:
39
//    when false, the optimizer should be able to completely optimize-out all
40
//    profile related code
41
// - typename ENUM:
42
//    Must be a c++ enum (or enum class) which satisfies the following requirements:
43
//    * The numerical values must be 0, 1, ... IOW the values must be usable as
44
//      indices in an array. (This is automatically the case if you don't
45
//      manually assign numeric values).
46
//    * There must be an enum value with the name 'NUM' which has a numerical
47
//      value equal to the number of other values in this enum. (This is
48
//      automatically the case if you put 'NUM' last in the list of enum
49
//      values).
50
//    * You must overload the following two functions for the type 'ENUM':
51
//          std::ostream& operator<<(std::ostream& os, EnumTypeName<ENUM>);
52
//          std::ostream& operator<<(std::ostream& os, EnumValueName<ENUM> evn);
53
//
54
// Example usage:
55
//    enum class WidgetProfileCounter {
56
//        CALCULATE,
57
//        INVALIDATE,
58
//        NUM // must be last
59
//    };
60
//    std::ostream& operator<<(std::ostream& os, EnumTypeName<WidgetProfileCounter>)
61
//    {
62
//        return os << "WidgetProfileCounter";
63
//    }
64
//    std::ostream& operator<<(std::ostream& os, EnumValueName<WidgetProfileCounter> evn)
65
//    {
66
//        std::string_view names[size_t(WidgetProfileCounter::NUM)] = {
67
//            "calculate"sv, "invalidate"sv
68
//        };
69
//        return os << names[size_t(evn.e)];
70
//    }
71
//
72
//    constexpr bool PRINT_WIDGET_PROFILE = true; // possibly depending on some #define
73
//
74
//    struct Widget : private ProfileCounters<PRINT_WIDGET_PROFILE, WidgetProfileCounter>
75
//    {
76
//        void calculate() {
77
//            tick(WidgetProfileCounter::CALCULATE);
78
//            // ...
79
//        }
80
//        void invalidate() {
81
//            tick(WidgetProfileCounter::CALCULATE);
82
//            // ...
83
//        }
84
//    };
85
86
template<bool ENABLED, typename ENUM>
87
class ProfileCounters
88
{
89
public:
90
	ProfileCounters(const ProfileCounters&) = delete;
91
	ProfileCounters(ProfileCounters&&) = delete;
92
	ProfileCounters& operator=(const ProfileCounters&) = delete;
93
	ProfileCounters& operator=(ProfileCounters&&) = delete;
94
95
	// Print counters on destruction.
96
	~ProfileCounters() {
97
		std::cout << "-- Profile counters: " << EnumTypeName<ENUM>() << " -- " << static_cast<void*>(this) << '\n';
98
		for (auto [i, count] : enumerate(counters)) {
99
			std::cout << EnumValueName{ENUM(i)} << ": " << count << '\n';
100
		}
101
	}
102
103
	// Increment
104
	void tick(ENUM e) const {
105
		++counters[size_t(e)];
106
	}
107
108
private:
109
	static constexpr auto NUM = size_t(ENUM::NUM); // value 'ENUM::NUM' must exist
110
	mutable std::array<unsigned, NUM> counters = {};
111
};
112
113
114
// Specialization for when profiling is disabled.
115
// This class is simple enough so that it can be completely optimized away.
116
template<typename ENUM>
117
class ProfileCounters<false, ENUM>
118
{
119
public:
120
	void tick(ENUM) const { /*nothing*/ }
121
};
122
123
#endif