From 1f7bfe14269fd1b82c1f7cb7189d8d836d51b5e0 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Tue, 13 Jun 2023 18:46:55 +0200 Subject: [PATCH] Benchmarks to evaluate cost of resource yield requests and resource allocation notifications. --- repos/mml/run/bestow_resources.run | 94 +++++++ repos/mml/run/resource_yield.run | 94 +++++++ repos/mml/src/app/grant_bench/main.cc | 351 ++++++++++++++++++++++++ repos/mml/src/app/grant_bench/target.mk | 3 + repos/mml/src/app/yield_bench/main.cc | 346 +++++++++++++++++++++++ repos/mml/src/app/yield_bench/target.mk | 3 + 6 files changed, 891 insertions(+) create mode 100644 repos/mml/run/bestow_resources.run create mode 100644 repos/mml/run/resource_yield.run create mode 100644 repos/mml/src/app/grant_bench/main.cc create mode 100644 repos/mml/src/app/grant_bench/target.mk create mode 100644 repos/mml/src/app/yield_bench/main.cc create mode 100644 repos/mml/src/app/yield_bench/target.mk diff --git a/repos/mml/run/bestow_resources.run b/repos/mml/run/bestow_resources.run new file mode 100644 index 0000000000..3d0e0e20a4 --- /dev/null +++ b/repos/mml/run/bestow_resources.run @@ -0,0 +1,94 @@ +set build_components { + core init hoitaja timer app/grant_bench +} + +source ${genode_dir}/repos/base/run/platform_drv.inc +append_platform_drv_build_components + +build $build_components + +create_boot_directory + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + + install_config $config + + set boot_modules { + core init hoitaja timer vfs.lib.so ld.lib.so benchmark_resource_award +} + +append_platform_drv_boot_modules + +build_boot_image $boot_modules +append qemu_args "-nographic " + +run_genode_until forever \ No newline at end of file diff --git a/repos/mml/run/resource_yield.run b/repos/mml/run/resource_yield.run new file mode 100644 index 0000000000..b959ef9ada --- /dev/null +++ b/repos/mml/run/resource_yield.run @@ -0,0 +1,94 @@ +set build_components { + core init hoitaja timer app/yield_bench +} + +source ${genode_dir}/repos/base/run/platform_drv.inc +append_platform_drv_build_components + +build $build_components + +create_boot_directory + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + + install_config $config + + set boot_modules { + core init hoitaja timer vfs.lib.so ld.lib.so benchmark_resource_yield +} + +append_platform_drv_boot_modules + +build_boot_image $boot_modules +append qemu_args "-nographic " + +run_genode_until forever \ No newline at end of file diff --git a/repos/mml/src/app/grant_bench/main.cc b/repos/mml/src/app/grant_bench/main.cc new file mode 100644 index 0000000000..0b67b6ff82 --- /dev/null +++ b/repos/mml/src/app/grant_bench/main.cc @@ -0,0 +1,351 @@ +/* + * \brief Test for yielding resources + * \author Norman Feske + * \date 2013-10-05 + * + * This test exercises the protocol between a parent and child, which is used + * by the parent to regain resources from a child subsystem. + * + * The program acts in either one of two roles, the parent or the child. The + * role is determined by reading a config argument. + * + * The child periodically allocates chunks of RAM until its RAM quota is + * depleted. Once it observes a yield request from the parent, however, it + * cooperatively releases as much resources as requested by the parent. + * + * The parent wait a while to give the child the chance to allocate RAM. It + * then sends a yield request and waits for a response. When getting the + * response, it validates whether the child complied to the request or not. + */ + +/* + * Copyright (C) 2013-2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Test { + class Child; + class Parent; + using namespace Genode; +} + + +/**************** + ** Child role ** + ****************/ + +/** + * The child eats more and more RAM. However, when receiving a yield request, + * it releases the requested amount of resources. + */ +class Test::Child +{ + private: + + struct Ram_chunk : List::Element + { + Env &env; + + size_t const size; + + Ram_dataspace_capability ds_cap; + + Ram_chunk(Env &env, size_t size) + : + env(env),size(size), ds_cap(env.ram().alloc(size)) + { } + + ~Ram_chunk() { env.ram().free(ds_cap); } + }; + + Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + bool const _expand; + List _ram_chunks { }; + Timer::Connection _timer { _env }; + Signal_handler _grant_handler; + Genode::uint64_t const _period_ms; + + void _handle_grant(); + + + + public: + + Child(Env &, Xml_node); + void main(); +}; + + +void Test::Child::_handle_grant() +{ + /* request yield request arguments */ + unsigned long start = Genode::Trace::timestamp(); + [[maybe_unused]] Genode::Parent::Resource_args const args = _env.parent().gained_resources(); + unsigned long end = Genode::Trace::timestamp(); + // Genode::Parent::Resource_args const args = _env.parent().yield_request(); + + _env.parent().yield_response(); + log("{\"grant-handle-et\": ", (end - start)/2000, "}"); + // size_t const gained_ram_quota = Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); + + // log("Gained RAM quota: ", gained_ram_quota); + /* + log("yield request: ", args.string()); + + size_t const requested_ram_quota = + Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); + + log("got request to free ", requested_ram_quota, " MB of RAM"); + + size_t const requested_cpu_quota = + Arg_string::find_arg(args.string(), "cpu_quota").ulong_value(0); + + log("released ", requested_cpu_quota, " portions of cpu_quota"); + + size_t const requested_gpu_quota = + Arg_string::find_arg(args.string(), "gpus").ulong_value(0); + + log("got request to release ", requested_gpu_quota, " gpus");*/ +} + + +Test::Child::Child(Env &env, Xml_node config) +: + _env(env), + _expand(config.attribute_value("expand", false)), + _grant_handler(_env.ep(), *this, &Child::_handle_grant), + _period_ms(config.attribute_value("period_ms", (Genode::uint64_t)500)) +{ + /* register yield signal handler */ + _env.parent().resource_avail_sigh(_grant_handler); +} + + +/***************** + ** Parent role ** + *****************/ + +/** + * The parent grants resource requests as long as it has free resources. + * Once in a while, it politely requests the child to yield resources. + */ +class Test::Parent +{ + private: + + Env &_env; + + Timer::Connection _timer { _env }; + + void _print_status() + { + log("quota: ", _child.pd().ram_quota().value / 1024, " KiB " + "used: ", _child.pd().used_ram().value / 1024, " KiB"); + } + + size_t _used_ram_prior_yield = 0; + + /* perform the test three times */ + unsigned _cnt = 5000; + + unsigned long _start = 0; + + unsigned long _end = 0; + unsigned long _sent = 0; + + enum State { WAIT, YIELD_REQUESTED, YIELD_GOT_RESPONSE }; + State _state = WAIT; + + void _schedule_one_second_timeout() + { + //log("wait ", _wait_cnt, "/", _wait_secs); + _timer.trigger_once(10000); + } + + void _init() + { + _state = WAIT; + _schedule_one_second_timeout(); + } + + void _bestow_resources() + { + /* remember quantum of resources used by the child */ + //_used_ram_prior_yield = _child.pd().used_ram().value; + + //log("request yield (ram prior yield: ", _used_ram_prior_yield); + + /* issue yield request */ + Genode::Parent::Resource_args award("ram_quota=5M,cpu_quota=10,gpus=1"); + + _start = Genode::Trace::timestamp(); + _child.accept(award); + _sent = Genode::Trace::timestamp(); + + _state = YIELD_REQUESTED; + } + + void _handle_timeout() + { + //_print_status(); + _bestow_resources(); + _schedule_one_second_timeout(); + } + + void _yield_response() + { + _end = Genode::Trace::timestamp(); + log("{\"bestow-rtt\": ", (_end-_start)/2000, ", \"bestow-transmit\": ", (_sent-_start)/2000, ",\"bestow-acked\":", (_end-_sent)/2000,"}"); + + _state = YIELD_GOT_RESPONSE; + + //_print_status(); + + if (_cnt-- > 0) { + _init(); + } else { + log("--- test-resource_yield finished ---"); + _env.parent().exit(0); + } + } + + Signal_handler _timeout_handler { + _env.ep(), *this, &Parent::_handle_timeout }; + + struct Policy : public Genode::Child_policy + { + Env &_env; + + Parent &_parent; + + Static_parent_services + _parent_services { _env }; + + Cap_quota const _cap_quota { 50 }; + Ram_quota const _ram_quota { 10*1024*1024 }; + Binary_name const _binary_name { "benchmark_resource_award" }; + + /* + * Config ROM service + */ + + struct Config_producer : Dynamic_rom_session::Content_producer + { + void produce_content(char *dst, Genode::size_t dst_len) override + { + Xml_generator xml(dst, dst_len, "config", [&] () { + xml.attribute("child", "yes"); }); + } + } _config_producer { }; + + Dynamic_rom_session _config_session { _env.ep().rpc_ep(), + ref_pd(), _env.rm(), + _config_producer }; + + typedef Genode::Local_service Config_service; + + Config_service::Single_session_factory _config_factory { _config_session }; + Config_service _config_service { _config_factory }; + + void yield_response() override + { + _parent._yield_response(); + } + + Policy(Parent &parent, Env &env) : _env(env), _parent(parent) { } + + Name name() const override { return "child"; } + + Binary_name binary_name() const override { return _binary_name; } + + Pd_session &ref_pd() override { return _env.pd(); } + Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); } + + void init(Pd_session &pd, Pd_session_capability pd_cap) override + { + pd.ref_account(ref_pd_cap()); + ref_pd().transfer_quota(pd_cap, _cap_quota); + ref_pd().transfer_quota(pd_cap, _ram_quota); + } + + Route resolve_session_request(Service::Name const &service_name, + Session_label const &label, + Session::Diag const diag) override + { + auto route = [&] (Service &service) { + return Route { .service = service, + .label = label, + .diag = diag }; }; + + if (service_name == "ROM" && label == "child -> config") + return route(_config_service); + + Service *service_ptr = nullptr; + _parent_services.for_each([&] (Service &s) { + if (!service_ptr && service_name == s.name()) + service_ptr = &s; }); + + if (!service_ptr) + throw Service_denied(); + + return route(*service_ptr); + } + }; + + Policy _policy { *this, _env }; + + Genode::Child _child { _env.rm(), _env.ep().rpc_ep(), _policy }; + + public: + + class Insufficient_yield { }; + + /** + * Constructor + */ + Parent(Env &env) : _env(env) + { + _timer.sigh(_timeout_handler); + _init(); + } +}; + + +/*************** + ** Component ** + ***************/ + +void Component::construct(Genode::Env &env) +{ + using namespace Genode; + + /* + * Read value '' attribute to decide whether to perform + * the child or the parent role. + */ + static Attached_rom_dataspace config(env, "config"); + bool const is_child = config.xml().attribute_value("child", false); + + if (is_child) { + log("--- test-resource_yield child role started ---"); + static Test::Child child(env, config.xml()); + } else { + log("--- test-resource_yield parent role started ---"); + static Test::Parent parent(env); + } +} diff --git a/repos/mml/src/app/grant_bench/target.mk b/repos/mml/src/app/grant_bench/target.mk new file mode 100644 index 0000000000..5c5b4a3e48 --- /dev/null +++ b/repos/mml/src/app/grant_bench/target.mk @@ -0,0 +1,3 @@ +TARGET = benchmark_resource_award +SRC_CC = main.cc +LIBS = base diff --git a/repos/mml/src/app/yield_bench/main.cc b/repos/mml/src/app/yield_bench/main.cc new file mode 100644 index 0000000000..0d3ce13c99 --- /dev/null +++ b/repos/mml/src/app/yield_bench/main.cc @@ -0,0 +1,346 @@ +/* + * \brief Test for yielding resources + * \author Norman Feske + * \date 2013-10-05 + * + * This test exercises the protocol between a parent and child, which is used + * by the parent to regain resources from a child subsystem. + * + * The program acts in either one of two roles, the parent or the child. The + * role is determined by reading a config argument. + * + * The child periodically allocates chunks of RAM until its RAM quota is + * depleted. Once it observes a yield request from the parent, however, it + * cooperatively releases as much resources as requested by the parent. + * + * The parent wait a while to give the child the chance to allocate RAM. It + * then sends a yield request and waits for a response. When getting the + * response, it validates whether the child complied to the request or not. + */ + +/* + * Copyright (C) 2013-2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Test { + class Child; + class Parent; + using namespace Genode; +} + + +/**************** + ** Child role ** + ****************/ + +/** + * The child eats more and more RAM. However, when receiving a yield request, + * it releases the requested amount of resources. + */ +class Test::Child +{ + private: + + struct Ram_chunk : List::Element + { + Env &env; + + size_t const size; + + Ram_dataspace_capability ds_cap; + + Ram_chunk(Env &env, size_t size) + : + env(env),size(size), ds_cap(env.ram().alloc(size)) + { } + + ~Ram_chunk() { env.ram().free(ds_cap); } + }; + + Env &_env; + Heap _heap { _env.ram(), _env.rm() }; + bool const _expand; + List _ram_chunks { }; + Timer::Connection _timer { _env }; + Signal_handler _yield_handler; + uint64_t const _period_ms; + + void _handle_yield(); + + + + public: + + Child(Env &, Xml_node); + void main(); +}; + + +void Test::Child::_handle_yield() +{ + /* request yield request arguments */ + //Genode::Parent::Resource_args const args = _env.parent().yield_request(); + _env.parent().yield_response(); +/* + log("yield request: ", args.string()); + + size_t const requested_ram_quota = + Arg_string::find_arg(args.string(), "ram_quota").ulong_value(0); + + log("got request to free ", requested_ram_quota, " MB of RAM"); + + size_t const requested_cpu_quota = + Arg_string::find_arg(args.string(), "cpu_quota").ulong_value(0); + + log("released ", requested_cpu_quota, " portions of cpu_quota"); + + size_t const requested_gpu_quota = + Arg_string::find_arg(args.string(), "gpus").ulong_value(0); + + log("got request to release ", requested_gpu_quota, " gpus");*/ + + + +} + + +Test::Child::Child(Env &env, Xml_node config) +: + _env(env), + _expand(config.attribute_value("expand", false)), + _yield_handler(_env.ep(), *this, &Child::_handle_yield), + _period_ms(config.attribute_value("period_ms", (uint64_t)500)) +{ + /* register yield signal handler */ + _env.parent().yield_sigh(_yield_handler); +} + + +/***************** + ** Parent role ** + *****************/ + +/** + * The parent grants resource requests as long as it has free resources. + * Once in a while, it politely requests the child to yield resources. + */ +class Test::Parent +{ + private: + + Env &_env; + + Timer::Connection _timer { _env }; + + void _print_status() + { + log("quota: ", _child.pd().ram_quota().value / 1024, " KiB " + "used: ", _child.pd().used_ram().value / 1024, " KiB"); + } + + size_t _used_ram_prior_yield = 0; + + /* perform the test three times */ + unsigned _cnt = 5000; + + unsigned long _start = 0; + + unsigned long _end = 0; + unsigned long _sent = 0; + + enum State { WAIT, YIELD_REQUESTED, YIELD_GOT_RESPONSE }; + State _state = WAIT; + + void _schedule_one_second_timeout() + { + //log("wait ", _wait_cnt, "/", _wait_secs); + _timer.trigger_once(10000); + } + + void _init() + { + _state = WAIT; + _schedule_one_second_timeout(); + } + + void _request_yield() + { + /* remember quantum of resources used by the child */ + _used_ram_prior_yield = _child.pd().used_ram().value; + + //log("request yield (ram prior yield: ", _used_ram_prior_yield); + + /* issue yield request */ + Genode::Parent::Resource_args yield_args("ram_quota=5M,cpu_quota=10,gpus=1"); + + _start = Genode::Trace::timestamp(); + _child.yield(yield_args); + _sent = Genode::Trace::timestamp(); + + _state = YIELD_REQUESTED; + } + + void _handle_timeout() + { + //_print_status(); + _request_yield(); + _schedule_one_second_timeout(); + } + + void _yield_response() + { + _end = Genode::Trace::timestamp(); + log("{\"yield-rtt\": ", (_end-_start)/2000, ", \"yield-request\": ", (_sent-_start)/2000, ",\"yield-response\":", (_end-_sent)/2000,"}"); + + _state = YIELD_GOT_RESPONSE; + + //_print_status(); + + if (_cnt-- > 0) { + _init(); + } else { + log("--- test-resource_yield finished ---"); + _env.parent().exit(0); + } + } + + Signal_handler _timeout_handler { + _env.ep(), *this, &Parent::_handle_timeout }; + + struct Policy : public Genode::Child_policy + { + Env &_env; + + Parent &_parent; + + Static_parent_services + _parent_services { _env }; + + Cap_quota const _cap_quota { 50 }; + Ram_quota const _ram_quota { 10*1024*1024 }; + Binary_name const _binary_name { "benchmark_resource_yield" }; + + /* + * Config ROM service + */ + + struct Config_producer : Dynamic_rom_session::Content_producer + { + void produce_content(char *dst, Genode::size_t dst_len) override + { + Xml_generator xml(dst, dst_len, "config", [&] () { + xml.attribute("child", "yes"); }); + } + } _config_producer { }; + + Dynamic_rom_session _config_session { _env.ep().rpc_ep(), + ref_pd(), _env.rm(), + _config_producer }; + + typedef Genode::Local_service Config_service; + + Config_service::Single_session_factory _config_factory { _config_session }; + Config_service _config_service { _config_factory }; + + void yield_response() override + { + _parent._yield_response(); + } + + Policy(Parent &parent, Env &env) : _env(env), _parent(parent) { } + + Name name() const override { return "child"; } + + Binary_name binary_name() const override { return _binary_name; } + + Pd_session &ref_pd() override { return _env.pd(); } + Pd_session_capability ref_pd_cap() const override { return _env.pd_session_cap(); } + + void init(Pd_session &pd, Pd_session_capability pd_cap) override + { + pd.ref_account(ref_pd_cap()); + ref_pd().transfer_quota(pd_cap, _cap_quota); + ref_pd().transfer_quota(pd_cap, _ram_quota); + } + + Route resolve_session_request(Service::Name const &service_name, + Session_label const &label, + Session::Diag const diag) override + { + auto route = [&] (Service &service) { + return Route { .service = service, + .label = label, + .diag = diag }; }; + + if (service_name == "ROM" && label == "child -> config") + return route(_config_service); + + Service *service_ptr = nullptr; + _parent_services.for_each([&] (Service &s) { + if (!service_ptr && service_name == s.name()) + service_ptr = &s; }); + + if (!service_ptr) + throw Service_denied(); + + return route(*service_ptr); + } + }; + + Policy _policy { *this, _env }; + + Genode::Child _child { _env.rm(), _env.ep().rpc_ep(), _policy }; + + public: + + class Insufficient_yield { }; + + /** + * Constructor + */ + Parent(Env &env) : _env(env) + { + _timer.sigh(_timeout_handler); + _init(); + } +}; + + +/*************** + ** Component ** + ***************/ + +void Component::construct(Genode::Env &env) +{ + using namespace Genode; + + /* + * Read value '' attribute to decide whether to perform + * the child or the parent role. + */ + static Attached_rom_dataspace config(env, "config"); + bool const is_child = config.xml().attribute_value("child", false); + + if (is_child) { + log("--- test-resource_yield child role started ---"); + static Test::Child child(env, config.xml()); + } else { + log("--- test-resource_yield parent role started ---"); + static Test::Parent parent(env); + } +} diff --git a/repos/mml/src/app/yield_bench/target.mk b/repos/mml/src/app/yield_bench/target.mk new file mode 100644 index 0000000000..74d1d88926 --- /dev/null +++ b/repos/mml/src/app/yield_bench/target.mk @@ -0,0 +1,3 @@ +TARGET = benchmark_resource_yield +SRC_CC = main.cc +LIBS = base