+#if HAVE_SMPI
+class Adagio : public Governor {
+private:
+ int best_pstate = 0;
+ double start_time = 0;
+ double comp_counter = 0;
+ double comp_timer = 0;
+
+ std::vector<std::vector<double>> rates; // Each host + all frequencies of that host
+
+ unsigned int task_id = 0;
+ bool iteration_running = false; /*< Are we currently between iteration_in and iteration_out calls? */
+
+public:
+ explicit Adagio(simgrid::s4u::Host* ptr)
+ : Governor(ptr), rates(100, std::vector<double>(ptr->get_pstate_count(), 0.0))
+ {
+ simgrid::smpi::plugin::ampi::on_iteration_in.connect([this](simgrid::s4u::Actor const& actor) {
+ // Every instance of this class subscribes to this event, so one per host
+ // This means that for any actor, all 'hosts' are normally notified of these
+ // changes, even those who don't currently run the actor 'proc_id'.
+ // -> Let's check if this signal call is for us!
+ if (get_host() == actor.get_host()) {
+ iteration_running = true;
+ }
+ });
+ simgrid::smpi::plugin::ampi::on_iteration_out.connect([this](simgrid::s4u::Actor const& actor) {
+ if (get_host() == actor.get_host()) {
+ iteration_running = false;
+ task_id = 0;
+ }
+ });
+ simgrid::s4u::Exec::on_start.connect([this](simgrid::s4u::Actor const&, simgrid::s4u::Exec const& activity) {
+ if (activity.get_host() == get_host())
+ pre_task();
+ });
+ simgrid::s4u::Exec::on_completion.connect([this](simgrid::s4u::Actor const&, simgrid::s4u::Exec const& activity) {
+ // For more than one host (not yet supported), we can access the host via
+ // simcalls_.front()->issuer->iface()->get_host()
+ if (activity.get_host() == get_host() && iteration_running) {
+ comp_timer += activity.get_finish_time() - activity.get_start_time();
+ }
+ });
+ // FIXME I think that this fires at the same time for all hosts, so when the src sends something,
+ // the dst will be notified even though it didn't even arrive at the recv yet
+ simgrid::s4u::Link::on_communicate.connect(
+ [this](const kernel::resource::NetworkAction&, const s4u::Host* src, const s4u::Host* dst) {
+ if ((get_host() == src || get_host() == dst) && iteration_running) {
+ post_task();
+ }
+ });
+ }
+
+ std::string get_name() const override { return "Adagio"; }
+
+ void pre_task()
+ {
+ sg_host_load_reset(get_host());
+ comp_counter = sg_host_get_computed_flops(get_host()); // Should be 0 because of the reset
+ comp_timer = 0;
+ start_time = simgrid::s4u::Engine::get_clock();
+ if (rates.size() <= task_id)
+ rates.resize(task_id + 5, std::vector<double>(get_host()->get_pstate_count(), 0.0));
+ if (rates[task_id][best_pstate] == 0)
+ best_pstate = 0;
+ get_host()->set_pstate(best_pstate); // Load our schedule
+ XBT_DEBUG("Set pstate to %i", best_pstate);
+ }
+
+ void post_task()
+ {
+ double computed_flops = sg_host_get_computed_flops(get_host()) - comp_counter;
+ double target_time = (simgrid::s4u::Engine::get_clock() - start_time);
+ target_time = target_time * 99.0 / 100.0; // FIXME We account for t_copy arbitrarily with 1%
+ // -- this needs to be fixed
+
+ bool is_initialized = rates[task_id][best_pstate] != 0;
+ rates[task_id][best_pstate] = computed_flops / comp_timer;
+ if (not is_initialized) {
+ for (int i = 1; i < get_host()->get_pstate_count(); i++) {
+ rates[task_id][i] = rates[task_id][0] * (get_host()->get_pstate_speed(i) / get_host()->get_speed());
+ }
+ }
+
+ for (int pstate = get_host()->get_pstate_count() - 1; pstate >= 0; pstate--) {
+ if (computed_flops / rates[task_id][pstate] <= target_time) {
+ // We just found the pstate we want to use!
+ best_pstate = pstate;
+ break;
+ }
+ }
+ task_id++;
+ }
+
+ void update() override {}
+};
+#endif