Skip to content

Extend xrt::runner profile json with QoS and sleep #8907

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 25, 2025
Merged
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
31 changes: 27 additions & 4 deletions src/runtime_src/core/common/runner/profile.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,30 @@ defininng:

There are two sections of profile json:

1. [bindings](#bindings)
2. [execution](#execution)
1. [qos](#qos)
2. [bindings](#bindings)
3. [execution](#execution)

The `bindings` section defines how external resources are created,
initialized, and bound to a run-recipe.

The `execution` section controls how the run-recipe is executed and
how many times along with what should be done in each iteration.

## QoS

Simple key/value pairs designating configuration parameters for
hardware context creation.
```
"qos": {
"gops": 10,
"fps": 30
},
```
The json schema doesn't enforce key names or value ranges, XRT will
warn but ignore unrecoqnized keys. Improper values are implementation
defined.

## Bindings

The bindings section specifies how external buffers should be created,
Expand Down Expand Up @@ -68,7 +83,8 @@ The simple attributes are json key-value pairs:

- `bind` indicates if the buffer should be re-bound to the run
recipe in each iteration of the recipe (more about this in the
execution section).
execution section). All buffers are by default bound to the
recipe upon creation.
- `size` (optional with file initialization) specifies the size
of the `xrt::bo` created and bound to the recipe.

Expand Down Expand Up @@ -193,6 +209,7 @@ iteration and before next iteration.
"bind": false,
"init": true,
"wait": true,
"sleep": 1000,
"validate": true
}
}
Expand All @@ -202,11 +219,17 @@ The iteration element specifies what should happen before after each
iteration of the run recipe.

- `bind` indicates if buffers should be re-bound to the
recipe before an iteration.
recipe before an iteration. Only buffers whos binding element
specifies `bind` are re-bound. All buffers are by default
bound to the recipe upon creation.
- `init` indicates if buffer should be initialized per what is
specified in the binding element.
- `wait` says that execution should wait for completion between
iterations and after last iteration.
- `sleep` specifies how many milliseconds to sleep in between iterations
of the recipe. If both `wait` and `sleep` are specified, sleep will
be applied after wait completes.
iterations and after last iteration.
- `validate` means buffer validation per what is specified in
the binding element.

Expand Down
64 changes: 54 additions & 10 deletions src/runtime_src/core/common/runner/runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "core/common/json/nlohmann/json.hpp"

#include <algorithm>
#include <chrono>
#include <fstream>
#include <iostream>
#include <istream>
Expand All @@ -45,6 +46,7 @@
#include <random>
#include <string>
#include <string_view>
#include <thread>
#include <tuple>
#include <utility>
#include <variant>
Expand Down Expand Up @@ -585,9 +587,10 @@ class recipe

public:
resources(xrt::device device, const xrt::xclbin& xclbin,
const xrt::hw_context::qos_type& qos,
const json& recipe, const artifacts::repo* repo)
: m_device{std::move(device)}
, m_hwctx{m_device, m_device.register_xclbin(xclbin)}
, m_hwctx{m_device, m_device.register_xclbin(xclbin), qos}
, m_buffers{create_buffers(m_device, recipe.at("buffers"))}
, m_kernels{create_kernels(m_device, m_hwctx, recipe.at("kernels"), repo)}
, m_cpus{create_cpus(recipe.value("cpus", empty_json))} // optional
Expand Down Expand Up @@ -1131,8 +1134,6 @@ class recipe
void
execute(size_t iteration)
{
XRT_DEBUGF("recipe::execution::execute(%d)\n", iteration);

// If single runlist then avoid the overhead of xrt::queue
if (m_runlists.size() == 1) {
m_runlists[0]->execute(iteration);
Expand All @@ -1156,8 +1157,6 @@ class recipe
void
wait()
{
XRT_DEBUGF("recipe::execution::wait()\n");

// If single runlist then it was submitted explicitly, so
// wait explicitly
if (m_runlists.size() == 1) {
Expand All @@ -1174,6 +1173,12 @@ class recipe
if (m_eptr)
std::rethrow_exception(m_eptr);
}

void
sleep(uint32_t sleep_ms) const
{
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms));
}
}; // class recipe::execution

xrt::device m_device;
Expand All @@ -1184,16 +1189,20 @@ class recipe
execution m_execution;

public:
recipe(xrt::device device, json recipe, const artifacts::repo* repo)
recipe(xrt::device device, json recipe, const xrt::hw_context::qos_type& qos, const artifacts::repo* repo)
: m_device{std::move(device)}
, m_recipe_json(std::move(recipe)) // paren required, else initialized as array
, m_header{m_recipe_json.at("header"), repo}
, m_resources{m_device, m_header.get_xclbin(), m_recipe_json.at("resources"), repo}
, m_resources{m_device, m_header.get_xclbin(), qos, m_recipe_json.at("resources"), repo}
, m_execution{m_resources, m_recipe_json.at("execution")}
{}

recipe(xrt::device device, json recipe, const artifacts::repo* repo)
: recipe::recipe(std::move(device), std::move(recipe), {}, repo)
{}

recipe(xrt::device device, const std::string& rr, const artifacts::repo* repo)
: recipe(std::move(device), load_json(rr), repo)
: recipe::recipe(std::move(device), load_json(rr), {}, repo)
{}

recipe(const recipe&) = default;
Expand Down Expand Up @@ -1242,6 +1251,13 @@ class recipe
XRT_DEBUGF("recipe::wait()\n");
m_execution.wait();
}

void
sleep(uint32_t sleep_ms) const
{
XRT_DEBUGF("recipe::sleep(%d)\n", sleep_ms);
m_execution.sleep(sleep_ms);
}
}; // class recipe

