Skip to content

Commit e11eca6

Browse files
authored
Add support for derived metrics (#65)
Adds support for derived metrics, which combine multiple hardware counters into a single value.
1 parent 1adbda8 commit e11eca6

17 files changed

+14662
-2915
lines changed

examples/api_example.cpp

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ const char *get_product_family_name(hwcpipe::device::product_id::gpu_family f) {
2929
}
3030
}
3131

32+
void print_sample_value(const hwcpipe::counter_sample &sample) {
33+
switch (sample.type) {
34+
case hwcpipe::counter_sample::type::uint64:
35+
std::cout << sample.value.uint64;
36+
return;
37+
case hwcpipe::counter_sample::type::float64:
38+
std::cout << sample.value.float64;
39+
return;
40+
}
41+
}
42+
3243
int main() {
3344
// Detect all GPUs & print some info
3445
for (const auto &gpu : hwcpipe::find_gpus()) {
@@ -84,6 +95,11 @@ int main() {
8495
std::cout << "Fragment Active Cycles counter not supported by this GPU." << std::endl;
8596
return -1;
8697
}
98+
ec = config.add_counter(MaliGeomSampleCullRate);
99+
if (ec) {
100+
std::cout << "Geometry Sample Cull Rate counter not supported by this GPU." << std::endl;
101+
return -1;
102+
}
87103

88104
auto sampler = hwcpipe::sampler<>(config);
89105

@@ -109,19 +125,32 @@ int main() {
109125
std::cout << ec.message() << std::endl;
110126
continue;
111127
}
112-
uint64_t active_cycles = sample.value;
128+
129+
std::cout << "GPU Active cycles [";
130+
print_sample_value(sample);
131+
std::cout << "] ; ";
113132

114133
// Fetch the Fragment Active Cycles counter
115134
ec = sampler.get_counter_value(MaliFragActiveCy, sample);
116135
if (ec) {
117136
std::cout << ec.message() << std::endl;
118137
continue;
119138
}
120-
uint64_t fragment_cycles = sample.value;
121139

122-
std::cout << " " << std::setw(3) << i << ": "
123-
<< "GPU Active Cycles [" << active_cycles << "] ; "
124-
<< "Fragment Active Cycles [" << fragment_cycles << "]" << std::endl;
140+
std::cout << "Fragment Active Cycles [";
141+
print_sample_value(sample);
142+
std::cout << "] ; ";
143+
144+
// Fetch the Geometry Sample Cull Rate counter
145+
ec = sampler.get_counter_value(MaliGeomSampleCullRate, sample);
146+
if (ec) {
147+
std::cout << ec.message() << std::endl;
148+
continue;
149+
}
150+
151+
std::cout << "Geometry Sample Cull Rate [";
152+
print_sample_value(sample);
153+
std::cout << "]" << std::endl;
125154
}
126155

127156
ec = sampler.stop_sampling();

hwcpipe/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ add_library(hwcpipe
1111
src/hwcpipe/detail/counter_database.cpp
1212
src/hwcpipe/all_gpu_counters.cpp
1313
src/hwcpipe/counter_metadata.cpp
14+
src/hwcpipe/derived_functions.cpp
1415
)
1516

1617
if(HWCPIPE_PIC)

hwcpipe/include/hwcpipe/detail/counter_database.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
namespace hwcpipe {
1616
namespace detail {
1717

18-
using backing_map_type = std::unordered_map<hwcpipe_counter, block_offset>;
18+
using backing_map_type = std::unordered_map<hwcpipe_counter, detail::counter_definition>;
1919

2020
/**
2121
* @brief An type that provides an enumerable view over the counters for a
@@ -138,7 +138,7 @@ class counter_database {
138138
* @brief Queries the database to find the block/offset address of a counter
139139
* for the specified GPU.
140140
*/
141-
HWCP_NODISCARD block_offset get_counter_address(gpu_id_type id, hwcpipe_counter counter, std::error_code &ec);
141+
HWCP_NODISCARD counter_definition get_counter_def(gpu_id_type id, hwcpipe_counter counter, std::error_code &ec);
142142
};
143143

144144
} // namespace detail

hwcpipe/include/hwcpipe/detail/internal_types.hpp

Lines changed: 158 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#pragma once
88

9+
#include "hwcpipe/hwcpipe_counter.h"
910
#include "hwcpipe/types.hpp"
1011

1112
#include <device/handle.hpp>
@@ -14,24 +15,179 @@
1415
#include <device/hwcnt/sampler/manual.hpp>
1516
#include <device/instance.hpp>
1617

18+
#include <cstdint>
19+
#include <initializer_list>
20+
#include <system_error>
1721
namespace hwcpipe {
1822
namespace detail {
1923

24+
namespace expression {
2025
/**
21-
* Structure representing the block type/offset address of a counter within
22-
* a particular GPU's PMU data.
26+
* The expression evaluation context provides an abstraction over some block of
27+
* hardware counter storage. It breaks an otherwise cyclic dependency between
28+
* the sampler class & the expression evaluator function type.
29+
*/
30+
class context {
31+
public:
32+
/**
33+
* @brief Returns the value a hardware counter from the sampler to be used
34+
* in the expression evaluator
35+
*
36+
* @param counter Hardware counter to get the value for.
37+
* @return the counter value
38+
*/
39+
HWCP_NODISCARD virtual double get_counter_value(hwcpipe_counter counter) const = 0;
40+
41+
/**
42+
* @brief Returns the AXI bus width in bytes
43+
*/
44+
HWCP_NODISCARD virtual double get_mali_config_ext_bus_byte_size() const = 0;
45+
46+
/**
47+
* @brief Returns the number of shader core (constant) to be used
48+
* in evaluator function
49+
*/
50+
HWCP_NODISCARD virtual double get_mali_config_shader_core_count() const = 0;
51+
52+
/**
53+
* @brief
54+
*/
55+
HWCP_NODISCARD virtual double get_mali_config_l2_cache_count() const = 0;
56+
};
57+
58+
// Signature for generated evaluation functions.
59+
using evaluator = double (*)(const context &);
60+
/**
61+
* Holds information about the expression that the sampler will need when
62+
* registering the counters and evaluating.
63+
*/
64+
struct expression_definition {
65+
/**
66+
* Pointer to the function that will evaluate the expression and return the
67+
* calculated result.
68+
*/
69+
evaluator eval;
70+
/**
71+
* The list of counters that this expression depends on. These will need to
72+
* be implicitly registered with the sampler so that they can be collected
73+
* when it is polled.
74+
*/
75+
const std::initializer_list<hwcpipe_counter> dependencies;
76+
};
77+
78+
} // namespace expression
79+
/**
80+
* Structure representing the block type/offset address and shift
81+
* scaling of a counter within a particular GPU's PMU data.
2382
*/
2483
struct block_offset {
2584
uint32_t offset;
85+
uint32_t shift;
2686
hwcpipe::device::hwcnt::block_type block_type;
2787
};
2888

89+
/**
90+
* Counters can either be raw hardware counters, or they are expressions based
91+
* on some combination of hardware counters, constants or other expressions.
92+
*
93+
* For hardware counters, we need to know their block/offset addresses so that
94+
* we can read them as we iterate over the block types. For expression counters
95+
* we need to call the function that will evaluate the formula.
96+
*/
97+
struct counter_definition {
98+
enum class type { invalid, hardware, expression };
99+
union u {
100+
block_offset address{};
101+
expression::expression_definition expression;
102+
explicit u(expression::expression_definition expression)
103+
: expression(expression) {}
104+
explicit u(block_offset address)
105+
: address(address) {}
106+
} u;
107+
type tag;
108+
109+
counter_definition()
110+
: tag(type::invalid)
111+
, u(block_offset{0, 0, device::hwcnt::block_type::fe}) {}
112+
explicit counter_definition(expression::expression_definition expression)
113+
: tag(type::expression)
114+
, u(expression) {}
115+
explicit counter_definition(block_offset address)
116+
: tag(type::hardware)
117+
, u(address) {}
118+
};
119+
29120
struct hwcpipe_backend_policy {
30121
using handle_type = device::handle;
31122
using instance_type = device::instance;
32123
using sampler_type = device::hwcnt::sampler::manual;
33124
using sample_type = device::hwcnt::sample;
34125
};
35126

127+
/**
128+
* This class should **only** be used in the derived expressions (derived_functions.cpp).
129+
*
130+
* Used to override the result of 0.0 / 0.0 to return 0.0. Operators for other types included
131+
* to resolve ambiguity.
132+
*/
133+
class hwcpipe_double {
134+
public:
135+
double value;
136+
137+
hwcpipe_double(double value)
138+
: value(value) {}
139+
140+
hwcpipe_double operator+(const hwcpipe_double &other) const { return hwcpipe_double(value + other.value); }
141+
142+
hwcpipe_double operator-(const hwcpipe_double &other) const { return hwcpipe_double(value - other.value); }
143+
144+
hwcpipe_double operator*(const hwcpipe_double &other) const { return hwcpipe_double(value * other.value); }
145+
146+
hwcpipe_double operator/(const hwcpipe_double &other) const {
147+
if (value == 0.0 && other.value == 0.0) {
148+
return hwcpipe_double(0.0);
149+
}
150+
return hwcpipe_double(value / other.value);
151+
}
152+
153+
hwcpipe_double operator+(const int &other) const { return hwcpipe_double(value + other); }
154+
155+
hwcpipe_double operator-(const int &other) const { return hwcpipe_double(value - other); }
156+
157+
hwcpipe_double operator*(const int &other) const { return hwcpipe_double(value * other); }
158+
159+
hwcpipe_double operator/(const int &other) const {
160+
if (value == 0.0 && other == 0) {
161+
return hwcpipe_double(0.0);
162+
}
163+
return hwcpipe_double(value / other);
164+
}
165+
166+
hwcpipe_double operator+(const double &other) const { return hwcpipe_double(value + other); }
167+
168+
hwcpipe_double operator-(const double &other) const { return hwcpipe_double(value - other); }
169+
170+
hwcpipe_double operator*(const double &other) const { return hwcpipe_double(value * other); }
171+
172+
hwcpipe_double operator/(const double &other) const {
173+
if (value == 0.0 && other == 0.0) {
174+
return hwcpipe_double(0.0);
175+
}
176+
return hwcpipe_double(value / other);
177+
}
178+
179+
bool operator<(const hwcpipe_double &other) const { return value < other.value; }
180+
181+
bool operator<=(const hwcpipe_double &other) const { return value <= other.value; }
182+
183+
bool operator>(const hwcpipe_double &other) const { return value > other.value; }
184+
185+
bool operator>=(const hwcpipe_double &other) const { return value >= other.value; }
186+
187+
bool operator==(const hwcpipe_double &other) const { return value == other.value; }
188+
189+
operator double() const { return value; }
190+
};
191+
36192
} // namespace detail
37193
} // namespace hwcpipe

0 commit comments

Comments
 (0)