From 21c07121020fb36c4a0ec5ef0109561bc6ae03d6 Mon Sep 17 00:00:00 2001 From: Garima Dhaked Date: Wed, 22 Apr 2026 01:32:07 -0600 Subject: [PATCH] xdp: add peak_read_bandwidth and peak_write_bandwidth metric sets for AIE dtrace Add new metric sets for interface tiles that configure both PORT_RUNNING and PORT_STALL events per channel, enabling peak bandwidth calculation. For peak_read_bandwidth (S2MM channels - read from DDR): - Counter 0: PORT_RUNNING_0 (Ch0 bytes transferred) - Counter 1: PORT_STALLED_0 (Ch0 stall cycles) - Counter 2: PORT_RUNNING_1 (Ch1 bytes transferred) - Counter 3: PORT_STALLED_1 (Ch1 stall cycles) For peak_write_bandwidth (MM2S channels - write to DDR): - Counter 0: PORT_RUNNING_0 (Ch0 bytes transferred) - Counter 1: PORT_STALLED_0 (Ch0 stall cycles) - Counter 2: PORT_RUNNING_1 (Ch1 bytes transferred) - Counter 3: PORT_STALLED_1 (Ch1 stall cycles) This enables post-processing to calculate: - Peak BW = Total Bytes / Running Cycles (excludes stall time) - Efficiency = Running Cycles / (Running Cycles + Stall Cycles) Signed-off-by: Garima Dhaked Made-with: Cursor --- .../filetypes/aie_control_config_filetype.cpp | 4 +- .../aie_dtrace/util/aie_dtrace_util.cpp | 6 + .../aie_dtrace/ve2/aie_dtrace_ct_writer.cpp | 118 ++++++++++++++---- .../aie_dtrace/ve2/aie_dtrace_ct_writer.h | 28 +++-- .../plugin/aie_dtrace/ve2/aie_dtrace_ve2.cpp | 32 ++++- .../aie_profile/aie_profile_metadata.cpp | 3 +- 6 files changed, 150 insertions(+), 41 deletions(-) diff --git a/profile/database/static_info/filetypes/aie_control_config_filetype.cpp b/profile/database/static_info/filetypes/aie_control_config_filetype.cpp index 27da3f57..3626c878 100644 --- a/profile/database/static_info/filetypes/aie_control_config_filetype.cpp +++ b/profile/database/static_info/filetypes/aie_control_config_filetype.cpp @@ -377,7 +377,9 @@ AIEControlConfigFiletype::getInterfaceTiles(const std::string& graphName, (metricStr != METRIC_BYTE_COUNT) && (metricStr != "ddr_bandwidth") && (metricStr != "read_bandwidth") && - (metricStr != "write_bandwidth")) + (metricStr != "write_bandwidth") && + (metricStr != "peak_read_bandwidth") && + (metricStr != "peak_write_bandwidth")) continue; } diff --git a/profile/plugin/aie_dtrace/util/aie_dtrace_util.cpp b/profile/plugin/aie_dtrace/util/aie_dtrace_util.cpp index 1d125029..6959f9e4 100644 --- a/profile/plugin/aie_dtrace/util/aie_dtrace_util.cpp +++ b/profile/plugin/aie_dtrace/util/aie_dtrace_util.cpp @@ -17,6 +17,12 @@ namespace xdp::aie::dtrace { {"ddr_bandwidth", {XAIE_EVENT_PORT_RUNNING_0_PL, XAIE_EVENT_PORT_RUNNING_1_PL, XAIE_EVENT_PORT_RUNNING_2_PL, XAIE_EVENT_PORT_RUNNING_3_PL}}, + {"peak_read_bandwidth", + {XAIE_EVENT_PORT_RUNNING_0_PL, XAIE_EVENT_PORT_STALLED_0_PL, + XAIE_EVENT_PORT_RUNNING_1_PL, XAIE_EVENT_PORT_STALLED_1_PL}}, + {"peak_write_bandwidth", + {XAIE_EVENT_PORT_RUNNING_0_PL, XAIE_EVENT_PORT_STALLED_0_PL, + XAIE_EVENT_PORT_RUNNING_1_PL, XAIE_EVENT_PORT_STALLED_1_PL}}, }; } diff --git a/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.cpp b/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.cpp index 8a9a8d20..8e7e80ed 100644 --- a/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.cpp +++ b/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.cpp @@ -474,6 +474,16 @@ std::string AieDtraceCTWriter::getPortDirection(const std::string& metricSet, ui return isMaster ? "output" : "input"; } + // peak_read_bandwidth: S2MM channels (input/read from DDR) + if (metricSet == "peak_read_bandwidth") { + return "input"; + } + + // peak_write_bandwidth: MM2S channels (output/write to DDR) + if (metricSet == "peak_write_bandwidth") { + return "output"; + } + // For input/s2mm metrics - always input direction if (metricSet.find("input") != std::string::npos || metricSet.find("s2mm") != std::string::npos) { @@ -670,7 +680,8 @@ std::vector AieDtraceCTWriter::getShimTileColumns(void* hwctx) return columns; } -std::vector AieDtraceCTWriter::getBandwidthCounterConfigs() +std::vector AieDtraceCTWriter::getBandwidthCounterConfigs( + const std::string& metricSet) { // VE2 shim tile DMA port indices for stream switch event monitoring // These port indices are architecture-specific and map to the physical @@ -690,16 +701,40 @@ std::vector AieDtraceCTWriter::getBandwidthCounterConfig // - MM2S ch0: slave South3 => port index 5 // - MM2S ch1: slave South7 => port index 9 // - // counterNumber, channel, dmaPortIndex, isMaster, direction + // For peak_read_bandwidth: 2 S2MM channels with RUNNING + STALL events + // For peak_write_bandwidth: 2 MM2S channels with RUNNING + STALL events + // For ddr_bandwidth/read_bandwidth/write_bandwidth: 4 ports with RUNNING events only + // + // counterNumber, channel, dmaPortIndex, isMaster, direction, eventType + if (metricSet == "peak_read_bandwidth") { + // S2MM ch0/ch1 with RUNNING + STALL events for peak read bandwidth + return { + {0, 0, 3, true, "input", "running"}, // Counter 0: S2MM Ch0 RUNNING + {1, 0, 3, true, "input", "stalled"}, // Counter 1: S2MM Ch0 STALL + {2, 1, 5, true, "input", "running"}, // Counter 2: S2MM Ch1 RUNNING + {3, 1, 5, true, "input", "stalled"} // Counter 3: S2MM Ch1 STALL + }; + } + else if (metricSet == "peak_write_bandwidth") { + // MM2S ch0/ch1 with RUNNING + STALL events for peak write bandwidth + return { + {0, 0, 5, false, "output", "running"}, // Counter 0: MM2S Ch0 RUNNING + {1, 0, 5, false, "output", "stalled"}, // Counter 1: MM2S Ch0 STALL + {2, 1, 9, false, "output", "running"}, // Counter 2: MM2S Ch1 RUNNING + {3, 1, 9, false, "output", "stalled"} // Counter 3: MM2S Ch1 STALL + }; + } + // Default: ddr_bandwidth, read_bandwidth, write_bandwidth return { - {0, 0, 3, true, "input"}, // Counter 0: S2MM Ch0 (master South1) - read_bandwidth - {1, 1, 5, true, "input"}, // Counter 1: S2MM Ch1 (master South3) - read_bandwidth - {2, 0, 5, false, "output"}, // Counter 2: MM2S Ch0 (slave South3) - write_bandwidth - {3, 1, 9, false, "output"} // Counter 3: MM2S Ch1 (slave South7) - write_bandwidth + {0, 0, 3, true, "input", "running"}, // Counter 0: S2MM Ch0 (master South1) - read_bandwidth + {1, 1, 5, true, "input", "running"}, // Counter 1: S2MM Ch1 (master South3) - read_bandwidth + {2, 0, 5, false, "output", "running"}, // Counter 2: MM2S Ch0 (slave South3) - write_bandwidth + {3, 1, 9, false, "output", "running"} // Counter 3: MM2S Ch1 (slave South7) - write_bandwidth }; } -std::vector AieDtraceCTWriter::generateStreamSwitchPortConfig(uint8_t column) +std::vector AieDtraceCTWriter::generateStreamSwitchPortConfig( + uint8_t column, const std::string& metricSet) { std::vector writes; @@ -707,7 +742,7 @@ std::vector AieDtraceCTWriter::generateStreamSwitchPortConfig(u (static_cast(SHIM_ROW) << rowShift); uint64_t regAddr = tileAddress + STREAM_SWITCH_EVENT_PORT_SEL_OFFSET; - auto configs = getBandwidthCounterConfigs(); + auto configs = getBandwidthCounterConfigs(metricSet); // Build the register value: 4 ports packed into 32 bits, 8 bits per port // Each port: bits [4:0] = DMA port index, bit [5] = slave(0)/master(1) @@ -721,8 +756,13 @@ std::vector AieDtraceCTWriter::generateStreamSwitchPortConfig(u } std::stringstream comment; - comment << "SS port sel @ col " << static_cast(column) - << " (S2MM ch0,ch1; MM2S ch0,ch1)"; + comment << "SS port sel @ col " << static_cast(column); + if (metricSet == "peak_read_bandwidth") + comment << " (S2MM ch0,ch1 x2 for running+stall)"; + else if (metricSet == "peak_write_bandwidth") + comment << " (MM2S ch0,ch1 x2 for running+stall)"; + else + comment << " (S2MM ch0,ch1; MM2S ch0,ch1)"; CTRegisterWrite write; write.address = regAddr; @@ -733,7 +773,8 @@ std::vector AieDtraceCTWriter::generateStreamSwitchPortConfig(u return writes; } -std::vector AieDtraceCTWriter::generatePerfCounterConfig(uint8_t column) +std::vector AieDtraceCTWriter::generatePerfCounterConfig( + uint8_t column, const std::string& metricSet) { std::vector writes; @@ -759,19 +800,33 @@ std::vector AieDtraceCTWriter::generatePerfCounterConfig(uint8_ constexpr uint64_t PERF_CTRL0_OFFSET = 0x00031000; constexpr uint64_t PERF_CTRL2_OFFSET = 0x0003100C; - // PORT_RUNNING events for byte counting (aie2ps shim tile events) + // PORT_RUNNING and PORT_STALLED events for aie2ps shim tile // Port_Running_N events: 134, 138, 142, 146 (decimal) + // Port_Stalled_N events: 135, 139, 143, 147 (decimal) constexpr uint8_t PORT_RUNNING_0_PL_EVENT = 0x86; // 134 + constexpr uint8_t PORT_STALLED_0_PL_EVENT = 0x87; // 135 constexpr uint8_t PORT_RUNNING_1_PL_EVENT = 0x8A; // 138 + constexpr uint8_t PORT_STALLED_1_PL_EVENT = 0x8B; // 139 constexpr uint8_t PORT_RUNNING_2_PL_EVENT = 0x8E; // 142 constexpr uint8_t PORT_RUNNING_3_PL_EVENT = 0x92; // 146 - uint8_t startEvents[4] = { - PORT_RUNNING_0_PL_EVENT, - PORT_RUNNING_1_PL_EVENT, - PORT_RUNNING_2_PL_EVENT, - PORT_RUNNING_3_PL_EVENT - }; + uint8_t startEvents[4]; + + if (metricSet == "peak_read_bandwidth" || metricSet == "peak_write_bandwidth") { + // For peak bandwidth: Counter 0,2 = RUNNING, Counter 1,3 = STALLED + // This allows calculating peak BW = bytes / running_cycles (excluding stalls) + startEvents[0] = PORT_RUNNING_0_PL_EVENT; // Ch0 running + startEvents[1] = PORT_STALLED_0_PL_EVENT; // Ch0 stalled + startEvents[2] = PORT_RUNNING_1_PL_EVENT; // Ch1 running + startEvents[3] = PORT_STALLED_1_PL_EVENT; // Ch1 stalled + } else { + // Default: ddr_bandwidth, read_bandwidth, write_bandwidth + // All 4 counters use PORT_RUNNING events for total throughput + startEvents[0] = PORT_RUNNING_0_PL_EVENT; + startEvents[1] = PORT_RUNNING_1_PL_EVENT; + startEvents[2] = PORT_RUNNING_2_PL_EVENT; + startEvents[3] = PORT_RUNNING_3_PL_EVENT; + } // Performance_Ctrl0: counters 0 and 1 // Bit layout: [31:24]=Cnt1_Stop, [23:16]=Cnt1_Start, [15:8]=Cnt0_Stop, [7:0]=Cnt0_Start @@ -809,10 +864,10 @@ std::vector AieDtraceCTWriter::generatePerfCounterConfig(uint8_ } std::vector AieDtraceCTWriter::generateBandwidthCounters( - const std::vector& shimColumns) + const std::vector& shimColumns, const std::string& metricSet) { std::vector counters; - auto configs = getBandwidthCounterConfigs(); + auto configs = getBandwidthCounterConfigs(metricSet); for (uint8_t column : shimColumns) { for (const auto& cfg : configs) { @@ -822,8 +877,9 @@ std::vector AieDtraceCTWriter::generateBandwidthCounters( info.counterNumber = cfg.counterNumber; info.module = "interface_tile"; info.address = calculateCounterAddress(column, SHIM_ROW, cfg.counterNumber, "interface_tile"); - info.metricSet = "ddr_bandwidth"; + info.metricSet = metricSet; info.portDirection = cfg.direction; + info.eventType = cfg.eventType; counters.push_back(info); } } @@ -898,6 +954,17 @@ bool AieDtraceCTWriter::writeBandwidthCTFile( else ctFile << "null"; + // Add event type for peak bandwidth metrics + if (!ctr.eventType.empty()) { + ctFile << ", \"event\": "; + if (ctr.eventType == "running") + ctFile << "\"r\""; + else if (ctr.eventType == "stalled") + ctFile << "\"s\""; + else + ctFile << "\"" << ctr.eventType << "\""; + } + ctFile << "}"; if (c < asmFileInfo.counters.size() - 1) ctFile << ","; @@ -964,7 +1031,8 @@ bool AieDtraceCTWriter::writeBandwidthCTFile( bool AieDtraceCTWriter::generateBandwidthCT( const std::string& outputPath, void* hwctx, - const std::vector& opLocations) + const std::vector& opLocations, + const std::string& metricSet) { if (opLocations.empty()) { xrt_core::message::send(severity_level::debug, "XRT", @@ -1025,7 +1093,7 @@ bool AieDtraceCTWriter::generateBandwidthCT( applyUcSpansFromOpLoc(asmFileInfoList); - auto allCounters = generateBandwidthCounters(shimColumns); + auto allCounters = generateBandwidthCounters(shimColumns, metricSet); if (allCounters.empty()) { xrt_core::message::send(severity_level::warning, "XRT", "AIE dtrace: No bandwidth counters generated"); @@ -1040,10 +1108,10 @@ bool AieDtraceCTWriter::generateBandwidthCT( std::vector beginBlockWrites; for (uint8_t column : shimColumns) { - auto ssWrites = generateStreamSwitchPortConfig(column); + auto ssWrites = generateStreamSwitchPortConfig(column, metricSet); beginBlockWrites.insert(beginBlockWrites.end(), ssWrites.begin(), ssWrites.end()); - auto pcWrites = generatePerfCounterConfig(column); + auto pcWrites = generatePerfCounterConfig(column, metricSet); beginBlockWrites.insert(beginBlockWrites.end(), pcWrites.begin(), pcWrites.end()); } diff --git a/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.h b/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.h index 76439b45..4aa473d7 100644 --- a/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.h +++ b/profile/plugin/aie_dtrace/ve2/aie_dtrace_ct_writer.h @@ -40,6 +40,7 @@ struct CTCounterInfo { uint64_t address; std::string metricSet; // Metric set name for this counter std::string portDirection; // "input"/"output" for throughput metrics (empty otherwise) + std::string eventType; // "running"/"stalled" for peak bandwidth metrics (empty otherwise) }; /** @@ -83,6 +84,7 @@ struct BandwidthCounterConfig { uint8_t dmaPortIndex; // Physical port index for stream switch (VE2-specific) bool isMaster; // true=S2MM/input (master), false=MM2S/output (slave) std::string direction; // "input" or "output" + std::string eventType; // "running" or "stalled" }; /** @@ -148,11 +150,13 @@ class AieDtraceCTWriter { * @param outputPath Full path for the generated CT file * @param hwctx Hardware context handle for partition info access * @param opLocations Vector of op_loc from aiebu_assembler::get_op_locations + * @param metricSet The metric set name (ddr_bandwidth, peak_read_bandwidth, etc.) * @return true if CT file was generated successfully, false otherwise */ bool generateBandwidthCT(const std::string& outputPath, void* hwctx, - const std::vector& opLocations); + const std::vector& opLocations, + const std::string& metricSet = "ddr_bandwidth"); private: /** @@ -239,31 +243,39 @@ class AieDtraceCTWriter { std::vector getShimTileColumns(void* hwctx); /** - * @brief Generate stream switch port configuration for 4 DMA channels per shim tile + * @brief Generate stream switch port configuration for DMA channels per shim tile * @param column Shim tile column + * @param metricSet The metric set name (ddr_bandwidth, peak_read_bandwidth, etc.) * @return Vector of register writes to configure stream switch ports */ - std::vector generateStreamSwitchPortConfig(uint8_t column); + std::vector generateStreamSwitchPortConfig(uint8_t column, + const std::string& metricSet = "ddr_bandwidth"); /** * @brief Generate performance counter configuration for 4 counters per shim tile * @param column Shim tile column + * @param metricSet The metric set name (ddr_bandwidth, peak_read_bandwidth, etc.) * @return Vector of register writes to configure performance counters */ - std::vector generatePerfCounterConfig(uint8_t column); + std::vector generatePerfCounterConfig(uint8_t column, + const std::string& metricSet = "ddr_bandwidth"); /** - * @brief Get fixed bandwidth counter configurations for a shim tile - * @return Vector of BandwidthCounterConfig for the 4 fixed counters + * @brief Get bandwidth counter configurations for a shim tile based on metric set + * @param metricSet The metric set name (ddr_bandwidth, peak_read_bandwidth, etc.) + * @return Vector of BandwidthCounterConfig for the 4 counters */ - std::vector getBandwidthCounterConfigs(); + std::vector getBandwidthCounterConfigs( + const std::string& metricSet = "ddr_bandwidth"); /** * @brief Generate bandwidth counters for all shim tiles in the partition * @param shimColumns Vector of shim tile columns + * @param metricSet The metric set name (ddr_bandwidth, peak_read_bandwidth, etc.) * @return Vector of CTCounterInfo for all bandwidth counters */ - std::vector generateBandwidthCounters(const std::vector& shimColumns); + std::vector generateBandwidthCounters(const std::vector& shimColumns, + const std::string& metricSet = "ddr_bandwidth"); /** * @brief Write the bandwidth CT file content with register configuration diff --git a/profile/plugin/aie_dtrace/ve2/aie_dtrace_ve2.cpp b/profile/plugin/aie_dtrace/ve2/aie_dtrace_ve2.cpp index b615dce5..67f284d6 100644 --- a/profile/plugin/aie_dtrace/ve2/aie_dtrace_ve2.cpp +++ b/profile/plugin/aie_dtrace/ve2/aie_dtrace_ve2.cpp @@ -244,15 +244,27 @@ namespace xdp { AieDtraceCTWriter ctWriter(db, metadata, deviceID, partitionStartCol); + // Get the metric set for interface tiles (module index 2 = shim) + std::string bandwidthMetricSet = "ddr_bandwidth"; + auto shimConfigMetrics = metadata->getConfigMetricsVec(2); + if (!shimConfigMetrics.empty()) { + bandwidthMetricSet = shimConfigMetrics.front().second; + xrt_core::message::send(severity_level::info, "XRT", + "AIE dtrace: Using metric set '" + bandwidthMetricSet + "' from configuration"); + } else { + xrt_core::message::send(severity_level::info, "XRT", + "AIE dtrace: No interface tile metrics configured, using default 'ddr_bandwidth'"); + } + bool generated = false; auto it = m_op_locations_cache.find(kernel_name); if (it != m_op_locations_cache.end() && !it->second.empty()) { - generated = ctWriter.generateBandwidthCT(outputPath, hwctx, it->second); + generated = ctWriter.generateBandwidthCT(outputPath, hwctx, it->second, bandwidthMetricSet); if (generated) { xrt_core::message::send(severity_level::debug, "XRT", "AIE dtrace: Bandwidth CT generated (self-contained) for kernel '" - + kernel_name + "'"); + + kernel_name + "' with metric set '" + bandwidthMetricSet + "'"); } } @@ -321,7 +333,9 @@ namespace xdp { uint8_t idToReport = (tile.subtype == io_type::GMIO) ? channel : streamPortId; uint8_t isChannel = (tile.subtype == io_type::GMIO) ? 1 : 0; uint8_t isMaster = aie::isInputSet(type, metricSet) ? 0 : 1; - if ((type == module_type::shim) && ((metricSet == "ddr_bandwidth") || (metricSet == "read_bandwidth") || (metricSet == "write_bandwidth"))) { + if ((type == module_type::shim) && ((metricSet == "ddr_bandwidth") || (metricSet == "read_bandwidth") || + (metricSet == "write_bandwidth") || (metricSet == "peak_read_bandwidth") || + (metricSet == "peak_write_bandwidth"))) { uint8_t idx = (portnum < tile.is_master_vec.size()) ? portnum : (logicalPortIndex < tile.is_master_vec.size()) ? logicalPortIndex : 0; isMaster = tile.is_master_vec.at(idx); @@ -494,7 +508,9 @@ namespace xdp { // Skip interface tiles with empty stream_ids for throughput metrics if ((type == module_type::shim) && - ((metricSet == "read_bandwidth") || (metricSet == "write_bandwidth") || (metricSet == "ddr_bandwidth")) && + ((metricSet == "read_bandwidth") || (metricSet == "write_bandwidth") || + (metricSet == "ddr_bandwidth") || (metricSet == "peak_read_bandwidth") || + (metricSet == "peak_write_bandwidth")) && tile.stream_ids.empty()) { std::stringstream msg; msg << "Skipping " << metricSet << " configuration for tile (" << +col << "," << +row @@ -522,7 +538,9 @@ namespace xdp { int numCounters = 0; auto numFreeCtr = stats.getNumRsc(loc, mod, xaiefal::XAIE_PERFCOUNT); - if (aie::isDebugVerbosity() && ((metricSet == "ddr_bandwidth") || (metricSet == "read_bandwidth") || (metricSet == "write_bandwidth"))) { + if (aie::isDebugVerbosity() && ((metricSet == "ddr_bandwidth") || (metricSet == "read_bandwidth") || + (metricSet == "write_bandwidth") || (metricSet == "peak_read_bandwidth") || + (metricSet == "peak_write_bandwidth"))) { std::stringstream msg; msg << metricSet << " **** counter reservation: tile (" << +col << "," << +row << ") startEvents.size()=" << startEvents.size() @@ -532,7 +550,9 @@ namespace xdp { } numFreeCtr = (startEvents.size() < numFreeCtr) ? startEvents.size() : numFreeCtr; - if ((type == module_type::shim) && ((metricSet == "ddr_bandwidth") || (metricSet == "read_bandwidth") || (metricSet == "write_bandwidth"))) { + if ((type == module_type::shim) && ((metricSet == "ddr_bandwidth") || (metricSet == "read_bandwidth") || + (metricSet == "write_bandwidth") || (metricSet == "peak_read_bandwidth") || + (metricSet == "peak_write_bandwidth"))) { numFreeCtr = tile.stream_ids.size(); } diff --git a/profile/plugin/aie_profile/aie_profile_metadata.cpp b/profile/plugin/aie_profile/aie_profile_metadata.cpp index 52eac0ff..b7148ed4 100644 --- a/profile/plugin/aie_profile/aie_profile_metadata.cpp +++ b/profile/plugin/aie_profile/aie_profile_metadata.cpp @@ -1286,7 +1286,8 @@ namespace xdp { auto metricVec = metricStrings.at(module_type::shim); if (m_dtraceBandwidthMode) { static const char* dtraceMetrics[] = - {"ddr_bandwidth", "read_bandwidth", "write_bandwidth"}; + {"ddr_bandwidth", "read_bandwidth", "write_bandwidth", + "peak_read_bandwidth", "peak_write_bandwidth"}; for (const char* m : dtraceMetrics) { if (std::find(metricVec.begin(), metricVec.end(), m) == metricVec.end()) metricVec.push_back(m);