// class profile - Execution profile
Expand Down Expand Up @@ -1521,7 +1537,8 @@ class profile
// - "init" indicates if buffer should be initialized per what is
// specified in the binding element.
// - "wait" says that execution should wait for completion between
// iterations and after last iteration.
// iterations and and sleep for specified milliseconds before
// next iteration.
// - "validate" means buffer validation per what is specified in
// the binding element.
class execution
Expand All @@ -1548,6 +1565,9 @@ class profile
if (m_iteration.value("wait", false))
m_profile->wait_recipe();

if (auto sleep_ms = m_iteration.value("sleep", 0))
m_profile->sleep_recipe(sleep_ms);

// Validate if requested (implies wait)
if (m_iteration.value("validate", false))
m_profile->validate();
Expand Down Expand Up @@ -1593,6 +1613,7 @@ class profile
json m_profile_json;
std::shared_ptr<artifacts::repo> m_repo;

xrt::hw_context::qos_type m_qos;
recipe m_recipe;
bindings m_bindings;
execution m_execution;
Expand Down Expand Up @@ -1633,6 +1654,28 @@ class profile
m_recipe.wait();
}

void
sleep_recipe(uint32_t time_ms)
{
m_recipe.sleep(time_ms);
}

private:
static xrt::hw_context::qos_type
init_qos(const json& j)
{
if (j.empty())
return {};

xrt::hw_context::qos_type qos;
for (auto [key, value] : j.items()) {
XRT_DEBUGF("qos[%s] = %d\n", key.c_str(), value.get<uint32_t>());
qos.emplace(std::move(key), value.get<uint32_t>());
}

return qos;
}

public:
// profile - constructor
//
Expand All @@ -1645,7 +1688,8 @@ class profile
std::shared_ptr<artifacts::repo> repo)
: m_profile_json{load_json(profile)}
, m_repo{std::move(repo)}
, m_recipe{device, load_json(recipe), m_repo.get()}
, m_qos{init_qos(m_profile_json.value("qos", json::object()))}
, m_recipe{device, load_json(recipe), m_qos, m_repo.get()}
, m_bindings{device, m_profile_json.at("bindings"), m_repo.get()}
, m_execution(this, m_profile_json.at("execution"))
{}
Expand Down
Loading