Skip to content

Commit

Permalink
Add support for derived metrics (#65)
Browse files Browse the repository at this point in the history
Adds support for derived metrics, which combine multiple hardware counters
into a single value.
  • Loading branch information
bengaineyarm committed Aug 10, 2023
1 parent 1adbda8 commit e11eca6
Show file tree
Hide file tree
Showing 17 changed files with 14,662 additions and 2,915 deletions.
39 changes: 34 additions & 5 deletions examples/api_example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ const char *get_product_family_name(hwcpipe::device::product_id::gpu_family f) {
}
}

void print_sample_value(const hwcpipe::counter_sample &sample) {
switch (sample.type) {
case hwcpipe::counter_sample::type::uint64:
std::cout << sample.value.uint64;
return;
case hwcpipe::counter_sample::type::float64:
std::cout << sample.value.float64;
return;
}
}

int main() {
// Detect all GPUs & print some info
for (const auto &gpu : hwcpipe::find_gpus()) {
Expand Down Expand Up @@ -84,6 +95,11 @@ int main() {
std::cout << "Fragment Active Cycles counter not supported by this GPU." << std::endl;
return -1;
}
ec = config.add_counter(MaliGeomSampleCullRate);
if (ec) {
std::cout << "Geometry Sample Cull Rate counter not supported by this GPU." << std::endl;
return -1;
}

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

Expand All @@ -109,19 +125,32 @@ int main() {
std::cout << ec.message() << std::endl;
continue;
}
uint64_t active_cycles = sample.value;

std::cout << "GPU Active cycles [";
print_sample_value(sample);
std::cout << "] ; ";

// Fetch the Fragment Active Cycles counter
ec = sampler.get_counter_value(MaliFragActiveCy, sample);
if (ec) {
std::cout << ec.message() << std::endl;
continue;
}
uint64_t fragment_cycles = sample.value;

std::cout << " " << std::setw(3) << i << ": "
<< "GPU Active Cycles [" << active_cycles << "] ; "
<< "Fragment Active Cycles [" << fragment_cycles << "]" << std::endl;
std::cout << "Fragment Active Cycles [";
print_sample_value(sample);
std::cout << "] ; ";

// Fetch the Geometry Sample Cull Rate counter
ec = sampler.get_counter_value(MaliGeomSampleCullRate, sample);
if (ec) {
std::cout << ec.message() << std::endl;
continue;
}

std::cout << "Geometry Sample Cull Rate [";
print_sample_value(sample);
std::cout << "]" << std::endl;
}

ec = sampler.stop_sampling();
Expand Down
1 change: 1 addition & 0 deletions hwcpipe/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ add_library(hwcpipe
src/hwcpipe/detail/counter_database.cpp
src/hwcpipe/all_gpu_counters.cpp
src/hwcpipe/counter_metadata.cpp
src/hwcpipe/derived_functions.cpp
)

if(HWCPIPE_PIC)
Expand Down
4 changes: 2 additions & 2 deletions hwcpipe/include/hwcpipe/detail/counter_database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
namespace hwcpipe {
namespace detail {

using backing_map_type = std::unordered_map<hwcpipe_counter, block_offset>;
using backing_map_type = std::unordered_map<hwcpipe_counter, detail::counter_definition>;

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

} // namespace detail
Expand Down
160 changes: 158 additions & 2 deletions hwcpipe/include/hwcpipe/detail/internal_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#pragma once

#include "hwcpipe/hwcpipe_counter.h"
#include "hwcpipe/types.hpp"

#include <device/handle.hpp>
Expand All @@ -14,24 +15,179 @@
#include <device/hwcnt/sampler/manual.hpp>
#include <device/instance.hpp>

#include <cstdint>
#include <initializer_list>
#include <system_error>
namespace hwcpipe {
namespace detail {

namespace expression {
/**
* Structure representing the block type/offset address of a counter within
* a particular GPU's PMU data.
* The expression evaluation context provides an abstraction over some block of
* hardware counter storage. It breaks an otherwise cyclic dependency between
* the sampler class & the expression evaluator function type.
*/
class context {
public:
/**
* @brief Returns the value a hardware counter from the sampler to be used
* in the expression evaluator
*
* @param counter Hardware counter to get the value for.
* @return the counter value
*/
HWCP_NODISCARD virtual double get_counter_value(hwcpipe_counter counter) const = 0;

/**
* @brief Returns the AXI bus width in bytes
*/
HWCP_NODISCARD virtual double get_mali_config_ext_bus_byte_size() const = 0;

/**
* @brief Returns the number of shader core (constant) to be used
* in evaluator function
*/
HWCP_NODISCARD virtual double get_mali_config_shader_core_count() const = 0;

/**
* @brief
*/
HWCP_NODISCARD virtual double get_mali_config_l2_cache_count() const = 0;
};

// Signature for generated evaluation functions.
using evaluator = double (*)(const context &);
/**
* Holds information about the expression that the sampler will need when
* registering the counters and evaluating.
*/
struct expression_definition {
/**
* Pointer to the function that will evaluate the expression and return the
* calculated result.
*/
evaluator eval;
/**
* The list of counters that this expression depends on. These will need to
* be implicitly registered with the sampler so that they can be collected
* when it is polled.
*/
const std::initializer_list<hwcpipe_counter> dependencies;
};

} // namespace expression
/**
* Structure representing the block type/offset address and shift
* scaling of a counter within a particular GPU's PMU data.
*/
struct block_offset {
uint32_t offset;
uint32_t shift;
hwcpipe::device::hwcnt::block_type block_type;
};

/**
* Counters can either be raw hardware counters, or they are expressions based
* on some combination of hardware counters, constants or other expressions.
*
* For hardware counters, we need to know their block/offset addresses so that
* we can read them as we iterate over the block types. For expression counters
* we need to call the function that will evaluate the formula.
*/
struct counter_definition {
enum class type { invalid, hardware, expression };
union u {
block_offset address{};
expression::expression_definition expression;
explicit u(expression::expression_definition expression)
: expression(expression) {}
explicit u(block_offset address)
: address(address) {}
} u;
type tag;

counter_definition()
: tag(type::invalid)
, u(block_offset{0, 0, device::hwcnt::block_type::fe}) {}
explicit counter_definition(expression::expression_definition expression)
: tag(type::expression)
, u(expression) {}
explicit counter_definition(block_offset address)
: tag(type::hardware)
, u(address) {}
};

struct hwcpipe_backend_policy {
using handle_type = device::handle;
using instance_type = device::instance;
using sampler_type = device::hwcnt::sampler::manual;
using sample_type = device::hwcnt::sample;
};

/**
* This class should **only** be used in the derived expressions (derived_functions.cpp).
*
* Used to override the result of 0.0 / 0.0 to return 0.0. Operators for other types included
* to resolve ambiguity.
*/
class hwcpipe_double {
public:
double value;

hwcpipe_double(double value)
: value(value) {}

hwcpipe_double operator+(const hwcpipe_double &other) const { return hwcpipe_double(value + other.value); }

hwcpipe_double operator-(const hwcpipe_double &other) const { return hwcpipe_double(value - other.value); }

hwcpipe_double operator*(const hwcpipe_double &other) const { return hwcpipe_double(value * other.value); }

hwcpipe_double operator/(const hwcpipe_double &other) const {
if (value == 0.0 && other.value == 0.0) {
return hwcpipe_double(0.0);
}
return hwcpipe_double(value / other.value);
}

hwcpipe_double operator+(const int &other) const { return hwcpipe_double(value + other); }

hwcpipe_double operator-(const int &other) const { return hwcpipe_double(value - other); }

hwcpipe_double operator*(const int &other) const { return hwcpipe_double(value * other); }

hwcpipe_double operator/(const int &other) const {
if (value == 0.0 && other == 0) {
return hwcpipe_double(0.0);
}
return hwcpipe_double(value / other);
}

hwcpipe_double operator+(const double &other) const { return hwcpipe_double(value + other); }

hwcpipe_double operator-(const double &other) const { return hwcpipe_double(value - other); }

hwcpipe_double operator*(const double &other) const { return hwcpipe_double(value * other); }

hwcpipe_double operator/(const double &other) const {
if (value == 0.0 && other == 0.0) {
return hwcpipe_double(0.0);
}
return hwcpipe_double(value / other);
}

bool operator<(const hwcpipe_double &other) const { return value < other.value; }

bool operator<=(const hwcpipe_double &other) const { return value <= other.value; }

bool operator>(const hwcpipe_double &other) const { return value > other.value; }

bool operator>=(const hwcpipe_double &other) const { return value >= other.value; }

bool operator==(const hwcpipe_double &other) const { return value == other.value; }

operator double() const { return value; }
};

} // namespace detail
} // namespace hwcpipe
Loading

0 comments on commit e11eca6

Please sign in to comment.