Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions projects/rdc/include/rdc_modules/rdc_rocp/RdcRocpBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ class RdcRocpBase {
*/
rdc_status_t rocp_lookup(rdc_gpu_field_t gpu_field, rdc_field_value_data* value,
rdc_field_type_t* type);

/**
* @brief Bulk lookup of multiple ROCProfiler counters for a single GPU
*
* @param[in] fields Vector of fields to lookup (all for the same GPU)
* @param[out] values Vector to be populated with returned values
* @param[out] types Vector to be populated with returned types
* @param[out] statuses Vector to be populated with status for each field
*
* @retval ::RDC_ST_OK The function has been executed successfully.
*/
rdc_status_t rocp_lookup_bulk(const std::vector<rdc_gpu_field_t>& fields,
std::vector<rdc_field_value_data>& values,
std::vector<rdc_field_type_t>& types,
std::vector<rdc_status_t>& statuses);

const char* get_field_id_from_name(rdc_field_t);
const std::vector<rdc_field_t> get_field_ids();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@ class CounterSampler {

rocprofiler_agent_id_t get_agent() const { return agent_; }

// Profile set for greedy packing
struct ProfileSet {
struct Profile {
rocprofiler_counter_config_id_t config;
std::vector<std::string> counter_names;
size_t expected_size;
};
std::vector<Profile> profiles;
};

// Sample multiple counters using greedy packing to minimize profiles
void sample_counters_with_packing(const std::vector<std::string>& counters,
std::map<std::string, double>& out_values,
uint64_t duration);

// Get the supported counters for an agent
static std::unordered_map<std::string, rocprofiler_counter_id_t> get_supported_counters(
rocprofiler_agent_id_t agent);
Expand All @@ -71,6 +86,7 @@ class CounterSampler {

std::map<std::vector<std::string>, rocprofiler_counter_config_id_t> cached_counter_;
std::map<uint64_t, uint64_t> counter_sizes_;
std::map<std::vector<std::string>, ProfileSet> cached_profile_sets_;

// Internal function used to set the profile for the agent when start_context is called
void set_profile(rocprofiler_context_id_t ctx, rocprofiler_device_counting_agent_cb_t cb) const;
Expand All @@ -82,6 +98,9 @@ class CounterSampler {
std::vector<rocprofiler_counter_record_dimension_info_t> get_counter_dimensions(
rocprofiler_counter_id_t counter);

// Create profiles using greedy packing algorithm
ProfileSet create_profiles_for_counters(const std::vector<std::string>& counters);

static std::vector<std::shared_ptr<CounterSampler>> samplers_;
};

Expand Down
195 changes: 195 additions & 0 deletions projects/rdc/rdc_libs/rdc_modules/rdc_rocp/RdcRocpBase.cc
Original file line number Diff line number Diff line change
Expand Up @@ -423,5 +423,200 @@ rdc_status_t RdcRocpBase::rocp_lookup(rdc_gpu_field_t gpu_field, rdc_field_value
return RDC_ST_OK;
}

rdc_status_t RdcRocpBase::rocp_lookup_bulk(const std::vector<rdc_gpu_field_t>& fields,
std::vector<rdc_field_value_data>& values,
std::vector<rdc_field_type_t>& types,
std::vector<rdc_status_t>& statuses) {
if (fields.empty()) {
return RDC_ST_OK;
}


Comment thread
adam360x marked this conversation as resolved.
Resolved
init_rocp_if_not();

// Resize output vectors
values.resize(fields.size());
types.resize(fields.size());
statuses.resize(fields.size());

// All fields should be for the same GPU
uint32_t agent_index = entity_to_prof_map[fields[0].gpu_index];

// Collect all unique metric names needed for sampling
std::vector<std::string> metrics_to_sample;
std::map<rdc_field_t, size_t> field_to_metric_index; // Maps field to position in metrics_to_sample

for (size_t i = 0; i < fields.size(); i++) {
const auto& field = fields[i].field_id;
types[i] = DOUBLE; // Default type
statuses[i] = RDC_ST_OK;

// Handle special case: RDC_FI_PROF_KFD_ID doesn't need sampling
if (field == RDC_FI_PROF_KFD_ID) {
types[i] = INTEGER;
values[i].l_int = agents[agent_index].gpu_id;
continue;
}

// Get metric name for this field
auto field_it = field_to_metric.find(field);
if (field_it == field_to_metric.end()) {
RDC_LOG(RDC_ERROR, "Error: Field " << field << " not found in field_to_metric map.");
statuses[i] = RDC_ST_BAD_PARAMETER;
continue;
}

const std::string metric_name = field_it->second;

// Check if we've already added this metric
if (field_to_metric_index.find(field) == field_to_metric_index.end()) {
field_to_metric_index[field] = metrics_to_sample.size();
metrics_to_sample.push_back(metric_name);
}

// Special case: RDC_FI_PROF_OCC_ELAPSED needs two metrics
if (field == RDC_FI_PROF_OCC_ELAPSED) {
if (field_to_metric_index.find(RDC_FI_PROF_OCC_PER_ACTIVE_CU) == field_to_metric_index.end()) {
auto occ_field_it = field_to_metric.find(RDC_FI_PROF_OCC_PER_ACTIVE_CU);
if (occ_field_it != field_to_metric.end()) {
field_to_metric_index[RDC_FI_PROF_OCC_PER_ACTIVE_CU] = metrics_to_sample.size();
metrics_to_sample.push_back(occ_field_it->second);
}
}
}
}

// Sample all counters at once using greedy packing
std::map<std::string, double> sampled_values;
auto counter_sampler = CounterSampler::get_samplers()[agent_index];
if (!counter_sampler) {
RDC_LOG(RDC_ERROR, "Error: Counter sampler not found for GPU index " << agent_index);
for (size_t i = 0; i < fields.size(); i++) {
statuses[i] = RDC_ST_BAD_PARAMETER;
}
return RDC_ST_BAD_PARAMETER;
}

const auto start_time = std::chrono::high_resolution_clock::now();
if (!metrics_to_sample.empty()) {
try {
counter_sampler->sample_counters_with_packing(metrics_to_sample, sampled_values,
collection_duration_us_k);
} catch (const std::exception& e) {
RDC_LOG(RDC_ERROR, "Error while sampling counter values: " << e.what());
for (size_t i = 0; i < fields.size(); i++) {
if (fields[i].field_id != RDC_FI_PROF_KFD_ID) {
statuses[i] = RDC_ST_BAD_PARAMETER;
}
}
return RDC_ST_BAD_PARAMETER;
}
}
const auto stop_time = std::chrono::high_resolution_clock::now();
const double elapsed = std::chrono::duration<double, std::milli>(stop_time - start_time).count();

// Process results for each field
for (size_t i = 0; i < fields.size(); i++) {
const auto& field = fields[i].field_id;

// Skip fields that already have values set (like RDC_FI_PROF_KFD_ID)
if (field == RDC_FI_PROF_KFD_ID) {
continue;
}

// Skip fields that had errors earlier
if (statuses[i] != RDC_ST_OK) {
continue;
}

// Get the sampled value for this field
auto field_it = field_to_metric.find(field);
if (field_it == field_to_metric.end()) {
continue;
}

const std::string& metric_name = field_it->second;
auto sampled_it = sampled_values.find(metric_name);
if (sampled_it == sampled_values.end()) {
RDC_LOG(RDC_ERROR, "Error: Metric " << metric_name << " not found in sampled values.");
statuses[i] = RDC_ST_BAD_PARAMETER;
continue;
}

double read_dbl = sampled_it->second;
const bool is_eval_field = (eval_fields.find(field) != eval_fields.end());

// Divide by elapsed time if needed
double divided_dbl = NAN;
if (is_eval_field) {
if (elapsed != 0.0) {
divided_dbl = read_dbl / (elapsed / 1000.0);
} else {
RDC_LOG(RDC_ERROR, "Error: Elapsed time is zero. Cannot divide by zero.");
statuses[i] = RDC_ST_BAD_PARAMETER;
continue;
}
}

// Handle special field processing
switch (field) {
Comment thread
bwelton marked this conversation as resolved.
Resolved
case RDC_FI_PROF_GPU_UTIL_PERCENT:
values[i].dbl = read_dbl / 100.0F;
break;
case RDC_FI_PROF_OCC_ELAPSED: {
const double active_cycles_val = read_dbl;
if (active_cycles_val != 0.0) {
auto occ_field_it = field_to_metric.find(RDC_FI_PROF_OCC_PER_ACTIVE_CU);
if (occ_field_it != field_to_metric.end()) {
auto occ_sampled_it = sampled_values.find(occ_field_it->second);
if (occ_sampled_it != sampled_values.end()) {
const double occupancy_val = occ_sampled_it->second;
values[i].dbl = occupancy_val / active_cycles_val;
} else {
statuses[i] = RDC_ST_BAD_PARAMETER;
}
} else {
statuses[i] = RDC_ST_BAD_PARAMETER;
}
} else {
statuses[i] = RDC_ST_BAD_PARAMETER;
}
} break;
case RDC_FI_PROF_EVAL_FLOPS_16_PERCENT: {
if (!is_eval_field) {
RDC_LOG(RDC_ERROR, "Field expected to be in the eval_fields list but it isn't!");
statuses[i] = RDC_ST_BAD_PARAMETER;
break;
}
const std::string target_version = agents[agent_index].name;
const bool isMI200 = (target_version.find("gfx90a") != std::string::npos);
if (isMI200) {
values[i].dbl = divided_dbl / (1024.0F / static_cast<double>(agents[agent_index].simd_per_cu));
} else { // Assume mi300
values[i].dbl = divided_dbl / (2048.0F / static_cast<double>(agents[agent_index].simd_per_cu));
}
} break;
case RDC_FI_PROF_EVAL_FLOPS_32_PERCENT:
case RDC_FI_PROF_EVAL_FLOPS_64_PERCENT:
if (!is_eval_field) {
RDC_LOG(RDC_ERROR, "Field expected to be in the eval_fields list but it isn't!");
statuses[i] = RDC_ST_BAD_PARAMETER;
break;
}
values[i].dbl = divided_dbl / (256.0F / static_cast<double>(agents[agent_index].simd_per_cu));
break;
default:
if (is_eval_field) {
values[i].dbl = divided_dbl;
} else {
values[i].dbl = read_dbl;
}
break;
}
}

return RDC_ST_OK;
}

} // namespace rdc
} // namespace amd
Loading