SWDEV-477244: Select() Expression Dimension Support (#1091)
* add support for select function in derived counters * formatting * renaming select dims variable name from set to map * format * Update doc with select() for dimensions * use : for defining range of values in select dims * - update dimension for metric after select. - make sure to raise runtime error if user provides range for a dimension. * use map instead of unordered_map for select dim info * new line EOF * fix bug: select() operator. * Update evaluate_ast.cpp format * added a check for dim value exceeds max. * Update source/lib/rocprofiler-sdk/counters/evaluate_ast.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update source/lib/rocprofiler-sdk/counters/evaluate_ast.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * updated doc with data example for select operation. * changelog.md * Update CHANGELOG.md --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
此提交包含在:
+11
@@ -136,3 +136,14 @@ Full documentation for ROCprofiler-SDK is available at [rocm.docs.amd.com/projec
|
||||
|
||||
- Removed gfx8 metric definitions
|
||||
- Removed `rocprofv3` installation to sbin directory
|
||||
|
||||
## ROCprofiler-SDK 0.5.0 for ROCm release 6.4
|
||||
|
||||
### Added
|
||||
|
||||
- Added support for select() operation in counter expression.
|
||||
### Changed
|
||||
|
||||
### Resolved issues
|
||||
|
||||
### Removed
|
||||
|
||||
@@ -290,6 +290,51 @@ Expression: 100*reduce(GL2C_HIT,sum)/(reduce(GL2C_HIT,sum)+reduce(GL2C_MISS,sum)
|
||||
The reduce function reduces counter values across all dimensions such as shader engine, SIMD, and so on, to produce a single output value. This helps to collect and compare values across the entire device.
|
||||
Here are the common reduction operations:
|
||||
|
||||
### Select Function
|
||||
|
||||
```yaml
|
||||
expression: select(Y, [DIMENSION_XCC=[0],DIMENSION_SHADER_ENGINE=[2]])
|
||||
```
|
||||
|
||||
select() only returns counter values which match the dimension indexes provided by the user in expression. This operation is to allow a user to state they only want to select specific dimensions index. Supported dimensions include ```DIMENSION_XCC, DIMENSION_AID, DIMENSION_SHADER_ENGINE, DIMENSION_AGENT, DIMENSION_SHADER_ARRAY, DIMENSION_WGP, DIMENSION_INSTANCE```. For example ``select(Y, [DIMENSION_XCC=[0],DIMENSION_SHADER_ENGINE=[2]])`` gives counter values which are from DIMENSION_XCC= 0 and DIMENSION_SHADER_ENGINE= 2 for Y Metric.
|
||||
|
||||
Let's say Y has XCC, SHADER_ENGINE(SE), WGP dimensions with sizes 2, 4, 4 respectively.
|
||||
|
||||
Raw Counter Data in 3D space:
|
||||
|
||||
#### XCC[0]:
|
||||
| |WGP[0]|WGP[1]|WGP[2]|WGP[3]|
|
||||
|-------|------|------|------|------|
|
||||
| SE[0] | 1 | 2 | 3 | 4 |
|
||||
| SE[1] | 5 | 6 | 7 | 8 |
|
||||
| SE[2] | 9 | 10 | 11 | 12 |
|
||||
| SE[3] | 13 | 14 | 15 | 16 |
|
||||
|
||||
#### XCC[1]:
|
||||
| |WGP[0]|WGP[1]|WGP[2]|WGP[3]|
|
||||
|-------|------|------|------|------|
|
||||
| SE[0] | 17 | 18 | 19 | 20 |
|
||||
| SE[1] | 21 | 22 | 23 | 24 |
|
||||
| SE[2] | 25 | 26 | 27 | 28 |
|
||||
| SE[3] | 29 | 30 | 31 | 32 |
|
||||
|
||||
Selecting at XCC=0 results to 2D space with WGP and SH dimensions, as shown below.
|
||||
|
||||
| |WGP[0]|WGP[1]|WGP[2]|WGP[3]|
|
||||
|-------|------|------|------|------|
|
||||
| SE[0] | 1 | 2 | 3 | 4 |
|
||||
| SE[1] | 5 | 6 | 7 | 8 |
|
||||
| SE[2] | 9 | 10 | 11 | 12 |
|
||||
| SE[3] | 13 | 14 | 15 | 16 |
|
||||
|
||||
similarly, for `select(Y, [DIMENSION_XCC=[0],DIMENSION_SHADER_ENGINE=[2]])` results in only WGP dimension with XCC=0 and SE=2.
|
||||
|
||||
| |WGP[0]|WGP[1]|WGP[2]|WGP[3]|
|
||||
|-------|------|------|------|------|
|
||||
| | 9 | 10 | 11 | 12 |
|
||||
|
||||
### Accumulate Function
|
||||
|
||||
- `sum`: Sums to create a single output. For example, `reduce(GL2C_HIT,sum)` sums all `GL2C_HIT` hardware register values.
|
||||
- `avr`: Calculates the average across all dimensions.
|
||||
- `min`: Selects minimum value across all dimensions.
|
||||
|
||||
@@ -22,10 +22,14 @@
|
||||
|
||||
#include "lib/rocprofiler-sdk/counters/evaluate_ast.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/ranges.h>
|
||||
@@ -33,7 +37,9 @@
|
||||
|
||||
#include "lib/common/utility.hpp"
|
||||
#include "lib/rocprofiler-sdk/counters/dimensions.hpp"
|
||||
#include "lib/rocprofiler-sdk/counters/id_decode.hpp"
|
||||
#include "lib/rocprofiler-sdk/counters/parser/reader.hpp"
|
||||
#include "rocprofiler-sdk/fwd.h"
|
||||
|
||||
namespace rocprofiler
|
||||
{
|
||||
@@ -128,6 +134,75 @@ perform_reduction(ReduceOperation reduce_op, std::vector<rocprofiler_record_coun
|
||||
return input_array;
|
||||
}
|
||||
|
||||
int64_t
|
||||
get_int_encoded_dimensions_from_string(const std::string& rangeStr)
|
||||
{
|
||||
int64_t result = 0;
|
||||
std::istringstream iss(rangeStr);
|
||||
std::string token;
|
||||
size_t bit_length = DIM_BIT_LENGTH / ROCPROFILER_DIMENSION_LAST;
|
||||
|
||||
while(std::getline(iss, token, ','))
|
||||
{
|
||||
token.erase(std::remove_if(token.begin(), token.end(), ::isspace), token.end());
|
||||
|
||||
size_t dash_pos = token.find(':');
|
||||
if(dash_pos != std::string::npos)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
fmt::format("Range based selection not supported by Dimension API. only select "
|
||||
"single value for each dimension."));
|
||||
int start = std::stoi(token.substr(0, dash_pos));
|
||||
int end = std::stoi(token.substr(dash_pos + 1));
|
||||
result |= (1LL << std::min(64, end + 1)) - (1LL << std::max(start, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
int num = std::stoi(token);
|
||||
if(num < (1 << bit_length))
|
||||
{
|
||||
result |= (1LL << num);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(fmt::format("Dimension value exceeds max allowed."));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<rocprofiler_record_counter_t>*
|
||||
perform_selection(std::map<rocprofiler_profile_counter_instance_types, std::string>& dimension_map,
|
||||
std::vector<rocprofiler_record_counter_t>* input_array)
|
||||
{
|
||||
if(input_array->empty()) return input_array;
|
||||
for(auto& dim_pair : dimension_map)
|
||||
{
|
||||
int64_t encoded_dim_values = get_int_encoded_dimensions_from_string(dim_pair.second);
|
||||
size_t bit_length = DIM_BIT_LENGTH / ROCPROFILER_DIMENSION_LAST;
|
||||
int64_t mask = (MAX_64 >> (64 - bit_length)) << ((dim_pair.first - 1) * bit_length);
|
||||
|
||||
input_array->erase(std::remove_if(input_array->begin(),
|
||||
input_array->end(),
|
||||
[&](rocprofiler_record_counter_t& rec) {
|
||||
bool should_remove =
|
||||
(encoded_dim_values &
|
||||
(1 << rocprofiler::counters::rec_to_dim_pos(
|
||||
rec.id, dim_pair.first))) == 0;
|
||||
if(!should_remove)
|
||||
{
|
||||
rec.id = rec.id | mask;
|
||||
rec.id = rec.id ^ mask;
|
||||
}
|
||||
return should_remove;
|
||||
}),
|
||||
input_array->end());
|
||||
}
|
||||
|
||||
return input_array;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const std::unordered_map<std::string, EvaluateASTMap>&
|
||||
@@ -214,6 +289,7 @@ EvaluateAST::EvaluateAST(rocprofiler_counter_id_t out_id,
|
||||
, _reduce_op(get_reduce_op_type_from_string(ast.reduce_op))
|
||||
, _agent(std::move(agent))
|
||||
, _reduce_dimension_set(ast.reduce_dimension_set)
|
||||
, _select_dimension_map(ast.select_dimension_map)
|
||||
, _out_id(out_id)
|
||||
{
|
||||
if(_type == NodeType::REFERENCE_NODE || _type == NodeType::ACCUMULATE_NODE)
|
||||
@@ -308,7 +384,25 @@ EvaluateAST::set_dimensions()
|
||||
break;
|
||||
case SELECT_NODE:
|
||||
{
|
||||
// TODO: future scope
|
||||
auto first = _children[0].set_dimensions();
|
||||
first.erase(std::remove_if(first.begin(),
|
||||
first.end(),
|
||||
[&](const MetricDimension& dim) {
|
||||
return _select_dimension_map.find(dim.type()) !=
|
||||
_select_dimension_map.end();
|
||||
}),
|
||||
first.end());
|
||||
if(first.empty())
|
||||
{
|
||||
_dimension_types = std::vector<MetricDimension>{
|
||||
{dimension_map().at(ROCPROFILER_DIMENSION_INSTANCE),
|
||||
1,
|
||||
ROCPROFILER_DIMENSION_INSTANCE}};
|
||||
}
|
||||
else
|
||||
{
|
||||
_dimension_types = first;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -675,8 +769,11 @@ EvaluateAST::evaluate(
|
||||
static_cast<int>(_reduce_op)));
|
||||
return perform_reduction(_reduce_op, result);
|
||||
}
|
||||
// Currently unsupported
|
||||
case SELECT_NODE: break;
|
||||
case SELECT_NODE:
|
||||
{
|
||||
auto* result = _children.at(0).evaluate(results_map, cache);
|
||||
return perform_selection(_select_dimension_map, result);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "lib/rocprofiler-sdk/aql/packet_construct.hpp"
|
||||
#include "lib/rocprofiler-sdk/counters/dimensions.hpp"
|
||||
#include "lib/rocprofiler-sdk/counters/id_decode.hpp"
|
||||
#include "lib/rocprofiler-sdk/counters/metrics.hpp"
|
||||
#include "lib/rocprofiler-sdk/counters/parser/raw_ast.hpp"
|
||||
|
||||
@@ -166,17 +167,18 @@ public:
|
||||
const rocprofiler_counter_id_t& out_id() const { return _out_id; }
|
||||
|
||||
private:
|
||||
NodeType _type{NONE};
|
||||
ReduceOperation _reduce_op{REDUCE_NONE};
|
||||
Metric _metric;
|
||||
double _raw_value{0};
|
||||
std::vector<EvaluateAST> _children;
|
||||
std::string _agent;
|
||||
std::vector<MetricDimension> _dimension_types{};
|
||||
std::vector<rocprofiler_record_counter_t> _static_value;
|
||||
std::unordered_set<rocprofiler_profile_counter_instance_types> _reduce_dimension_set;
|
||||
bool _expanded{false};
|
||||
rocprofiler_counter_id_t _out_id{.handle = 0};
|
||||
NodeType _type{NONE};
|
||||
ReduceOperation _reduce_op{REDUCE_NONE};
|
||||
Metric _metric;
|
||||
double _raw_value{0};
|
||||
std::vector<EvaluateAST> _children;
|
||||
std::string _agent;
|
||||
std::vector<MetricDimension> _dimension_types{};
|
||||
std::vector<rocprofiler_record_counter_t> _static_value;
|
||||
std::unordered_set<rocprofiler_profile_counter_instance_types> _reduce_dimension_set;
|
||||
std::map<rocprofiler_profile_counter_instance_types, std::string> _select_dimension_map;
|
||||
bool _expanded{false};
|
||||
rocprofiler_counter_id_t _out_id{.handle = 0};
|
||||
};
|
||||
|
||||
using EvaluateASTMap = std::unordered_map<std::string, EvaluateAST>;
|
||||
|
||||
@@ -23,13 +23,16 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "lib/rocprofiler-sdk/agent.hpp"
|
||||
#include "lib/rocprofiler-sdk/counters/evaluate_ast.hpp"
|
||||
#include "lib/rocprofiler-sdk/counters/id_decode.hpp"
|
||||
#include "lib/rocprofiler-sdk/counters/parser/reader.hpp"
|
||||
|
||||
namespace
|
||||
@@ -364,6 +367,7 @@ construct_test_data_dim(
|
||||
record.counter_value =
|
||||
static_cast<double>(rand()) / (static_cast<double>(RAND_MAX / 50000)) + 1.0;
|
||||
auto recursive_dim = construct_test_data_dim(record.id, dims, dim_size);
|
||||
if(!dims.empty()) ret.pop_back();
|
||||
ret.insert(ret.end(), recursive_dim.begin(), recursive_dim.end());
|
||||
}
|
||||
return ret;
|
||||
@@ -1233,3 +1237,132 @@ TEST(evaluate_ast, derived_counter_reduction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(evatuate_ast, evaluate_select)
|
||||
{
|
||||
using namespace rocprofiler::counters;
|
||||
|
||||
auto get_base_rec_id = [](uint64_t counter_id) {
|
||||
rocprofiler_counter_instance_id_t base_id = 0;
|
||||
set_counter_in_rec(base_id, {.handle = counter_id});
|
||||
return base_id;
|
||||
};
|
||||
|
||||
auto select_dim =
|
||||
[](auto a,
|
||||
std::unordered_map<rocprofiler::counters::rocprofiler_profile_counter_instance_types,
|
||||
std::vector<int>> dims) -> auto
|
||||
{
|
||||
if(a.empty()) return a;
|
||||
for(auto& dim_pair : dims)
|
||||
{
|
||||
a.erase(std::remove_if(a.begin(),
|
||||
a.end(),
|
||||
[&](rocprofiler_record_counter_t rec) {
|
||||
return std::find(dim_pair.second.begin(),
|
||||
dim_pair.second.end(),
|
||||
rec_to_dim_pos(rec.id, dim_pair.first)) ==
|
||||
dim_pair.second.end();
|
||||
}),
|
||||
a.end());
|
||||
}
|
||||
for(auto& dim_pair : dims)
|
||||
{
|
||||
size_t bit_length = DIM_BIT_LENGTH / ROCPROFILER_DIMENSION_LAST;
|
||||
int64_t mask = (MAX_64 >> (64 - bit_length)) << ((dim_pair.first - 1) * bit_length);
|
||||
for(auto& rec : a)
|
||||
{
|
||||
rec.id = rec.id | mask;
|
||||
rec.id = rec.id ^ mask;
|
||||
}
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, Metric> metrics = {
|
||||
{"VOORHEES", Metric("gfx9", "VOORHEES", "a", "a", "a", "", "", 0)},
|
||||
{"KRUEGER", Metric("gfx9", "KRUEGER", "a", "a", "a", "", "", 1)},
|
||||
{"MYERS", Metric("gfx9", "MYERS", "a", "a", "a", "", "", 2)},
|
||||
{"BATES",
|
||||
Metric("gfx9",
|
||||
"BATES",
|
||||
"C",
|
||||
"C",
|
||||
"C",
|
||||
"select(VOORHEES+KRUEGER,[DIMENSION_XCC=[0]])",
|
||||
"",
|
||||
3)},
|
||||
{"KRAMER",
|
||||
Metric("gfx9",
|
||||
"KRAMER",
|
||||
"C",
|
||||
"C",
|
||||
"C",
|
||||
"select(MYERS,[DIMENSION_XCC=[1],DIMENSION_SHADER_ARRAY=[0]])",
|
||||
"",
|
||||
4)},
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, std::vector<rocprofiler_record_counter_t>> base_counter_data = {
|
||||
{"VOORHEES", construct_test_data_dim(get_base_rec_id(0), {ROCPROFILER_DIMENSION_XCC}, 8)},
|
||||
{"KRUEGER", construct_test_data_dim(get_base_rec_id(1), {ROCPROFILER_DIMENSION_XCC}, 8)},
|
||||
{"MYERS",
|
||||
construct_test_data_dim(get_base_rec_id(2),
|
||||
{ROCPROFILER_DIMENSION_XCC, ROCPROFILER_DIMENSION_SHADER_ARRAY},
|
||||
8)}};
|
||||
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, EvaluateAST>> asts;
|
||||
for(const auto& [val, metric] : metrics)
|
||||
{
|
||||
RawAST* ast = nullptr;
|
||||
auto buf = yy_scan_string(metric.expression().empty() ? metric.name().c_str()
|
||||
: metric.expression().c_str());
|
||||
yyparse(&ast);
|
||||
ASSERT_TRUE(ast) << metric.expression() << " " << metric.name();
|
||||
asts.emplace("gfx9", std::unordered_map<std::string, EvaluateAST>{})
|
||||
.first->second.emplace(val,
|
||||
EvaluateAST({.handle = metric.id()}, metrics, *ast, "gfx9"));
|
||||
yy_delete_buffer(buf);
|
||||
delete ast;
|
||||
}
|
||||
|
||||
std::vector<std::tuple<std::string, std::vector<rocprofiler_record_counter_t>, int64_t>>
|
||||
derived_counters = {
|
||||
{"BATES",
|
||||
select_dim(plus_vec(base_counter_data["VOORHEES"], base_counter_data["KRUEGER"]),
|
||||
{{ROCPROFILER_DIMENSION_XCC, {0}}}),
|
||||
2},
|
||||
{"KRAMER",
|
||||
select_dim(
|
||||
base_counter_data["MYERS"],
|
||||
{{ROCPROFILER_DIMENSION_XCC, {1}}, {ROCPROFILER_DIMENSION_SHADER_ARRAY, {0}}}),
|
||||
1},
|
||||
};
|
||||
std::unordered_map<uint64_t, std::vector<rocprofiler_record_counter_t>> base_counter_decode;
|
||||
for(const auto& [name, base_counter_v] : base_counter_data)
|
||||
{
|
||||
base_counter_decode[metrics[name].id()] = base_counter_v;
|
||||
}
|
||||
|
||||
for(auto& [name, expected, eval_count] : derived_counters)
|
||||
{
|
||||
ROCP_INFO << name;
|
||||
auto eval_counters =
|
||||
rocprofiler::counters::get_required_hardware_counters(asts, "gfx9", metrics[name]);
|
||||
ASSERT_TRUE(eval_counters);
|
||||
ASSERT_EQ(eval_counters->size(), eval_count);
|
||||
std::vector<std::unique_ptr<std::vector<rocprofiler_record_counter_t>>> cache;
|
||||
asts.at("gfx9").at(name).expand_derived(asts.at("gfx9"));
|
||||
auto ret = asts.at("gfx9").at(name).evaluate(base_counter_decode, cache);
|
||||
EXPECT_EQ(ret->size(), expected.size());
|
||||
int pos = 0;
|
||||
asts.at("gfx9").at(name).set_out_id(*ret);
|
||||
for(const auto& v : *ret)
|
||||
{
|
||||
set_counter_in_rec(expected[pos].id, {.handle = metrics[name].id()});
|
||||
EXPECT_EQ(v.id, expected[pos].id);
|
||||
EXPECT_FLOAT_EQ(v.counter_value, expected[pos].counter_value);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
新增問題並參考
封鎖使用者