Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions profile/database/static_info/aie_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,11 @@ namespace xdp::aie {
boost::property_tree::ptree pt;
pt.put("start_col", e.start_col);
pt.put("num_cols", e.num_cols);
// The query is device-wide, so it also reports partitions belonging to
// other contexts and other processes. These two identify the owner:
// "id" is the driver's context id, which matches hwctx_handle::get_slotidx().
pt.put("id", e.metadata.id);
pt.put("pid", e.pid);
infoPt.push_back(std::make_pair("", pt));
}
}
Expand Down
20 changes: 17 additions & 3 deletions profile/plugin/aie_dtrace/aie_dtrace_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,23 @@ namespace xdp {
virtual void endPoll() {}
virtual void freeResources() {}

virtual void generateCTForRun(void* /*run_impl_ptr*/, void* /*hwctx*/, uint32_t /*run_uid*/,
const std::string& /*kernel_name*/,
void* /*elf_handle*/) {}
// Called once per xrt::run construction, where the ELF is available:
// generates every CT file the configured inference sequence needs so that
// run start only has to pick one. When a single metric set covers every
// inference there is nothing to pick, so the CT is also programmed here.
virtual void generateCTsForRun(void* /*run_impl_ptr*/, void* /*hwctx*/, uint32_t /*run_uid*/,
const std::string& /*kernel_name*/,
void* /*elf_handle*/) {}

// Called on every xrt::run start: advances this kernel's inference counter
// and hands the matching pre-generated CT to XRT. Does nothing unless a
// multi-inference sequence is configured.
virtual void applyCTForRun(void* /*run_impl_ptr*/, void* /*hwctx*/, uint32_t /*run_uid*/,
const std::string& /*kernel_name*/) {}

// Called at hardware context teardown, to report a configured inference
// sequence that the application did not run far enough to consume.
virtual void reportUnusedSelections() {}

uint64_t getDeviceID() { return deviceID; }
};
Expand Down
279 changes: 266 additions & 13 deletions profile/plugin/aie_dtrace/aie_dtrace_metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,31 @@ namespace xdp {

const bool usingBlob = profiling_runtime_config::has_control_instrumentation();
const auto& ci = profiling_runtime_config::control_instrumentation();
const auto& runs = ci.profile_runs;

const bool useProfileRuns = usingBlob && ci.has_explicit_profile_runs && !runs.empty();
multiInference = useProfileRuns;

// configMetrics describes the hardware context as a whole: it is what
// isConfigured() gates on and what createAIEProfileConfig() reports. A
// profile_runs sequence has no single answer for it, so resolve it from the
// first inference (falling back to any top-level key the sequence omits)
// and let the per-inference differences live in metricSelections instead.
const auto& effAieTile = (useProfileRuns && runs[0].aie_tile.has_value())
? runs[0].aie_tile : ci.aie_tile;
const auto& effInterfaceTile = (useProfileRuns && runs[0].interface_tile.has_value())
? runs[0].interface_tile : ci.interface_tile;
const auto& effMemTile = (useProfileRuns && runs[0].mem_tile.has_value())
? runs[0].mem_tile : ci.mem_tile;

// Core (aie) tile metrics (e.g. compute_io_bound). Only used to enable the
// metric; the tiles themselves are fixed to the first column.
std::vector<std::string> aieMetricsSettings;
if (usingBlob && ci.aie_tile.has_value() && !ci.aie_tile->empty()) {
if (usingBlob && effAieTile.has_value() && !effAieTile->empty()) {
xrt_core::message::send(severity_level::info, "XRT",
"AIE dtrace: using aie_tile metric '" + *ci.aie_tile
"AIE dtrace: using aie_tile metric '" + *effAieTile
+ "' from Debug.profiling_runtime_config.");
aieMetricsSettings = getSettingsVector("all:" + *ci.aie_tile);
aieMetricsSettings = getSettingsVector("all:" + *effAieTile);
}
else {
const std::string tileBasedAie =
Expand All @@ -100,11 +116,11 @@ namespace xdp {
getConfigMetricsForAIETiles(CORE_MODULE_IDX, aieMetricsSettings);

std::vector<std::string> metricsSettings;
if (usingBlob && ci.interface_tile.has_value() && !ci.interface_tile->empty()) {
if (usingBlob && effInterfaceTile.has_value() && !effInterfaceTile->empty()) {
xrt_core::message::send(severity_level::info, "XRT",
"AIE dtrace: using interface_tile metric '" + *ci.interface_tile
"AIE dtrace: using interface_tile metric '" + *effInterfaceTile
+ "' from Debug.profiling_runtime_config.");
metricsSettings = getSettingsVector("all:" + *ci.interface_tile);
metricsSettings = getSettingsVector("all:" + *effInterfaceTile);
}
else {
const std::string tileBased =
Expand All @@ -128,11 +144,30 @@ namespace xdp {
const std::string iniPorts =
xrt_core::config::get_aie_dtrace_settings_memory_tile_input_ports();
const bool iniPortsSet = !iniPorts.empty();
// The design points (which memory tile ports exist) are a property of the
// design, so they stay global even when the metric sets vary per inference;
// only whether to instrument them in a given inference is per-entry.
const bool anyRunWantsL2L2 = useProfileRuns
&& std::any_of(runs.begin(), runs.end(), [](const auto& r) {
return r.mem_tile.has_value() && *r.mem_tile == INPUT_PORTS_METRIC_SET;
});

if (useProfileRuns
&& std::any_of(runs.begin(), runs.end(), [](const auto& r) {
return r.memory_tile_input_ports.has_value() && !r.memory_tile_input_ports->empty();
})) {
xrt_core::message::send(severity_level::warning, "XRT",
"AIE dtrace: memory_tile_input_ports inside a profile_runs entry is not supported; "
"design points are taken from control_instrumentation.memory_tile_input_ports "
"(or AIE_dtrace_settings.memory_tile_input_ports) for every inference.");
}

const bool blobPortsSet = usingBlob && ci.memory_tile_input_ports.has_value()
&& !ci.memory_tile_input_ports->empty();
const bool memTileFieldFromBlob = usingBlob && ci.mem_tile.has_value()
&& !ci.mem_tile->empty();
const bool memTileUsesBlob = usingBlob && (memTileFieldFromBlob || blobPortsSet);
const bool memTileFieldFromBlob = usingBlob && effMemTile.has_value()
&& !effMemTile->empty();
const bool memTileUsesBlob = usingBlob
&& (memTileFieldFromBlob || blobPortsSet || anyRunWantsL2L2);

// Mem tile settings that are not L2-L2 select a per-tile counter metric set such
// as output_channels_details. Both families program the same mem tile performance
Expand All @@ -142,17 +177,21 @@ namespace xdp {

bool l2L2FromBlob = false;
if (memTileUsesBlob) {
if (memTileFieldFromBlob && *ci.mem_tile == INPUT_PORTS_METRIC_SET) {
const bool blobEnablesL2L2 = anyRunWantsL2L2
|| (memTileFieldFromBlob && *effMemTile == INPUT_PORTS_METRIC_SET);

if (blobEnablesL2L2) {
l2L2TransferEnabled = true;
l2L2FromBlob = true;
xrt_core::message::send(severity_level::info, "XRT",
"AIE dtrace: enabling L2-L2 via mem_tile metric '" + *ci.mem_tile
"AIE dtrace: enabling L2-L2 via mem_tile metric '"
+ std::string(INPUT_PORTS_METRIC_SET)
+ "' from Debug.profiling_runtime_config.");
} else if (memTileFieldFromBlob) {
xrt_core::message::send(severity_level::info, "XRT",
"AIE dtrace: using mem_tile metric '" + *ci.mem_tile
"AIE dtrace: using mem_tile metric '" + *effMemTile
+ "' from Debug.profiling_runtime_config.");
memTileMetricsSettings = getSettingsVector("all:" + *ci.mem_tile);
memTileMetricsSettings = getSettingsVector("all:" + *effMemTile);
}
}
else {
Expand Down Expand Up @@ -216,9 +255,223 @@ namespace xdp {
}
}

// Built last so it sees the final l2L2TransferEnabled, which the design
// point validation above can still turn back off.
if (useProfileRuns) {
metricSelections.reserve(runs.size());
for (size_t i = 0; i < runs.size(); ++i)
metricSelections.push_back(buildSelectionFromProfileRun(runs[i], i));

std::stringstream msg;
msg << "AIE dtrace: profiling the first " << metricSelections.size()
<< " inferences of each kernel:";
for (size_t i = 0; i < metricSelections.size(); ++i)
msg << "\n inference " << i << ": "
<< metricSelections[i].describe();
xrt_core::message::send(severity_level::info, "XRT", msg.str());
}
else {
metricSelections.push_back(buildSelectionFromConfigMetrics());
}

xrt_core::message::send(severity_level::info, "XRT", "Finished parsing AIE dtrace metadata.");
}

std::string MetricSelection::describe() const
{
std::stringstream msg;
const char* sep = "";

if (includeBandwidth) {
msg << "interface_tile=" << bandwidthMetricSet
<< ":" << static_cast<int>(bandwidthChannel);
sep = ", ";
}
if (!coreMetricSet.empty()) {
msg << sep << "aie_tile=" << coreMetricSet;
sep = ", ";
}
if (includeL2L2) {
msg << sep << "mem_tile=" << INPUT_PORTS_METRIC_SET;
sep = ", ";
}
if (!memTileMetricSet.empty())
msg << sep << "mem_tile=" << memTileMetricSet
<< ":" << static_cast<int>(memTileChannel);

const std::string out = msg.str();
return out.empty() ? std::string("no metrics") : out;
}

// Reduce the whole-context config maps to the one selection that every
// inference shares. This is the single-configuration form: the CT is the same
// no matter how many times the kernel runs.
MetricSelection AieDtraceMetadata::buildSelectionFromConfigMetrics()
{
MetricSelection selection;

for (const auto& tc : getConfigMetricsVec(CORE_MODULE_IDX)) {
selection.coreMetricSet = tc.second;
break;
}

// Interface-tile bandwidth metrics are configured by default unless the user
// turned interface tiles off, which leaves the shim config map empty.
const auto shimConfigMetrics = getConfigMetricsVec(SHIM_MODULE_IDX);
selection.includeBandwidth = !shimConfigMetrics.empty();

if (selection.includeBandwidth) {
selection.bandwidthMetricSet = shimConfigMetrics.front().second;
// The detailed_ddr_*_bandwidth metric sets carry a DMA channel in their
// ":<channel>" suffix, which getConfigMetricsForInterfaceTiles stored in
// configChannel0 keyed by tile.
const auto& shimTile = shimConfigMetrics.front().first;
for (const auto& tc : configChannel0) {
if ((tc.first.col == shimTile.col) && (tc.first.row == shimTile.row)) {
selection.bandwidthChannel = tc.second;
break;
}
}
}

selection.includeL2L2 = l2L2TransferEnabled;

// Per-tile mem tile counters (output_channels_details / mm2s_channels_details).
// L2-L2 and these share the mem tile performance counters, and the constructor
// already drops the per-tile set when L2-L2 is enabled for this configuration.
const auto memTileConfigMetrics = getConfigMetricsVec(MEM_TILE_MODULE_IDX);
if (!memTileConfigMetrics.empty()) {
selection.memTileMetricSet = memTileConfigMetrics.front().second;
const auto& memTile = memTileConfigMetrics.front().first;
for (const auto& tc : configChannel0) {
if ((tc.first.col == memTile.col) && (tc.first.row == memTile.row)) {
selection.memTileChannel = tc.second;
break;
}
}
// An empty list means every partition column. A named-column setting
// copies those columns; "all" leaves the list empty.
if (!memTileAllColumns) {
for (const auto& tc : memTileConfigMetrics)
selection.memTileColumns.push_back(tc.first.col);
}
}

return selection;
}

// Resolve one profile_runs entry. Unlike the single-configuration form these
// are not routed through getConfigMetricsFor*Tiles: the CT writer derives its
// own tiles, so all that is needed here is the metric set names and the DMA
// channel carried in the interface tile's ":<channel>" suffix.
MetricSelection
AieDtraceMetadata::buildSelectionFromProfileRun(
const profiling_runtime_config::profile_run_t& run, size_t index) const
{
const std::string scope = "profile_runs[" + std::to_string(index) + "]";
MetricSelection selection;

if (run.interface_tile.has_value() && !run.interface_tile->empty()) {
std::vector<std::string> parts;
boost::split(parts, *run.interface_tile, boost::is_any_of(":"));
const std::string& metricSet = parts.front();

if (!isBandwidthMetricSet(metricSet)) {
xrt_core::message::send(severity_level::warning, "XRT",
"AIE dtrace: " + scope + ".interface_tile='" + *run.interface_tile
+ "' is not a known interface tile metric set; no bandwidth counters "
"will be programmed for that inference.");
}
else if (metricSet != "off") {
selection.includeBandwidth = true;
selection.bandwidthMetricSet = metricSet;

if (parts.size() > 1) {
try {
selection.bandwidthChannel = aie::convertStringToUint8(parts[1]);
}
catch (const std::invalid_argument&) {
xrt_core::message::send(severity_level::warning, "XRT",
"AIE dtrace: channel '" + parts[1] + "' in " + scope
+ ".interface_tile is not an integer; using channel 0.");
}
}
}
}

if (run.aie_tile.has_value() && !run.aie_tile->empty()) {
// Accept "all:<metric>" as well as a bare "<metric>", matching what
// getConfigMetricsForAIETiles allows for the single-configuration form.
std::vector<std::string> parts;
boost::split(parts, *run.aie_tile, boost::is_any_of(":"));
const std::string& metricSet = parts.back();

if (!isCoreMetricSet(metricSet)) {
xrt_core::message::send(severity_level::warning, "XRT",
"AIE dtrace: " + scope + ".aie_tile='" + *run.aie_tile
+ "' is not a known core (aie) tile metric set. Supported: compute_io_bound, off.");
}
else if (metricSet != "off") {
selection.coreMetricSet = metricSet;
}
}

if (run.mem_tile.has_value() && !run.mem_tile->empty()) {
if (*run.mem_tile == INPUT_PORTS_METRIC_SET) {
selection.includeL2L2 = l2L2TransferEnabled;
}
else {
std::vector<std::string> parts;
boost::split(parts, *run.mem_tile, boost::is_any_of(":"));
auto metricPos = std::find_if(parts.begin(), parts.end(),
[this](const std::string& part) { return isMemTileMetricSet(part); });

if (metricPos == parts.end()) {
xrt_core::message::send(severity_level::warning, "XRT",
"AIE dtrace: " + scope + ".mem_tile='" + *run.mem_tile
+ "' is not a known mem tile metric set. Supported: input_ports, "
"output_channels_details, mm2s_channels_details, off.");
}
else if (*metricPos != "off") {
selection.memTileMetricSet = *metricPos;
// A column ahead of the metric names one column. "all" and a bare
// metric leave memTileColumns empty, which means every partition column.
if ((metricPos != parts.begin()) && (parts.front().compare("all") != 0)) {
try {
selection.memTileColumns.push_back(aie::convertStringToUint8(parts.front()));
}
catch (const std::invalid_argument&) {
xrt_core::message::send(severity_level::warning, "XRT",
"AIE dtrace: column '" + parts.front() + "' in " + scope
+ ".mem_tile is not an integer; using every partition column.");
}
}
auto channelPos = std::next(metricPos);
if (channelPos != parts.end()) {
try {
const uint8_t requested = aie::convertStringToUint8(*channelPos);
if (requested < NUM_MEM_TILE_DMA_CHANNELS)
selection.memTileChannel = requested;
else
xrt_core::message::send(severity_level::warning, "XRT",
"AIE dtrace: mem tile MM2S channel " + std::to_string(requested)
+ " in " + scope + ".mem_tile is out of range (0-"
+ std::to_string(NUM_MEM_TILE_DMA_CHANNELS - 1)
+ "). Using channel 0.");
}
catch (const std::invalid_argument&) {
xrt_core::message::send(severity_level::warning, "XRT",
"AIE dtrace: channel '" + *channelPos + "' in " + scope
+ ".mem_tile is not an integer; using channel 0.");
}
}
}
}
}

return selection;
}

void AieDtraceMetadata::checkDtraceSettings()
{
using boost::property_tree::ptree;
Expand Down
Loading
Loading