diff --git a/src/view/src/compute/rocprofvis_compute_kernel_details.cpp b/src/view/src/compute/rocprofvis_compute_kernel_details.cpp index 78ca4c31..99b57082 100644 --- a/src/view/src/compute/rocprofvis_compute_kernel_details.cpp +++ b/src/view/src/compute/rocprofvis_compute_kernel_details.cpp @@ -32,7 +32,7 @@ ComputeKernelDetailsView::ComputeKernelDetailsView( { SubscribeToEvents(); - m_roofline = std::make_unique(data_provider); + m_roofline = std::make_unique(data_provider, Roofline::SingleKernel); m_kernel_metric_table = std::make_unique(data_provider, compute_selection); m_widget_name = GenUniqueName("ComputeKernelDetailsView"); @@ -65,6 +65,10 @@ void ComputeKernelDetailsView::SubscribeToEvents() m_data_provider.ComputeModel().GetKernelSelectionTable().Clear(); m_kernel_metric_table->FetchData(evt->GetId()); } + if(m_roofline) + { + m_roofline->SetWorkload(evt->GetId()); + } m_sol_table.Clear(); } }; @@ -80,7 +84,6 @@ void ComputeKernelDetailsView::SubscribeToEvents() m_memory_chart.FetchMemChartMetrics(); if(m_roofline) { - m_roofline->SetWorkload(m_compute_selection->GetSelectedWorkload()); m_roofline->SetKernel(evt->GetId()); } } diff --git a/src/view/src/compute/rocprofvis_compute_roofline.cpp b/src/view/src/compute/rocprofvis_compute_roofline.cpp index 68416f8f..bd105120 100644 --- a/src/view/src/compute/rocprofvis_compute_roofline.cpp +++ b/src/view/src/compute/rocprofvis_compute_roofline.cpp @@ -6,6 +6,7 @@ #include "implot/implot.h" #include "rocprofvis_data_provider.h" #include "rocprofvis_settings_manager.h" +#include "rocprofvis_utils.h" #include "widgets/rocprofvis_gui_helpers.h" #include #include @@ -54,7 +55,7 @@ constexpr const char* DISPLAY_NAMES_PRESET[] = { "FP64", // PresetModel::Type::FP64 }; -Roofline::Roofline(DataProvider& data_provider) +Roofline::Roofline(DataProvider& data_provider, KernelMode kernel_mode) : m_data_provider(data_provider) , m_settings(SettingsManager::GetInstance()) , m_show_menus(true) @@ -63,6 +64,7 @@ Roofline::Roofline(DataProvider& data_provider) , m_hovered_item_distance(FLT_MAX) , m_workload_changed(false) , m_kernel_changed(false) +, m_kernel_mode(kernel_mode) , m_options_changed(false) , m_workload(nullptr) , m_requested_workload_id(0) @@ -107,6 +109,7 @@ Roofline::Update() { if(m_workload_changed) { + m_workload = nullptr; const std::unordered_map& workloads = m_data_provider.ComputeModel().GetWorkloads(); if(workloads.count(m_requested_workload_id) > 0) @@ -215,7 +218,8 @@ Roofline::Update() for(const std::pair& kernel : m_workload->kernels) { kernel_duration_scale = - std::max(kernel_duration_scale, kernel.second.duration_total); + std::max(kernel_duration_scale, + kernel.second.dispatch_metrics[KernelInfo::DurationTotal]); } ItemModel::Type model_type = ItemModel::Intensity; ItemModel::SubType model_subtype; @@ -237,7 +241,9 @@ Roofline::Update() DISPLAY_NAMES_KERNEL_INTENSITY[intensity.second.type]) + ": " + kernel.second.name, static_cast( - static_cast(kernel.second.duration_total) / + static_cast( + kernel.second + .dispatch_metrics[KernelInfo::DurationTotal]) / static_cast(kernel_duration_scale)) }); } } @@ -245,9 +251,10 @@ Roofline::Update() } m_workload_changed = false; } - if(m_kernel_changed && m_workload) + if(m_kernel_changed) { - if(m_workload->kernels.count(m_requested_kernel_id) > 0) + m_kernel = nullptr; + if(m_workload && m_workload->kernels.count(m_requested_kernel_id) > 0) { m_kernel = &m_workload->kernels.at(m_requested_kernel_id); } @@ -311,242 +318,236 @@ Roofline::Update() void Roofline::Render() { - if(m_workload) + ImGui::BeginChild( + "roofline", + ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().x * 0.5f), + ImGuiChildFlags_Border); + const ImVec2 region = ImGui::GetContentRegionAvail(); + const ImGuiStyle& style = ImGui::GetStyle(); + const ImPlotStyle& plot_style = ImPlot::GetStyle(); + ImGui::SetCursorPos(ImVec2(region.x * 0.5f - plot_style.PlotBorderSize - + ImGui::CalcTextSize("Roofline Analysis").x * 0.5f, + plot_style.PlotPadding.y)); + ImGui::TextUnformatted("Roofline Analysis"); + if(!m_workload || + (m_workload->roofline.ceiling_bandwidth.empty() || + m_workload->roofline.ceiling_compute.empty()) || + m_kernel_mode == SingleKernel && + (!m_kernel || m_kernel->roofline.intensities.empty())) { - ImGui::BeginChild("roofline", ImVec2(ImGui::GetContentRegionAvail().x, - ImGui::GetContentRegionAvail().x * 0.5f)); - const ImVec2 region = ImGui::GetContentRegionAvail(); - const ImGuiStyle& style = ImGui::GetStyle(); - const ImPlotStyle& plot_style = ImPlot::GetStyle(); - ImGui::SetCursorPos(ImVec2(region.x * 0.5f - plot_style.PlotBorderSize - - ImGui::CalcTextSize("Roofline Analysis").x * 0.5f, - plot_style.PlotPadding.y)); - ImGui::TextUnformatted("Roofline Analysis"); - if(m_workload->roofline.ceiling_bandwidth.empty() || - m_workload->roofline.ceiling_compute.empty() || - (m_kernel && m_kernel->roofline.intensities.empty())) - { - ImGui::GetWindowDrawList()->AddRect( - ImGui::GetCursorScreenPos() + - ImVec2(plot_style.PlotBorderSize + plot_style.PlotPadding.x, - plot_style.PlotBorderSize + plot_style.PlotPadding.y), - ImGui::GetCursorScreenPos() + region - - ImVec2(plot_style.PlotBorderSize + plot_style.PlotPadding.x, - plot_style.PlotBorderSize + plot_style.PlotPadding.y), - ImGui::GetColorU32(style.Colors[ImGuiCol_TableBorderStrong])); - ImGui::SetCursorPos((region - ImGui::CalcTextSize("No data available.")) * - 0.5f); - ImGui::TextDisabled("No data available."); - } - else + ImGui::GetWindowDrawList()->AddRect( + ImGui::GetCursorScreenPos() + + ImVec2(plot_style.PlotBorderSize + plot_style.PlotPadding.x, + plot_style.PlotBorderSize + plot_style.PlotPadding.y), + ImGui::GetCursorScreenPos() + region - + ImVec2(plot_style.PlotBorderSize + plot_style.PlotPadding.x, + plot_style.PlotBorderSize + plot_style.PlotPadding.y + + ImGui::GetFrameHeightWithSpacing()), + ImGui::GetColorU32(style.Colors[ImGuiCol_TableBorderStrong])); + ImGui::SetCursorPos((region - ImGui::CalcTextSize("No data available.")) * 0.5f); + ImGui::TextDisabled("No data available."); + } + else + { + ImPlot::PushStyleColor(ImPlotCol_PlotBg, + ImGui::GetStyleColorVec4(ImGuiCol_FrameBg)); + ImPlot::PushStyleColor(ImPlotCol_FrameBg, + m_settings.GetColor(Colors::kTransparent)); + ImPlot::PushColormap("flame"); + if(ImPlot::BeginPlot("plot", ImVec2(-1, -1), + ImPlotFlags_NoTitle | ImPlotFlags_NoFrame | + ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | + ImPlotFlags_Crosshairs)) { - ImPlot::PushStyleColor(ImPlotCol_PlotBg, - ImGui::GetStyleColorVec4(ImGuiCol_FrameBg)); - ImPlot::PushStyleColor(ImPlotCol_FrameBg, - m_settings.GetColor(Colors::kTransparent)); - ImPlot::PushColormap("flame"); - if(ImPlot::BeginPlot("plot", ImVec2(-1, -1), - ImPlotFlags_NoTitle | ImPlotFlags_NoFrame | - ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | - ImPlotFlags_Crosshairs)) + ImPlot::SetupAxis(ImAxis_X1, "Arithmetic Intensity (FLOP/Byte)", + ImPlotAxisFlags_NoSideSwitch | ImPlotAxisFlags_NoHighlight); + ImPlot::SetupAxis(ImAxis_Y1, "Performance (GFLOP/s)", + ImPlotAxisFlags_NoSideSwitch | ImPlotAxisFlags_NoHighlight); + ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Log10); + ImPlot::SetupAxisScale(ImAxis_Y1, ImPlotScale_Log10); + ImPlot::SetupAxisLimits(ImAxis_X1, m_workload->roofline.min.x, + m_workload->roofline.max.x); + ImPlot::SetupAxisLimits(ImAxis_Y1, m_workload->roofline.min.y / 10, + m_workload->roofline.max.y * 10); + ImPlot::SetupAxisLimitsConstraints(ImAxis_X1, m_workload->roofline.min.x, + m_workload->roofline.max.x); + ImPlot::SetupAxisLimitsConstraints(ImAxis_Y1, m_workload->roofline.min.y / 10, + m_workload->roofline.max.y * 10); + PlotHoverIdx(); + for(size_t i = 0; i < m_items.size(); i++) { - ImPlot::SetupAxis(ImAxis_X1, "Arithmetic Intensity (FLOP/Byte)", - ImPlotAxisFlags_NoSideSwitch | - ImPlotAxisFlags_NoHighlight); - ImPlot::SetupAxis(ImAxis_Y1, "Performance (GFLOP/s)", - ImPlotAxisFlags_NoSideSwitch | - ImPlotAxisFlags_NoHighlight); - ImPlot::SetupAxisScale(ImAxis_X1, ImPlotScale_Log10); - ImPlot::SetupAxisScale(ImAxis_Y1, ImPlotScale_Log10); - ImPlot::SetupAxisLimits(ImAxis_X1, m_workload->roofline.min.x, - m_workload->roofline.max.x); - ImPlot::SetupAxisLimits(ImAxis_Y1, m_workload->roofline.min.y / 10, - m_workload->roofline.max.y * 10); - ImPlot::SetupAxisLimitsConstraints(ImAxis_X1, m_workload->roofline.min.x, - m_workload->roofline.max.x); - ImPlot::SetupAxisLimitsConstraints(ImAxis_Y1, - m_workload->roofline.min.y / 10, - m_workload->roofline.max.y * 10); - PlotHoverIdx(); - for(size_t i = 0; i < m_items.size(); i++) + bool display = false; + switch(m_items[i].type) { - bool display = false; + case ItemModel::Type::CeilingCompute: + case ItemModel::Type::CeilingBandwidth: + { + display = m_items[i].info.ceiling; + break; + } + case ItemModel::Type::Intensity: + { + display = + m_items[i].info.intensity && + (m_kernel ? m_items[i].parent_info.kernel == m_kernel : true); + } + } + display &= m_items[i].visible; + if(display) + { + ImGui::PushID(i); + bool hovered = m_hovered_item_idx && m_hovered_item_idx.value() == i; switch(m_items[i].type) { case ItemModel::Type::CeilingCompute: case ItemModel::Type::CeilingBandwidth: { - display = m_items[i].info.ceiling; + ImPlot::SetNextLineStyle(ImPlot::GetColormapColor(i), + hovered + ? plot_style.LineWeight * 3.0f + : plot_style.LineWeight); + ImPlot::PlotLineG( + "", + [](int idx, void* user_data) -> ImPlotPoint { + const WorkloadInfo::Roofline::Line* line = + static_cast( + user_data); + ImPlotPoint point(-1.0, -1.0); + if(line) + { + if(idx == 0) + { + point.x = line->p1.x; + point.y = line->p1.y; + } + else + { + point.x = line->p2.x; + point.y = line->p2.y; + } + } + return point; + }, + (void*) &m_items[i].info.ceiling->position, 2); break; } case ItemModel::Type::Intensity: { - display = - m_items[i].info.intensity && - (m_kernel ? m_items[i].parent_info.kernel == m_kernel - : true); + ImPlot::SetNextMarkerStyle( + IMPLOT_AUTO, + plot_style.MarkerSize + + (m_scale_intensity && m_kernel_mode == AllKernels + ? m_items[i].weight + : 0.0f) * + 2.0f * plot_style.MarkerSize + + (hovered ? plot_style.MarkerSize : 0.0f), + ImPlot::GetColormapColor(i), IMPLOT_AUTO, + ImPlot::GetColormapColor(i)); + ImPlot::PlotScatter( + "", &m_items[i].info.intensity->position.x, + &m_items[i].info.intensity->position.y, 1); + break; } } - display &= m_items[i].visible; - if(display) + if(hovered) { - ImGui::PushID(i); - bool hovered = - m_hovered_item_idx && m_hovered_item_idx.value() == i; - switch(m_items[i].type) + ImGui::PushStyleVar( + ImGuiStyleVar_WindowPadding, + m_settings.GetDefaultIMGUIStyle().WindowPadding); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, + m_settings.GetDefaultStyle().FrameRounding); + if(ImGui::BeginItemTooltip()) { - case ItemModel::Type::CeilingCompute: - case ItemModel::Type::CeilingBandwidth: + switch(m_items[i].type) { - ImPlot::SetNextLineStyle(ImPlot::GetColormapColor(i), - hovered ? plot_style.LineWeight * - 3.0f - : plot_style.LineWeight); - ImPlot::PlotLineG( - "", - [](int idx, void* user_data) -> ImPlotPoint { - const WorkloadInfo::Roofline::Line* line = - static_cast< - const WorkloadInfo::Roofline::Line*>( - user_data); - ImPlotPoint point(-1.0, -1.0); - if(line) - { - if(idx == 0) - { - point.x = line->p1.x; - point.y = line->p1.y; - } - else - { - point.x = line->p2.x; - point.y = line->p2.y; - } - } - return point; - }, - (void*) &m_items[i].info.ceiling->position, 2); - break; - } - case ItemModel::Type::Intensity: - { - ImPlot::SetNextMarkerStyle( - IMPLOT_AUTO, - plot_style.MarkerSize + - (m_scale_intensity ? m_items[i].weight : 0.0f) * - 2.0f * plot_style.MarkerSize + - (hovered ? plot_style.MarkerSize : 0.0f), - ImPlot::GetColormapColor(i), IMPLOT_AUTO, - ImPlot::GetColormapColor(i)); - ImPlot::PlotScatter( - "", &m_items[i].info.intensity->position.x, - &m_items[i].info.intensity->position.y, 1); - break; - } - } - if(hovered) - { - ImGui::PushStyleVar( - ImGuiStyleVar_WindowPadding, - m_settings.GetDefaultIMGUIStyle().WindowPadding); - ImGui::PushStyleVar( - ImGuiStyleVar_WindowRounding, - m_settings.GetDefaultStyle().FrameRounding); - if(ImGui::BeginItemTooltip()) - { - switch(m_items[i].type) + case ItemModel::Type::CeilingCompute: { - case ItemModel::Type::CeilingCompute: - { - ImGui::GetWindowDrawList()->AddRectFilled( - ImGui::GetCursorScreenPos(), - ImGui::GetCursorScreenPos() + - ImGui::CalcTextSize( - m_items[i].label.c_str()), - ImGui::GetColorU32( - ImPlot::GetColormapColor(i))); - ImGui::TextUnformatted(m_items[i].label.c_str()); - ImGui::Text( - "%.0f GFLOP/s", - std::round( - m_items[i].info.ceiling->throughput)); - break; - } - case ItemModel::Type::CeilingBandwidth: - { - ImGui::GetWindowDrawList()->AddRectFilled( - ImGui::GetCursorScreenPos(), - ImGui::GetCursorScreenPos() + - ImGui::CalcTextSize( - m_items[i].label.c_str()), - ImGui::GetColorU32( - ImPlot::GetColormapColor(i))); - ImGui::TextUnformatted(m_items[i].label.c_str()); - ImGui::Text( - "%.0f GB/s", - std::round( - m_items[i].info.ceiling->throughput)); - break; - } - case ItemModel::Type::Intensity: - { - ImGui::BeginGroup(); - ImGui::GetWindowDrawList()->AddRectFilled( - ImGui::GetCursorScreenPos(), - ImGui::GetCursorScreenPos() + - ImGui::CalcTextSize( - DISPLAY_NAMES_KERNEL_INTENSITY - [m_items[i].subtype.intensity]), - ImGui::GetColorU32( - ImPlot::GetColormapColor(i))); - ImGui::TextUnformatted( - DISPLAY_NAMES_KERNEL_INTENSITY - [m_items[i].subtype.intensity]); - ImVec2 reserved_pos = ImGui::GetCursorPos(); - ImGui::NewLine(); - ImGui::Text( - "Inovcation(s): %u", - m_items[i] - .parent_info.kernel->invocation_count); - ImGui::Text( - "Duration: %llu", - m_items[i] - .parent_info.kernel->duration_total); - ImGui::Text( - "Arithmetic Intensity: %f FLOP/Byte", - m_items[i].info.intensity->position.x); - ImGui::Text( - "Performance: %f GFLOP/s", - m_items[i].info.intensity->position.y); - ImGui::EndGroup(); - ImGui::SetCursorPos(reserved_pos); - ElidedText( - m_items[i].parent_info.kernel->name.c_str(), - ImGui::GetItemRectSize().x); - break; - } + ImGui::GetWindowDrawList()->AddRectFilled( + ImGui::GetCursorScreenPos(), + ImGui::GetCursorScreenPos() + + ImGui::CalcTextSize(m_items[i].label.c_str()), + ImGui::GetColorU32(ImPlot::GetColormapColor(i))); + ImGui::TextUnformatted(m_items[i].label.c_str()); + ImGui::Text( + "%.0f GFLOP/s", + std::round(m_items[i].info.ceiling->throughput)); + break; + } + case ItemModel::Type::CeilingBandwidth: + { + ImGui::GetWindowDrawList()->AddRectFilled( + ImGui::GetCursorScreenPos(), + ImGui::GetCursorScreenPos() + + ImGui::CalcTextSize(m_items[i].label.c_str()), + ImGui::GetColorU32(ImPlot::GetColormapColor(i))); + ImGui::TextUnformatted(m_items[i].label.c_str()); + ImGui::Text( + "%.0f GB/s", + std::round(m_items[i].info.ceiling->throughput)); + break; + } + case ItemModel::Type::Intensity: + { + ImGui::BeginGroup(); + ImGui::GetWindowDrawList()->AddRectFilled( + ImGui::GetCursorScreenPos(), + ImGui::GetCursorScreenPos() + + ImGui::CalcTextSize( + DISPLAY_NAMES_KERNEL_INTENSITY + [m_items[i].subtype.intensity]), + ImGui::GetColorU32(ImPlot::GetColormapColor(i))); + ImGui::TextUnformatted( + DISPLAY_NAMES_KERNEL_INTENSITY + [m_items[i].subtype.intensity]); + ImVec2 reserved_pos = ImGui::GetCursorPos(); + ImGui::NewLine(); + ImGui::Text( + "Invocation(s): %u", + m_items[i].parent_info.kernel->dispatch_metrics + [KernelInfo::InvocationCount]); + ImGui::Text( + "Duration: %s", + nanosecond_to_formatted_str( + static_cast( + m_items[i] + .parent_info.kernel->dispatch_metrics + [KernelInfo::DurationTotal]), + m_settings.GetUserSettings() + .unit_settings.time_format, + true) + .c_str()); + ImGui::Text("Arithmetic Intensity: %f FLOP/Byte", + m_items[i].info.intensity->position.x); + ImGui::Text("Performance: %f GFLOP/s", + m_items[i].info.intensity->position.y); + ImGui::EndGroup(); + ImGui::SetCursorPos(reserved_pos); + ElidedText( + m_items[i].parent_info.kernel->name.c_str(), + ImGui::GetItemRectSize().x); + break; } - ImGui::EndTooltip(); } - ImGui::PopStyleVar(2); + ImGui::EndTooltip(); } - ImGui::PopID(); + ImGui::PopStyleVar(2); } + ImGui::PopID(); } - ImPlot::EndPlot(); - } - bool menus_item_hovered = false; - RenderMenus(region, style, plot_style, menus_item_hovered); - if(!menus_item_hovered) - { - m_hovered_item_idx = std::nullopt; - m_hovered_item_distance = FLT_MAX; } - ImPlot::PopColormap(); - ImPlot::PopStyleColor(2); + ImPlot::EndPlot(); } - ImGui::EndChild(); + bool menus_item_hovered = false; + RenderMenus(region, style, plot_style, menus_item_hovered); + if(!menus_item_hovered) + { + m_hovered_item_idx = std::nullopt; + m_hovered_item_distance = FLT_MAX; + } + ImPlot::PopColormap(); + ImPlot::PopStyleColor(2); } + ImGui::EndChild(); } void @@ -747,13 +748,13 @@ Roofline::RenderMenus(const ImVec2 region, const ImGuiStyle& style, } ImGui::EndChild(); ImGui::EndChild(); - if(m_menus_mode == Options && !m_kernel) + if(m_menus_mode == Options && m_kernel_mode == AllKernels) { ImGui::SeparatorText("Options"); ImGui::Checkbox("##scale_intensity", &m_scale_intensity); ImGui::SameLine(); ElidedText("Scale kernel marker size to duration", - ImGui::GetContentRegionAvail().x, region.x * 0.5f, true); + ImGui::GetContentRegionAvail().x, region.x * 0.5f, false, true); } ImGui::EndChild(); ImGui::PopStyleColor(); diff --git a/src/view/src/compute/rocprofvis_compute_roofline.h b/src/view/src/compute/rocprofvis_compute_roofline.h index 9a84b365..c95de1c1 100644 --- a/src/view/src/compute/rocprofvis_compute_roofline.h +++ b/src/view/src/compute/rocprofvis_compute_roofline.h @@ -21,7 +21,13 @@ class SettingsManager; class Roofline : public RocWidget { public: - Roofline(DataProvider& data_provider); + enum KernelMode + { + SingleKernel, + AllKernels, + }; + + Roofline(DataProvider& data_provider, KernelMode kernel_mode); void Update() override; void Render() override; @@ -100,6 +106,7 @@ class Roofline : public RocWidget bool m_workload_changed; const WorkloadInfo* m_workload; uint32_t m_requested_workload_id; + KernelMode m_kernel_mode; bool m_kernel_changed; const KernelInfo* m_kernel; uint32_t m_requested_kernel_id; diff --git a/src/view/src/compute/rocprofvis_compute_summary.cpp b/src/view/src/compute/rocprofvis_compute_summary.cpp index 9f263adc..57d2cca4 100644 --- a/src/view/src/compute/rocprofvis_compute_summary.cpp +++ b/src/view/src/compute/rocprofvis_compute_summary.cpp @@ -2,21 +2,40 @@ // SPDX-License-Identifier: MIT #include "rocprofvis_compute_summary.h" +#include "icons/rocprovfis_icon_defines.h" +#include "implot/implot.h" #include "rocprofvis_compute_roofline.h" #include "rocprofvis_compute_selection.h" #include "rocprofvis_data_provider.h" #include "rocprofvis_event_manager.h" +#include "rocprofvis_settings_manager.h" +#include "rocprofvis_utils.h" +#include "widgets/rocprofvis_gui_helpers.h" +#include +#include +#include namespace RocProfVis { namespace View { +constexpr float IMPLOT_LEGEND_ICON_SHRINK = 2.0f; // Implot_internal.h +constexpr double PIE_CHART_RADIUS = 1.0; +constexpr double BAR_CHART_THICKNESS = 0.67; +constexpr size_t NUM_TOP_KERNELS = 10; +constexpr ImVec2 CHART_FIT_PADDING = ImVec2(0.1f, 0.1f); +constexpr float FILTER_COMBO_RELATIVE_MIN_WIDTH = 17.0f; +constexpr const char* DISPLAY_STRING_METRICS[]{ "Invocation(s)", "Total Duration", + "Min Duration", "Max Duration", + "Mean Duration", "Median Duration" }; + ComputeSummaryView::ComputeSummaryView( DataProvider& data_provider, std::shared_ptr compute_selection) : RocWidget() , m_data_provider(data_provider) , m_roofline(nullptr) +, m_top_kernels(nullptr) , m_compute_selection(compute_selection) , m_client_id(IdGenerator::GetInstance().GenerateId()) { @@ -29,6 +48,10 @@ ComputeSummaryView::ComputeSummaryView( { return; } + if(m_top_kernels) + { + m_top_kernels->SetWorkload(selection_changed_event->GetId()); + } if(m_roofline) { m_roofline->SetWorkload(selection_changed_event->GetId()); @@ -54,12 +77,14 @@ ComputeSummaryView::ComputeSummaryView( m_metrics_fetched_token = EventManager::GetInstance()->Subscribe( static_cast(RocEvents::kComputeMetricsFetched), metrics_fetched_handler); - m_roofline = std::make_unique(m_data_provider); + m_top_kernels = std::make_unique(m_data_provider); + m_roofline = std::make_unique(m_data_provider, Roofline::AllKernels); m_widget_name = GenUniqueName("ComputeSummaryView"); } -ComputeSummaryView::~ComputeSummaryView() { +ComputeSummaryView::~ComputeSummaryView() +{ EventManager::GetInstance()->Unsubscribe( static_cast(RocEvents::kComputeWorkloadSelectionChanged), m_workload_selection_changed_token); @@ -70,6 +95,10 @@ ComputeSummaryView::~ComputeSummaryView() { void ComputeSummaryView::Update() { + if(m_top_kernels) + { + m_top_kernels->Update(); + } if(m_roofline) { m_roofline->Update(); @@ -80,6 +109,10 @@ void ComputeSummaryView::Render() { ImGui::BeginChild("summary"); + if(m_top_kernels) + { + m_top_kernels->Render(); + } if(m_roofline) { m_roofline->Render(); @@ -87,5 +120,744 @@ ComputeSummaryView::Render() ImGui::EndChild(); } +ComputeTopKernels::ComputeTopKernels(DataProvider& dp) +: m_data_provider(dp) +, m_settings(SettingsManager::GetInstance()) +, m_workload_changed(false) +, m_requested_workload_id(ComputeSelection::INVALID_SELECTION_ID) +, m_workload(nullptr) +, m_display_mode(Pie) +, m_show_legend(true) +, m_hovered_idx(std::nullopt) +, m_padded_idx(std::nullopt) +, m_padded_info(nullptr) +, m_time_format_changed_token(EventManager::InvalidSubscriptionToken) +{ + m_kernel_pie.metric_sets = { + KernelPieModel::MetricSet{ KernelInfo::InvocationCount, true, {}, {} }, + KernelPieModel::MetricSet{ KernelInfo::DurationTotal, true, {}, {} }, + KernelPieModel::MetricSet{ KernelInfo::DurationMin, false, {}, {} }, + KernelPieModel::MetricSet{ KernelInfo::DurationMax, false, {}, {} }, + KernelPieModel::MetricSet{ KernelInfo::DurationMean, false, {}, {} }, + KernelPieModel::MetricSet{ KernelInfo::DurationMedian, false, {}, {} }, + }; + m_kernel_pie.selected_metric = KernelInfo::DurationTotal; + m_kernel_bar.metric_sets = { + KernelBarModel::MetricSet{ KernelInfo::InvocationCount, 0, {}, "" }, + KernelBarModel::MetricSet{ KernelInfo::DurationTotal, 0, {}, "" }, + KernelBarModel::MetricSet{ KernelInfo::DurationMin, 0, {}, "" }, + KernelBarModel::MetricSet{ KernelInfo::DurationMax, 0, {}, "" }, + KernelBarModel::MetricSet{ KernelInfo::DurationMean, 0, {}, "" }, + KernelBarModel::MetricSet{ KernelInfo::DurationMedian, 0, {}, "" }, + }; + m_kernel_bar.selected_metric = KernelInfo::DurationTotal; + m_kernel_bar.width = 0.0f; + m_kernel_bar.tick_interval_dirty = false; + m_kernel_bar.axis_label_dirty = false; + auto time_format_changed_handler = [this](std::shared_ptr e) { + m_kernel_bar.tick_interval_dirty = true; + m_kernel_bar.axis_label_dirty = true; + }; + m_time_format_changed_token = EventManager::GetInstance()->Subscribe( + static_cast(RocEvents::kTimeFormatChanged), time_format_changed_handler); +} + +ComputeTopKernels::~ComputeTopKernels() +{ + EventManager::GetInstance()->Unsubscribe( + static_cast(RocEvents::kTimeFormatChanged), m_time_format_changed_token); +} + +void +ComputeTopKernels::GetTopKernels(std::vector& kernels) +{ + if(kernels.size() <= NUM_TOP_KERNELS) + { + std::sort(kernels.begin(), kernels.end(), TopKernelsComparator{}); + } + else + { + std::partial_sort(kernels.begin(), kernels.begin() + NUM_TOP_KERNELS, + kernels.end(), TopKernelsComparator{}); + m_padded_info = std::make_unique(); + m_padded_info->name = "Others"; + m_padded_info->dispatch_metrics = {}; + for(size_t i = NUM_TOP_KERNELS; i < kernels.size(); ++i) + { + m_padded_info->dispatch_metrics[KernelInfo::InvocationCount] += + kernels[i]->dispatch_metrics[KernelInfo::InvocationCount]; + m_padded_info->dispatch_metrics[KernelInfo::DurationTotal] += + kernels[i]->dispatch_metrics[KernelInfo::DurationTotal]; + } + kernels.resize(NUM_TOP_KERNELS); + kernels.push_back(m_padded_info.get()); + m_padded_idx = kernels.size() - 1; + } + m_kernels = kernels; +} + +void +ComputeTopKernels::Update() +{ + if(m_data_provider.GetState() == ProviderState::kReady) + { + if(m_workload_changed) + { + m_workload = nullptr; + m_kernels.clear(); + m_padded_info = nullptr; + m_padded_idx = std::nullopt; + const std::unordered_map& workloads = + m_data_provider.ComputeModel().GetWorkloads(); + if(workloads.count(m_requested_workload_id) > 0) + { + m_workload = &workloads.at(m_requested_workload_id); + } + if(m_workload) + { + std::vector all_kernels = + m_data_provider.ComputeModel().GetKernelInfoList(m_workload->id); + GetTopKernels(all_kernels); + std::array dispatch_metrics_total{}; + std::array dispatch_metrics_max{}; + for(const KernelInfo* kernel : m_kernels) + { + for(size_t i = 0; i < kernel->dispatch_metrics.size(); i++) + { + KernelInfo::DispatchMetric metric = + static_cast(i); + dispatch_metrics_total[metric] += + kernel->dispatch_metrics[metric]; + dispatch_metrics_max[metric] = + std::max(dispatch_metrics_max[metric], + kernel->dispatch_metrics[metric]); + } + } + std::array accumulated_pie_angles{}; + m_kernel_pie.labels.resize(m_kernels.size()); + for(size_t i = 0; i < m_kernels.size(); i++) + { + m_kernel_pie.labels[i] = m_kernels[i]->name.c_str(); + for(size_t j = 0; j < m_kernels[i]->dispatch_metrics.size(); j++) + { + KernelInfo::DispatchMetric metric = + static_cast(j); + if(m_kernel_pie.metric_sets[metric].available) + { + m_kernel_pie.metric_sets[metric].pct_values.resize( + m_kernels.size()); + m_kernel_pie.metric_sets[metric].slices.resize( + m_kernels.size()); + float value = static_cast( + m_kernels[i]->dispatch_metrics[metric]) / + dispatch_metrics_total[metric]; + m_kernel_pie.metric_sets[metric].pct_values[i] = value; + value *= 360.0f; + m_kernel_pie.metric_sets[metric].slices[i] = + KernelPieModel::Slice{ accumulated_pie_angles[metric], + value }; + accumulated_pie_angles[metric] += value; + } + } + } + for(KernelBarModel::MetricSet& metric_set : m_kernel_bar.metric_sets) + { + metric_set.max_value = dispatch_metrics_max[metric_set.metric]; + } + } + m_workload_changed = false; + m_kernel_bar.tick_interval_dirty = true; + m_kernel_bar.axis_label_dirty = true; + } + if(m_kernel_bar.tick_interval_dirty) + { + for(KernelBarModel::MetricSet& metric_set : m_kernel_bar.metric_sets) + { + float label_width; + switch(metric_set.metric) + { + case KernelInfo::InvocationCount: + { + label_width = m_kernel_bar.width / 10.0f; + break; + } + default: + { + label_width = + ImGui::CalcTextSize( + std::string(nanosecond_to_formatted_str( + static_cast(metric_set.max_value), + m_settings.GetUserSettings() + .unit_settings.time_format, + true) + + "gap") + .c_str()) + .x; + break; + } + } + FittedGraphAxisInterval fitted_interval = + fit_graph_axis_interval(static_cast(metric_set.max_value), + m_kernel_bar.width, label_width, false, 0); + metric_set.axis_tick_label_positions.resize( + fitted_interval.interval_count); + for(int i = 0; i < fitted_interval.interval_count; i++) + { + metric_set.axis_tick_label_positions[i] = + static_cast(i) * fitted_interval.interval_ns; + } + } + m_kernel_bar.tick_interval_dirty = false; + } + if(m_kernel_bar.axis_label_dirty) + { + for(KernelBarModel::MetricSet& metric_set : m_kernel_bar.metric_sets) + { + switch(m_kernel_bar.selected_metric) + { + case KernelInfo::InvocationCount: + { + metric_set.axis_title = + DISPLAY_STRING_METRICS[m_kernel_bar.selected_metric]; + break; + } + default: + { + metric_set.axis_title = + std::string( + DISPLAY_STRING_METRICS[m_kernel_bar.selected_metric]) + + " (" + + timeformat_sufix( + m_settings.GetUserSettings().unit_settings.time_format) + + ")"; + break; + } + } + } + m_kernel_bar.axis_label_dirty = false; + } + } +} + +void +ComputeTopKernels::Render() +{ + ImGui::BeginChild( + "top_kernels", + ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().x * 0.5f), + ImGuiChildFlags_Border); + const ImVec2 region = ImGui::GetContentRegionAvail(); + const ImGuiStyle& style = ImGui::GetStyle(); + const ImPlotStyle& plot_style = ImPlot::GetStyle(); + ImGui::SetCursorPos( + ImVec2(region.x * 0.5f - plot_style.PlotBorderSize - + ImGui::CalcTextSize("Top Kernels by Execution Time").x * 0.5f, + plot_style.PlotPadding.y)); + ImGui::TextUnformatted("Top Kernels by Execution Time"); + if(!m_workload || m_kernels.empty()) + { + ImGui::GetWindowDrawList()->AddRect( + ImGui::GetCursorScreenPos() + + ImVec2(plot_style.PlotBorderSize + plot_style.PlotPadding.x, + plot_style.PlotBorderSize + plot_style.PlotPadding.y), + ImGui::GetWindowPos() + region - + ImVec2(plot_style.PlotBorderSize + plot_style.PlotPadding.x, + plot_style.PlotPadding.y + ImGui::GetFrameHeightWithSpacing()), + ImGui::GetColorU32(style.Colors[ImGuiCol_TableBorderStrong])); + ImGui::SetCursorPos((region - ImGui::CalcTextSize("No data available.")) * 0.5f); + ImGui::TextDisabled("No data available."); + } + else + { + // Chart... + std::optional hovered_idx = std::nullopt; + TimeFormat time_format = m_settings.GetUserSettings().unit_settings.time_format; + ImPlot::PushColormap("flame"); + RenderTable(plot_style, time_format, hovered_idx); + m_hovered_idx = hovered_idx; + switch(m_display_mode) + { + case Pie: + { + RenderPieChart(plot_style, time_format); + break; + } + case Bar: + { + RenderBarChart(plot_style, time_format); + break; + } + } + ImPlot::PopColormap(); + // Chart switcher... + ImGui::SetCursorPosX(plot_style.PlotPadding.x); + ImGui::BeginGroup(); + if(IconButton(ICON_CHART_PIE, + m_settings.GetFontManager().GetIconFont(FontType::kDefault), + ImVec2(0, 0), nullptr, ImVec2(0, 0), false, style.FramePadding, + m_settings.GetColor(m_display_mode == Pie ? Colors::kButton + : Colors::kTransparent), + m_settings.GetColor(Colors::kButtonHovered), + m_settings.GetColor(Colors::kButtonActive))) + { + m_display_mode = Pie; + } + ImGui::SameLine(); + if(IconButton(ICON_CHART_BAR, + m_settings.GetFontManager().GetIconFont(FontType::kDefault), + ImVec2(0, 0), nullptr, ImVec2(0, 0), false, style.FramePadding, + m_settings.GetColor(m_display_mode == Bar ? Colors::kButton + : Colors::kTransparent), + m_settings.GetColor(Colors::kButtonHovered), + m_settings.GetColor(Colors::kButtonActive))) + { + m_display_mode = Bar; + } + ImGui::EndGroup(); + // Metric combo... + KernelInfo::DispatchMetric* selected_metric = nullptr; + switch(m_display_mode) + { + case Pie: + { + selected_metric = &m_kernel_pie.selected_metric; + break; + } + case Bar: + { + selected_metric = &m_kernel_bar.selected_metric; + break; + } + } + ImGui::SameLine(); + ImGui::SetCursorPosX(ImGui::GetContentRegionMax().x - + ImGui::CalcTextSize("Plot:").x - region.x * 0.25f - + 2.0f * plot_style.PlotPadding.x); + ImGui::AlignTextToFramePadding(); + ImGui::PushStyleVarX(ImGuiStyleVar_ItemSpacing, plot_style.PlotPadding.x); + ImGui::TextUnformatted("Plot:"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(region.x * 0.25f); + if(selected_metric && + ImGui::BeginCombo("##plot_combo", + DISPLAY_STRING_METRICS[static_cast(*selected_metric)])) + { + for(int i = 0; i < IM_ARRAYSIZE(DISPLAY_STRING_METRICS); i++) + { + if((m_display_mode == Pie ? m_kernel_pie.metric_sets[i].available + : true) && + ImGui::Selectable(DISPLAY_STRING_METRICS[i])) + { + *selected_metric = static_cast(i); + } + } + ImGui::EndCombo(); + } + ImGui::PopStyleVar(); + } + ImGui::EndChild(); +} + +void +ComputeTopKernels::SetWorkload(uint32_t id) +{ + m_requested_workload_id = id; + m_workload_changed = true; +} + +void +ComputeTopKernels::RenderPieChart(const ImPlotStyle& plot_style, TimeFormat time_format) +{ + ImPlot::PushStyleVar(ImPlotStyleVar_FitPadding, CHART_FIT_PADDING); + ImPlot::PushStyleColor(ImPlotCol_PlotBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg)); + ImPlot::PushStyleColor(ImPlotCol_FrameBg, m_settings.GetColor(Colors::kTransparent)); + if(ImPlot::BeginPlot( + "##Pie", + ImVec2(-1, ImGui::GetContentRegionAvail().y - + ImGui::GetFrameHeightWithSpacing() - plot_style.PlotPadding.y), + ImPlotFlags_Equal | ImPlotFlags_NoFrame | ImPlotFlags_CanvasOnly)) + { + ImPlot::SetupAxes(nullptr, nullptr, + ImPlotAxisFlags_AutoFit | ImPlotAxisFlags_NoDecorations | + ImPlotAxisFlags_NoHighlight, + ImPlotAxisFlags_AutoFit | ImPlotAxisFlags_NoDecorations | + ImPlotAxisFlags_NoHighlight); + PlotHoverIdx(); + ImPlot::PlotPieChart( + m_kernel_pie.labels.data(), + m_kernel_pie.metric_sets[m_kernel_pie.selected_metric].pct_values.data(), + m_kernel_pie.metric_sets[m_kernel_pie.selected_metric].pct_values.size(), 0.0, + 0.0, PIE_CHART_RADIUS, + [](double value, char* buff, int size, void* user_data) -> int { + (void) user_data; + if(value * 100.0 > 10.0) + { + snprintf(buff, size, "%.1f%%", value * 100.0); + } + else + { + buff[0] = '\0'; + } + return 0; + }); + if(m_hovered_idx) + { + ImPlot::PushColormap("white"); + ImGui::PushID(1); + ImPlot::PlotPieChart( + &m_kernel_pie.labels[m_hovered_idx.value()], + &m_kernel_pie.metric_sets[m_kernel_pie.selected_metric] + .pct_values[m_hovered_idx.value()], + 1, 0.0, 0.0, PIE_CHART_RADIUS, + [](double value, char* buff, int size, void* user_data) -> int { + (void) user_data; + if(value) + { + snprintf(buff, size, "%.1f%%", value * 100.0); + } + else + { + buff[0] = '\0'; + } + return 0; + }, + nullptr, + m_kernel_pie.metric_sets[m_kernel_pie.selected_metric] + .slices[m_hovered_idx.value()] + .angle + + 90); + ImGui::PopID(); + ImPlot::PopColormap(); + } + ImPlot::EndPlot(); + } + ImPlot::PopStyleColor(2); + ImPlot::PopStyleVar(); + RenderPlotTooltip(m_kernel_pie.selected_metric, time_format); +} + +void +ComputeTopKernels::RenderBarChart(const ImPlotStyle& plot_style, TimeFormat time_format) +{ + ImGui::BeginChild("bar_area", ImVec2(0, ImGui::GetContentRegionAvail().y - + ImGui::GetFrameHeightWithSpacing() - + plot_style.PlotPadding.y)); + float y_axis_width = 0.1f * ImGui::GetContentRegionAvail().x; + ImPlot::PushStyleVar(ImPlotStyleVar_FitPadding, CHART_FIT_PADDING); + ImPlot::PushStyleColor(ImPlotCol_PlotBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg)); + ImPlot::PushStyleColor(ImPlotCol_FrameBg, m_settings.GetColor(Colors::kTransparent)); + ImGui::SetCursorPos(ImVec2(y_axis_width, 0.0f)); + if(ImPlot::BeginPlot("##Bar", ImVec2(-1, -1), + ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | + ImPlotFlags_CanvasOnly)) + { + ImPlot::SetupAxes( + m_kernel_bar.metric_sets[m_kernel_bar.selected_metric].axis_title.c_str(), + nullptr, ImPlotAxisFlags_Lock | ImPlotAxisFlags_NoHighlight, + ImPlotAxisFlags_AutoFit | ImPlotAxisFlags_NoTickLabels | + ImPlotAxisFlags_NoHighlight); + ImPlot::SetupAxisLimits( + ImAxis_X1, 0.0, + m_kernel_bar.metric_sets[m_kernel_bar.selected_metric].max_value * + (1 + CHART_FIT_PADDING.x * 0.5), + ImPlotCond_Always); + if(m_kernel_bar.selected_metric != KernelInfo::InvocationCount) + { + ImPlot::SetupAxisFormat( + ImAxis_X1, + [](double value, char* buff, int size, void* user_data) -> int { + if(value >= 0.0 && user_data) + { + const std::string fmt = nanosecond_to_formatted_str( + value, *static_cast(user_data)); + const size_t len_fmt = + std::min(fmt.length(), static_cast(size - 1)); + std::memcpy(buff, fmt.c_str(), len_fmt); + buff[len_fmt] = '\0'; + } + else + { + buff[0] = '\0'; + } + return 0; + }, + &time_format); + } + ImPlot::SetupAxisTicks(ImAxis_X1, + m_kernel_bar.metric_sets[m_kernel_bar.selected_metric] + .axis_tick_label_positions.data(), + m_kernel_bar.metric_sets[m_kernel_bar.selected_metric] + .axis_tick_label_positions.size()); + PlotHoverIdx(); + for(size_t i = 0; i < m_kernels.size(); i++) + { + if(i != m_padded_idx || + m_kernel_bar.selected_metric == KernelInfo::InvocationCount || + m_kernel_bar.selected_metric == KernelInfo::DurationTotal) + { + ImGui::PushID(i); + ImGui::SetCursorScreenPos(ImVec2( + plot_style.PlotPadding.x, + ImPlot::PlotToPixels(ImPlotPoint(0, i), IMPLOT_AUTO, IMPLOT_AUTO).y - + ImGui::GetFontSize() * 0.5f)); + ElidedText(m_kernels[i]->name.c_str(), + y_axis_width - plot_style.LabelPadding.x, + ImGui::GetContentRegionAvail().x * 0.5f, true); + if(i == m_hovered_idx) + { + ImPlot::PushColormap("white"); + } + ImPlot::SetNextFillStyle(ImPlot::GetColormapColor(static_cast(i))); + // PlotBars(uint64_t) may be undefined on Linux, cast to ImU64. + const ImU64 bar_value = static_cast( + m_kernels[i]->dispatch_metrics[m_kernel_bar.selected_metric]); + ImPlot::PlotBars(m_kernels[i]->name.c_str(), &bar_value, 1, + BAR_CHART_THICKNESS, i, ImPlotBarsFlags_Horizontal); + if(i == m_hovered_idx) + { + ImPlot::PopColormap(); + } + ImGui::PopID(); + } + } + float plot_width = ImPlot::GetPlotSize().x; + if(m_kernel_bar.width != plot_width) + { + m_kernel_bar.width = plot_width; + m_kernel_bar.tick_interval_dirty = true; + } + if(ImPlot::IsPlotHovered()) + { + RenderPlotTooltip(m_kernel_bar.selected_metric, time_format); + } + ImPlot::EndPlot(); + } + ImPlot::PopStyleColor(2); + ImPlot::PopStyleVar(); + ImGui::EndChild(); +} + +void +ComputeTopKernels::RenderTable(const ImPlotStyle& plot_style, TimeFormat time_format, + std::optional& hovered_idx) +{ + ImGui::SetCursorPos(ImVec2(plot_style.PlotBorderSize + plot_style.PlotPadding.x, + ImGui::GetFontSize() + plot_style.PlotBorderSize + + plot_style.PlotPadding.y + + 2 * plot_style.LabelPadding.y)); + if(ImGui::BeginTable( + "Table", 2 + KernelInfo::NumMetrics, + ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable, + ImGui::GetContentRegionAvail() - + ImVec2(plot_style.PlotBorderSize + plot_style.PlotPadding.x, + ImGui::GetContentRegionAvail().y * 0.5f + + ImGui::GetFrameHeightWithSpacing() + + 2 * plot_style.PlotPadding.y + plot_style.PlotBorderSize))) + { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("##legend", + ImGuiTableColumnFlags_NoResize | + ImGuiTableColumnFlags_NoSort | + ImGuiTableColumnFlags_WidthFixed, + ImGui::GetFontSize()); + ImGui::TableSetupColumn("Name"); + for(int i = 0; i < KernelInfo::NumMetrics; i++) + { + KernelInfo::DispatchMetric metric = + static_cast(i); + switch(metric) + { + case KernelInfo::InvocationCount: + { + ImGui::TableSetupColumn( + DISPLAY_STRING_METRICS[KernelInfo::InvocationCount]); + break; + } + default: + { + ImGui::TableSetupColumn( + std::string(std::string(DISPLAY_STRING_METRICS[metric]) + " (" + + timeformat_sufix(time_format) + ")") + .c_str()); + break; + } + } + } + ImGui::TableHeadersRow(); + for(size_t i = 0; i < m_kernels.size(); i++) + { + ImGui::PushID(i); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::GetWindowDrawList()->AddRectFilled( + ImGui::GetCursorScreenPos() + + ImVec2(2 * IMPLOT_LEGEND_ICON_SHRINK, 2 * IMPLOT_LEGEND_ICON_SHRINK), + ImGui::GetCursorScreenPos() + + ImVec2(ImGui::GetFontSize() - 2 * IMPLOT_LEGEND_ICON_SHRINK, + ImGui::GetFontSize() - 2 * IMPLOT_LEGEND_ICON_SHRINK), + ImGui::GetColorU32( + ImGui::GetColorU32(ImPlot::GetColormapColor(static_cast(i))), + false ? 0.75f : 1.0f)); + ImGui::Selectable("", false, + (m_hovered_idx == i ? ImGuiSelectableFlags_Highlight + : ImGuiSelectableFlags_None) | + ImGuiSelectableFlags_SpanAllColumns); + if(ImGui::IsItemHovered()) + { + hovered_idx = i; + } + ImGui::TableSetColumnIndex(1); + ElidedText(m_kernels[i]->name.c_str(), ImGui::GetContentRegionAvail().x, + ImGui::GetWindowWidth() * 0.5f); + for(size_t j = 0; j < m_kernels[i]->dispatch_metrics.size(); j++) + { + ImGui::TableSetColumnIndex(2 + j); + KernelInfo::DispatchMetric metric = + static_cast(j); + switch(metric) + { + case KernelInfo::InvocationCount: + { + ImGui::Text("%u", m_kernels[i]->dispatch_metrics[metric]); + break; + } + case KernelInfo::DurationTotal: + { + ImGui::TextUnformatted( + nanosecond_to_formatted_str( + static_cast( + m_kernels[i]->dispatch_metrics[metric]), + time_format) + .c_str()); + break; + } + default: + { + ImGui::TextUnformatted( + i == m_padded_idx + ? "-" + : nanosecond_to_formatted_str( + static_cast( + m_kernels[i]->dispatch_metrics[metric]), + time_format) + .c_str()); + break; + } + } + } + ImGui::PopID(); + } + ImGui::EndTable(); + } +} + +void +ComputeTopKernels::RenderPlotTooltip(KernelInfo::DispatchMetric metric, + TimeFormat time_format) +{ + if(m_hovered_idx && 0 <= m_hovered_idx && m_hovered_idx < m_kernels.size()) + { + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, + m_settings.GetDefaultIMGUIStyle().WindowPadding); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, + m_settings.GetDefaultStyle().FrameRounding); + if(ImGui::BeginTooltip()) + { + ImVec2 reserved_pos = ImGui::GetCursorPos(); + ImGui::GetWindowDrawList()->AddRectFilled( + ImGui::GetCursorScreenPos(), + ImGui::GetCursorScreenPos() + + ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetFontSize()), + ImGui::GetColorU32( + ImPlot::GetColormapColor(static_cast(m_hovered_idx.value())))); + ImGui::NewLine(); + switch(metric) + { + case KernelInfo::InvocationCount: + { + ImGui::Text( + "%s: %llu", DISPLAY_STRING_METRICS[metric], + m_kernels[m_hovered_idx.value()]->dispatch_metrics[metric]); + break; + } + default: + { + ImGui::Text( + "%s: %s", DISPLAY_STRING_METRICS[metric], + nanosecond_to_formatted_str( + m_kernels[m_hovered_idx.value()]->dispatch_metrics[metric], + time_format, true) + .c_str()); + break; + } + } + ImGui::SetCursorPos(reserved_pos); + ElidedText(m_kernels[m_hovered_idx.value()]->name.c_str(), + ImGui::GetItemRectSize().x); + ImGui::EndTooltip(); + } + ImGui::PopStyleVar(2); + } +} + +void +ComputeTopKernels::PlotHoverIdx() +{ + if(ImPlot::IsPlotHovered() && !m_kernels.empty()) + { + ImPlotPoint mouse_pos = ImPlot::GetPlotMousePos(); + switch(m_display_mode) + { + case Pie: + { + if(sqrt(mouse_pos.x * mouse_pos.x + mouse_pos.y * mouse_pos.y) <= + PIE_CHART_RADIUS) + { + double angle = fmod( + -atan2(mouse_pos.x, mouse_pos.y) * 180.0 / PI + 360.0, 360.0); + for(size_t i = 0; + i < m_kernel_pie.metric_sets[m_kernel_pie.selected_metric] + .slices.size(); + i++) + { + if(angle >= m_kernel_pie.metric_sets[m_kernel_pie.selected_metric] + .slices[i] + .angle && + angle < + m_kernel_pie.metric_sets[m_kernel_pie.selected_metric] + .slices[i] + .angle + + m_kernel_pie.metric_sets[m_kernel_pie.selected_metric] + .slices[i] + .size_angle) + { + m_hovered_idx = i; + break; + } + } + } + break; + } + case Bar: + { + for(size_t i = 0; i < m_kernels.size(); i++) + { + if(mouse_pos.x >= 0.0 && + mouse_pos.x <= + m_kernels[i]->dispatch_metrics[m_kernel_bar.selected_metric] && + mouse_pos.y >= + static_cast(i) - BAR_CHART_THICKNESS / 2.0 && + mouse_pos.y <= static_cast(i) + BAR_CHART_THICKNESS / 2.0) + { + m_hovered_idx = i; + break; + } + } + break; + } + } + } +} + } // namespace View } // namespace RocProfVis diff --git a/src/view/src/compute/rocprofvis_compute_summary.h b/src/view/src/compute/rocprofvis_compute_summary.h index 57121f3b..ab27c37e 100644 --- a/src/view/src/compute/rocprofvis_compute_summary.h +++ b/src/view/src/compute/rocprofvis_compute_summary.h @@ -2,8 +2,13 @@ // SPDX-License-Identifier: MIT #pragma once +#include "compute/rocprofvis_compute_model_types.h" #include "rocprofvis_event_manager.h" +#include "rocprofvis_utils.h" #include "widgets/rocprofvis_widget.h" +#include + +struct ImPlotStyle; namespace RocProfVis { @@ -11,8 +16,10 @@ namespace View { class DataProvider; +class SettingsManager; class ComputeSelection; class Roofline; +class ComputeTopKernels; class ComputeSummaryView : public RocWidget { @@ -27,8 +34,9 @@ class ComputeSummaryView : public RocWidget private: DataProvider& m_data_provider; - std::shared_ptr m_compute_selection; - std::unique_ptr m_roofline; + std::shared_ptr m_compute_selection; + std::unique_ptr m_roofline; + std::unique_ptr m_top_kernels; uint64_t m_client_id; @@ -36,5 +44,101 @@ class ComputeSummaryView : public RocWidget EventManager::SubscriptionToken m_metrics_fetched_token; }; +class ComputeTopKernels : public RocWidget +{ +public: + ComputeTopKernels(DataProvider& dp); + ~ComputeTopKernels(); + + void Update() override; + void Render() override; + + void SetWorkload(uint32_t id); + +private: + enum DisplayMode + { + Pie, + Bar, + }; + struct KernelPieModel + { + struct Slice + { + float angle; + float size_angle; + }; + // Metric specific configuration... + struct MetricSet + { + KernelInfo::DispatchMetric metric; // Id. + bool available; // Some metrics cannot be displayed in pie. + std::vector pct_values; // Values as a % of 360. + std::vector slices; // Geometry for hover detection. + }; + std::vector labels; + KernelInfo::DispatchMetric selected_metric; + std::array metric_sets; + }; + struct KernelBarModel + { + // Metric specific configuration... + struct MetricSet + { + KernelInfo::DispatchMetric metric; // Id. + uint64_t max_value; // Max X value for X axis definition. + std::vector axis_tick_label_positions; // X axis tick positions for X + // axis definition. + std::string axis_title; + }; + KernelInfo::DispatchMetric selected_metric; + std::array metric_sets; + float width; // Width of plot for X axis definition. + bool axis_label_dirty; // User changed time format. + bool tick_interval_dirty; // User changed time format/selected_metric/width. + }; + struct TopKernelsComparator + { + bool operator()(const KernelInfo* a, const KernelInfo* b) const + { + return a->dispatch_metrics[KernelInfo::DurationTotal] > + b->dispatch_metrics[KernelInfo::DurationTotal]; + } + }; + + void GetTopKernels(std::vector& kernels); + void RenderPieChart(const ImPlotStyle& plot_style, TimeFormat time_format); + void RenderBarChart(const ImPlotStyle& plot_style, TimeFormat time_format); + void RenderTable(const ImPlotStyle& plot_style, TimeFormat time_format, + std::optional& hovered_idx); + void RenderPlotTooltip(KernelInfo::DispatchMetric metric, TimeFormat time_format); + + // Input handling... + void PlotHoverIdx(); + + // Interfaces... + DataProvider& m_data_provider; + SettingsManager& m_settings; + + // Kernel/Pie specific state... + KernelPieModel m_kernel_pie; + KernelBarModel m_kernel_bar; + + // Common state... + DisplayMode m_display_mode; + bool m_show_legend; + std::vector m_kernels; // top kernels + std::optional m_hovered_idx; + std::optional m_padded_idx; // "Others" position + std::unique_ptr m_padded_info; // "Others" data + + // External flags... + bool m_workload_changed; + const WorkloadInfo* m_workload; + uint32_t m_requested_workload_id; + + EventManager::SubscriptionToken m_time_format_changed_token; +}; + } // namespace View } // namespace RocProfVis diff --git a/src/view/src/compute/rocprofvis_compute_tester.cpp b/src/view/src/compute/rocprofvis_compute_tester.cpp index fe10a1d9..ae97f291 100644 --- a/src/view/src/compute/rocprofvis_compute_tester.cpp +++ b/src/view/src/compute/rocprofvis_compute_tester.cpp @@ -332,17 +332,17 @@ ComputeTester::Render() ImGui::TableNextColumn(); ImGui::Text(kernel.second.name.c_str()); ImGui::TableNextColumn(); - ImGui::Text("%u", kernel.second.invocation_count); + ImGui::Text("%llu", kernel.second.dispatch_metrics[KernelInfo::InvocationCount]); ImGui::TableNextColumn(); - ImGui::Text("%llu", kernel.second.duration_total); + ImGui::Text("%llu", kernel.second.dispatch_metrics[KernelInfo::DurationTotal]); ImGui::TableNextColumn(); - ImGui::Text("%u", kernel.second.duration_min); + ImGui::Text("%llu", kernel.second.dispatch_metrics[KernelInfo::DurationMin]); ImGui::TableNextColumn(); - ImGui::Text("%u", kernel.second.duration_max); + ImGui::Text("%llu", kernel.second.dispatch_metrics[KernelInfo::DurationMax]); ImGui::TableNextColumn(); - ImGui::Text("%u", kernel.second.duration_mean); + ImGui::Text("%llu", kernel.second.dispatch_metrics[KernelInfo::DurationMean]); ImGui::TableNextColumn(); - ImGui::Text("%u", kernel.second.duration_median); + ImGui::Text("%llu", kernel.second.dispatch_metrics[KernelInfo::DurationMedian]); ImGui::PopID(); } ImGui::EndTable(); diff --git a/src/view/src/compute/rocprofvis_compute_view.cpp b/src/view/src/compute/rocprofvis_compute_view.cpp index e3f7fdc0..b960c5ce 100644 --- a/src/view/src/compute/rocprofvis_compute_view.cpp +++ b/src/view/src/compute/rocprofvis_compute_view.cpp @@ -234,6 +234,5 @@ ComputeView::RenderWorkloadSelection() } } - } // namespace View } // namespace RocProfVis diff --git a/src/view/src/model/compute/rocprofvis_compute_model_types.h b/src/view/src/model/compute/rocprofvis_compute_model_types.h index f43d7ff7..2734a8b1 100644 --- a/src/view/src/model/compute/rocprofvis_compute_model_types.h +++ b/src/view/src/model/compute/rocprofvis_compute_model_types.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace RocProfVis { @@ -53,6 +54,16 @@ struct Point struct KernelInfo { + enum DispatchMetric + { + InvocationCount, + DurationTotal, + DurationMin, + DurationMax, + DurationMean, + DurationMedian, + NumMetrics + }; struct Roofline { struct Intensity @@ -64,15 +75,10 @@ struct KernelInfo Intensity> intensities; }; - uint32_t id; - std::string name; - uint32_t invocation_count; - uint64_t duration_total; - uint32_t duration_min; - uint32_t duration_max; - uint32_t duration_mean; - uint32_t duration_median; - Roofline roofline; + uint32_t id; + std::string name; + std::array dispatch_metrics; + Roofline roofline; }; struct WorkloadInfo diff --git a/src/view/src/rocprofvis_data_provider.cpp b/src/view/src/rocprofvis_data_provider.cpp index 8d0c1139..bbd08cda 100644 --- a/src/view/src/rocprofvis_data_provider.cpp +++ b/src/view/src/rocprofvis_data_provider.cpp @@ -3927,30 +3927,35 @@ DataProvider::ProcessLoadComputeTrace(RequestInfo& req) ROCPROFVIS_ASSERT(result == kRocProfVisResultSuccess); kernel.id = static_cast(uint64_data); kernel.name = GetString(kernel_handle, kRPVControllerKernelName, 0); + kernel.dispatch_metrics = {}; result = rocprofvis_controller_get_uint64( kernel_handle, kRPVControllerKernelInvocationCount, 0, &uint64_data); ROCPROFVIS_ASSERT(result == kRocProfVisResultSuccess); - kernel.invocation_count = static_cast(uint64_data); - result = rocprofvis_controller_get_uint64( + kernel.dispatch_metrics[KernelInfo::InvocationCount] = uint64_data; + result = rocprofvis_controller_get_uint64( kernel_handle, kRPVControllerKernelDurationTotal, 0, &uint64_data); ROCPROFVIS_ASSERT(result == kRocProfVisResultSuccess); - kernel.duration_total = uint64_data; - result = rocprofvis_controller_get_uint64( + kernel.dispatch_metrics[KernelInfo::DurationTotal] = uint64_data; + result = rocprofvis_controller_get_uint64( kernel_handle, kRPVControllerKernelDurationMin, 0, &uint64_data); ROCPROFVIS_ASSERT(result == kRocProfVisResultSuccess); - kernel.duration_min = static_cast(uint64_data); - result = rocprofvis_controller_get_uint64( + kernel.dispatch_metrics[KernelInfo::DurationMin] = + static_cast(uint64_data); + result = rocprofvis_controller_get_uint64( kernel_handle, kRPVControllerKernelDurationMax, 0, &uint64_data); ROCPROFVIS_ASSERT(result == kRocProfVisResultSuccess); - kernel.duration_max = static_cast(uint64_data); - result = rocprofvis_controller_get_uint64( + kernel.dispatch_metrics[KernelInfo::DurationMax] = + static_cast(uint64_data); + result = rocprofvis_controller_get_uint64( kernel_handle, kRPVControllerKernelDurationMean, 0, &uint64_data); ROCPROFVIS_ASSERT(result == kRocProfVisResultSuccess); - kernel.duration_mean = static_cast(uint64_data); - result = rocprofvis_controller_get_uint64( + kernel.dispatch_metrics[KernelInfo::DurationMean] = + static_cast(uint64_data); + result = rocprofvis_controller_get_uint64( kernel_handle, kRPVControllerKernelDurationMedian, 0, &uint64_data); ROCPROFVIS_ASSERT(result == kRocProfVisResultSuccess); - kernel.duration_median = static_cast(uint64_data); + kernel.dispatch_metrics[KernelInfo::DurationMedian] = + static_cast(uint64_data); workload.kernels[kernel.id] = std::move(kernel); } rocprofvis_handle_t* roofline_handle = nullptr; diff --git a/src/view/src/rocprofvis_summary_view.cpp b/src/view/src/rocprofvis_summary_view.cpp index 540502f4..01efad11 100644 --- a/src/view/src/rocprofvis_summary_view.cpp +++ b/src/view/src/rocprofvis_summary_view.cpp @@ -945,8 +945,8 @@ TopKernels::PlotHoverIdx() const { for(size_t i = 0; i < m_kernels->size(); i++) { - if(mouse_pos.x >= static_cast(i) - PIE_CHART_RADIUS / 2.0 && - mouse_pos.x <= static_cast(i) + PIE_CHART_RADIUS / 2.0 && + if(mouse_pos.x >= static_cast(i) - BAR_CHART_THICKNESS / 2.0 && + mouse_pos.x <= static_cast(i) + BAR_CHART_THICKNESS / 2.0 && mouse_pos.y >= 0.0 && mouse_pos.y <= (*m_kernels)[i].exec_time_sum) { idx = i; diff --git a/src/view/src/rocprofvis_timeline_view.cpp b/src/view/src/rocprofvis_timeline_view.cpp index 7c4ab899..80b4ef1a 100644 --- a/src/view/src/rocprofvis_timeline_view.cpp +++ b/src/view/src/rocprofvis_timeline_view.cpp @@ -891,7 +891,6 @@ TimelineView::GetGraphs() void TimelineView::CalculateGridInterval() { - // measure the size of the label to determine the step size std::string label = nanosecond_to_formatted_str( m_tpt->GetRangeX(), m_settings.GetUserSettings().unit_settings.time_format, @@ -899,32 +898,11 @@ TimelineView::CalculateGridInterval() "gap"; ImVec2 label_size = ImGui::CalcTextSize(label.c_str()); - // calculate the number of intervals based on the graph width and label width - int interval_count = - label_size.x > 0 ? static_cast(m_tpt->GetGraphSizeX() / label_size.x) : 0; + FittedGraphAxisInterval fitted_interval = fit_graph_axis_interval( + m_tpt->GetVWidth(), m_tpt->GetGraphSizeX(), label_size.x, false, 3); - double interval_ns = calculate_nice_interval(m_tpt->GetVWidth(), interval_count); - double step_size_px = interval_ns * m_tpt->GetPixelsPerNs(); - - int pad_amount = 2; // +2 for the first and last label - - // If the step size is smaller than the label size, try to adjust the interval - // count - while(step_size_px < label_size.x) - { - interval_count--; - interval_ns = calculate_nice_interval(m_tpt->GetVWidth(), interval_count); - step_size_px = interval_ns * m_tpt->GetPixelsPerNs(); - // If the interval count is too small break out and pad it - if(interval_count <= 3) - { - pad_amount++; - break; - } - } - - m_grid_interval_ns = interval_ns; - m_grid_interval_count = interval_count + pad_amount; + m_grid_interval_ns = fitted_interval.interval_ns; + m_grid_interval_count = fitted_interval.interval_count; } void @@ -1557,44 +1535,19 @@ TimelineView::RenderHistogram() ImFont* font = m_settings.GetFontManager().GetFont(FontType::kSmall); float label_font_size = font->FontSize; - // Interval calculation - // measure the size of the label to determine the step size std::string label = nanosecond_to_formatted_str(m_tpt->GetRangeX(), time_format, true) + "gap"; ImVec2 label_size = ImGui::CalcTextSize(label.c_str()); - // calculate the number of intervals based on the graph width and label width - // reserve space for first and last label - int interval_count = - static_cast((ruler_width - label_size.x * 2.0f) / label_size.x); - if(interval_count < 1) interval_count = 1; + FittedGraphAxisInterval fitted_interval = + fit_graph_axis_interval(m_tpt->GetRangeX(), ruler_width, label_size.x, true, 0); double pixels_per_ns = m_tpt->GetGraphSizeX() / m_tpt->GetRangeX(); - double interval_ns = calculate_nice_interval(m_tpt->GetRangeX(), interval_count); - double step_size_px = interval_ns * pixels_per_ns; - int pad_amount = 2; // +2 for the first and last label - - // If the step size is smaller than the label size, try to adjust the interval - // count - while(step_size_px < label_size.x) - { - interval_count--; - interval_ns = calculate_nice_interval(m_tpt->GetRangeX(), interval_count); - step_size_px = interval_ns * pixels_per_ns; - // If the interval count is too small break out - if(interval_count <= 0) - { - break; - } - } - - const int num_ticks = interval_count + pad_amount; - double grid_line_start_ns = 0; - ImVec2 window_pos = ImGui::GetWindowPos(); + ImVec2 window_pos = ImGui::GetWindowPos(); - for(int i = 0; i < num_ticks; i++) + for(int i = 0; i < fitted_interval.interval_count; i++) { - double tick_ns = grid_line_start_ns + (i * interval_ns); + double tick_ns = i * fitted_interval.interval_ns; // calculate x pos avoiding tpt related functions because histogram does not // use zoom/pan logic float tick_x = static_cast(window_pos.x + tick_ns * pixels_per_ns); diff --git a/src/view/src/rocprofvis_utils.cpp b/src/view/src/rocprofvis_utils.cpp index dcc6510f..263a21b0 100644 --- a/src/view/src/rocprofvis_utils.cpp +++ b/src/view/src/rocprofvis_utils.cpp @@ -262,6 +262,43 @@ RocProfVis::View::calculate_nice_interval(double view_range, int target_division return nice_multiplier * scale; } +RocProfVis::View::FittedGraphAxisInterval +RocProfVis::View::fit_graph_axis_interval(double max_ns, float available_width, + float label_width, bool pad_first_and_last, + int min_intervals) +{ + // calculate the number of intervals based on available width and label width + // reserve space for first and last label if requested + int interval_count = + label_width > 0 + ? (static_cast((pad_first_and_last ? available_width - label_width * 2.0f + : available_width) / + label_width)) + : 0; + if(interval_count < 1) interval_count = 1; + + double pixels_per_ns = available_width / max_ns; + double interval_ns = calculate_nice_interval(max_ns, interval_count); + double step_size_px = interval_ns * pixels_per_ns; + + int pad_amount = 2; // +2 for the first and last label + + // If the step size is smaller than the label size, try to adjust the interval + // count + while(step_size_px < label_width) + { + interval_count--; + interval_ns = calculate_nice_interval(max_ns, interval_count); + step_size_px = interval_ns * pixels_per_ns; + // If the interval count is too small break out + if(interval_count <= min_intervals) + { + break; + } + } + return { interval_ns, interval_count + pad_amount }; +} + RocProfVis::View::ViewRangeNS RocProfVis::View::calculate_adaptive_view_range(double item_start_ns, double item_duration_ns) diff --git a/src/view/src/rocprofvis_utils.h b/src/view/src/rocprofvis_utils.h index d72156f2..2af17c81 100644 --- a/src/view/src/rocprofvis_utils.h +++ b/src/view/src/rocprofvis_utils.h @@ -182,6 +182,28 @@ timeformat_sufix(TimeFormat format); double calculate_nice_interval(double view_range, int target_divisions); +typedef struct FittedGraphAxisInterval +{ + double interval_ns; + int interval_count; +} FittedGraphAxisInterval; + +/** + * @brief Calculates an axis interval that is fitted to the axis label width without + * labels overlap. + * + * @param max_ns Max axis value. + * @param available_width Axis width. + * @param label_width Width of axis label. + * @param pad_first_and_last If true, reserves 2 label_width of space for first/last + * labels. + * @param min_intervals Floor number of intervals. + * @return FittedGraphAxisInterval struct with interval count and size. + */ +FittedGraphAxisInterval +fit_graph_axis_interval(double max_ns, float available_width, float label_width, + bool pad_first_and_last, int min_intervals); + typedef struct ViewRangeNS { double start_ns; diff --git a/src/view/src/widgets/rocprofvis_gui_helpers.cpp b/src/view/src/widgets/rocprofvis_gui_helpers.cpp index 2aeff40e..28615e8c 100644 --- a/src/view/src/widgets/rocprofvis_gui_helpers.cpp +++ b/src/view/src/widgets/rocprofvis_gui_helpers.cpp @@ -223,7 +223,7 @@ EndTooltipStyled() void ElidedText(const char* text, float available_width, float tooltip_width, - bool imgui_AlignTextToFramePadding) + bool right_justify, bool imgui_AlignTextToFramePadding) { ImGuiStyle style = ImGui::GetStyle(); SettingsManager& settings = SettingsManager::GetInstance(); @@ -254,6 +254,10 @@ ElidedText(const char* text, float available_width, float tooltip_width, ImGui::GetFrameHeightWithSpacing()), true); } + else if(right_justify) + { + ImGui::SetCursorPosX(available_width - text_width); + } ImGui::TextUnformatted(text); if(elide) { diff --git a/src/view/src/widgets/rocprofvis_gui_helpers.h b/src/view/src/widgets/rocprofvis_gui_helpers.h index 05185e68..e9f33a2f 100644 --- a/src/view/src/widgets/rocprofvis_gui_helpers.h +++ b/src/view/src/widgets/rocprofvis_gui_helpers.h @@ -50,7 +50,7 @@ EndTooltipStyled(); void ElidedText(const char* text, float available_width, float tooltip_width = 0.0f, - bool imgui_AlignTextToFramePadding = false); + bool right_justify = false, bool imgui_AlignTextToFramePadding = false); /* * Center the next text item horizontally with respect to the available