diff --git a/meson.build b/meson.build index fbdfd5612bca7c73114362510192bbd2fdb9ee9e..b1d3247c851c56e17362d77848ac171f434f9f9e 100644 --- a/meson.build +++ b/meson.build @@ -26,54 +26,14 @@ batsched_deps = [ # Source files src = [ - 'src/algo/conservative_bf.cpp', - 'src/algo/conservative_bf.hpp', - 'src/algo/crasher.cpp', - 'src/algo/crasher.hpp', 'src/algo/easy_bf.cpp', - 'src/algo/easy_bf_fast.cpp', - 'src/algo/easy_bf_fast.hpp', 'src/algo/easy_bf.hpp', - 'src/algo/easy_bf_plot_liquid_load_horizon.cpp', - 'src/algo/easy_bf_plot_liquid_load_horizon.hpp', - 'src/algo/energy_bf.cpp', - 'src/algo/energy_bf_dicho.cpp', - 'src/algo/energy_bf_dicho.hpp', - 'src/algo/energy_bf.hpp', - 'src/algo/energy_bf_idle_sleeper.cpp', - 'src/algo/energy_bf_idle_sleeper.hpp', - 'src/algo/energy_bf_machine_subpart_sleeper.cpp', - 'src/algo/energy_bf_machine_subpart_sleeper.hpp', - 'src/algo/energy_bf_monitoring_inertial_shutdown.cpp', - 'src/algo/energy_bf_monitoring_inertial_shutdown.hpp', - 'src/algo/energy_bf_monitoring_period.cpp', - 'src/algo/energy_bf_monitoring_period.hpp', - 'src/algo/energy_watcher.cpp', - 'src/algo/energy_watcher.hpp', - 'src/algo/fcfs_fast.cpp', - 'src/algo/fcfs_fast.hpp', 'src/algo/fcfs.cpp', 'src/algo/fcfs.hpp', - 'src/algo/filler.cpp', - 'src/algo/filler.hpp', - 'src/algo/killer2.cpp', - 'src/algo/killer2.hpp', - 'src/algo/killer.cpp', - 'src/algo/killer.hpp', - 'src/algo/random.cpp', - 'src/algo/random.hpp', 'src/algo/rejecter.cpp', 'src/algo/rejecter.hpp', 'src/algo/sequencer.cpp', 'src/algo/sequencer.hpp', - 'src/algo/sequencer_dvfs.cpp', - 'src/algo/sequencer_dvfs.hpp', - 'src/algo/sleeper.cpp', - 'src/algo/sleeper.hpp', - 'src/algo/submitter.cpp', - 'src/algo/submitter.hpp', - 'src/algo/wt_estimator.cpp', - 'src/algo/wt_estimator.hpp', 'src/decision.cpp', 'src/decision.hpp', 'src/exact_numbers.hpp', diff --git a/src/algo/conservative_bf.cpp b/src/algo/conservative_bf.cpp deleted file mode 100644 index fd165e7b8444c3f178d0ec9c0580502732c76738..0000000000000000000000000000000000000000 --- a/src/algo/conservative_bf.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include "conservative_bf.hpp" - -#include <loguru.hpp> - -#include "../pempek_assert.hpp" - -using namespace std; - -ConservativeBackfilling::ConservativeBackfilling(Workload *workload, SchedulingDecision *decision, - Queue *queue, ResourceSelector * selector, double rjms_delay, rapidjson::Document *variant_options) : - ISchedulingAlgorithm(workload, decision, queue, selector, rjms_delay, variant_options) -{ - if (variant_options->HasMember("dump_previsional_schedules")) - { - PPK_ASSERT_ERROR((*variant_options)["dump_previsional_schedules"].IsBool(), - "Invalid options: 'dump_previsional_schedules' should be a boolean"); - _dump_provisional_schedules = (*variant_options)["dump_previsional_schedules"].GetBool(); - } - - if (variant_options->HasMember("dump_prefix")) - { - PPK_ASSERT_ERROR((*variant_options)["dump_prefix"].IsString(), - "Invalid options: 'dump_prefix' should be a string"); - _dump_prefix = (*variant_options)["dump_prefix"].GetString(); - } -} - -ConservativeBackfilling::~ConservativeBackfilling() -{ -} - -void ConservativeBackfilling::on_simulation_start(double date, const rapidjson::Value & batsim_config) -{ - _schedule = Schedule(_nb_machines, date); - (void) batsim_config; -} - -void ConservativeBackfilling::on_simulation_end(double date) -{ - (void) date; -} - -void ConservativeBackfilling::make_decisions(double date, - SortableJobOrder::UpdateInformation *update_info, - SortableJobOrder::CompareInformation *compare_info) -{ - // Let's remove finished jobs from the schedule - for (const string & ended_job_id : _jobs_ended_recently) - _schedule.remove_job((*_workload)[ended_job_id]); - - // Let's handle recently released jobs - std::vector<std::string> recently_queued_jobs; - for (const string & new_job_id : _jobs_released_recently) - { - const Job * new_job = (*_workload)[new_job_id]; - - if (new_job->nb_requested_resources > _nb_machines) - { - _decision->add_reject_job(new_job_id, date); - } - else if (!new_job->has_walltime) - { - LOG_SCOPE_FUNCTION(INFO); - LOG_F(INFO, "Date=%g. Rejecting job '%s' as it has no walltime", date, new_job_id.c_str()); - _decision->add_reject_job(new_job_id, date); - } - else - { - _queue->append_job(new_job, update_info); - recently_queued_jobs.push_back(new_job_id); - } - } - - // Let's update the schedule's present - _schedule.update_first_slice(date); - - // Queue sorting - _queue->sort_queue(update_info, compare_info); - - // If no resources have been released, we can just insert the new jobs into the schedule - if (_jobs_ended_recently.empty()) - { - for (const string & new_job_id : recently_queued_jobs) - { - const Job * new_job = (*_workload)[new_job_id]; - Schedule::JobAlloc alloc = _schedule.add_job_first_fit(new_job, _selector); - - // If the job should start now, let's say it to the resource manager - if (alloc.started_in_first_slice) - { - _decision->add_execute_job(new_job->id, alloc.used_machines, date); - _queue->remove_job(new_job); - } - } - } - else - { - // Since some resources have been freed, - // Let's compress the schedule following conservative backfilling rules: - // For each non running job j - // Remove j from the schedule - // Add j into the schedule - // If j should be executed now - // Take the decision to run j now - for (auto job_it = _queue->begin(); job_it != _queue->end(); ) - { - const Job * job = (*job_it)->job; - - _schedule.remove_job_if_exists(job); -// if (_dump_provisional_schedules) -// _schedule.incremental_dump_as_batsim_jobs_file(_dump_prefix); - Schedule::JobAlloc alloc = _schedule.add_job_first_fit(job, _selector); -// if (_dump_provisional_schedules) -// _schedule.incremental_dump_as_batsim_jobs_file(_dump_prefix); - - if (alloc.started_in_first_slice) - { - _decision->add_execute_job(job->id, alloc.used_machines, date); - job_it = _queue->remove_job(job_it); - } - else - ++job_it; - } - } - - // And now let's see if we can estimate some waiting times - - for (const std::string & job_id : _jobs_whose_waiting_time_estimation_has_been_requested_recently) - { - const Job * new_job = (*_workload)[job_id]; - double answer = _schedule.query_wait(new_job->nb_requested_resources, new_job->walltime, _selector); - _decision->add_answer_estimate_waiting_time(job_id, answer, date); - } - - if (_dump_provisional_schedules) - _schedule.incremental_dump_as_batsim_jobs_file(_dump_prefix); -} diff --git a/src/algo/conservative_bf.hpp b/src/algo/conservative_bf.hpp deleted file mode 100644 index 717a991479edb20b21ef1e992093d4e609820beb..0000000000000000000000000000000000000000 --- a/src/algo/conservative_bf.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include <list> - -#include "../isalgorithm.hpp" -#include "../json_workload.hpp" -#include "../locality.hpp" -#include "../schedule.hpp" - -class ConservativeBackfilling : public ISchedulingAlgorithm -{ -public: - ConservativeBackfilling(Workload * workload, SchedulingDecision * decision, Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options); - virtual ~ConservativeBackfilling(); - - virtual void on_simulation_start(double date, const rapidjson::Value & batsim_config); - - virtual void on_simulation_end(double date); - - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); - -private: - Schedule _schedule; - bool _dump_provisional_schedules = false; - std::string _dump_prefix = "/tmp/dump"; -}; diff --git a/src/algo/crasher.cpp b/src/algo/crasher.cpp deleted file mode 100644 index 1339973e0d11e878e0cc0bfdad87d825998845b4..0000000000000000000000000000000000000000 --- a/src/algo/crasher.cpp +++ /dev/null @@ -1,147 +0,0 @@ -#include "crasher.hpp" - -#include "../pempek_assert.hpp" - -// Mouhahahaha -#include <stdlib.h> -#include <signal.h> - -#include <stdexcept> - -#include <loguru.hpp> - -Crasher::CrashType Crasher::crash_type_from_string(const std::string &str) -{ - if (str == "segmentation_fault") return CrashType::SEGMENTATION_FAULT; - else if (str == "infinite_loop") return CrashType::INFINITE_LOOP; - else if (str == "terminate_processus_success") return CrashType::TERMINATE_PROCESSUS_SUCCESS; - else if (str == "terminate_processus_failure") return CrashType::TERMINATE_PROCESSUS_FAILURE; - else if (str == "abort") return CrashType::ABORT; - else if (str == "suspend_process") return CrashType::SUSPEND_PROCESS; - else - { - PPK_ASSERT_ERROR(false, "Invalid crash type string: %s", str.c_str()); - return CrashType::SEGMENTATION_FAULT; - } -} - -std::string Crasher::crash_type_to_string(Crasher::CrashType type) -{ - switch(type) - { - case CrashType::SEGMENTATION_FAULT: return "segmentation_fault"; - case CrashType::INFINITE_LOOP: return "infinite_loop"; - case CrashType::TERMINATE_PROCESSUS_SUCCESS: return "terminate_processus_success"; - case CrashType::TERMINATE_PROCESSUS_FAILURE: return "terminate_processus_failure"; - case CrashType::ABORT: return "abort"; - case CrashType::SUSPEND_PROCESS: return "suspend_process"; - } - - throw std::invalid_argument("Unknown crash type"); -} - -Crasher::Crasher(Workload *workload, - SchedulingDecision *decision, - Queue *queue, - ResourceSelector *selector, - double rjms_delay, - rapidjson::Document *variant_options) : - Sequencer(workload, decision, queue, selector, rjms_delay, variant_options) -{ - if (variant_options->HasMember("crash_on_start")) - { - PPK_ASSERT_ERROR((*variant_options)["crash_on_start"].IsBool(), - "Invalid options: 'crash_on_start' should be a boolean"); - _crash_on_start = (*variant_options)["crash_on_start"].GetBool(); - } - - if (variant_options->HasMember("crash_on_end")) - { - PPK_ASSERT_ERROR((*variant_options)["crash_on_end"].IsBool(), - "Invalid options: 'crash_on_end' should be a boolean"); - _crash_on_end = (*variant_options)["crash_on_end"].GetBool(); - } - - if (variant_options->HasMember("crash_on_decision_call")) - { - PPK_ASSERT_ERROR((*variant_options)["crash_on_decision_call"].IsBool(), - "Invalid options: 'crash_on_decision_call' should be a boolean"); - _crash_on_decision_call = (*variant_options)["crash_on_decision_call"].GetBool(); - } - - if (variant_options->HasMember("crash_on_decision_call_number")) - { - PPK_ASSERT_ERROR((*variant_options)["crash_on_decision_call_number"].IsNumber(), - "Invalid options: 'crash_on_decision_call_number' should be a number"); - _crash_on_decision_call_number = (*variant_options)["crash_on_decision_call_number"].GetInt(); - PPK_ASSERT_ERROR(_crash_on_decision_call_number >= 0, - "Invalid options: 'crash_on_decision_call_number' should be non-negative " - "but got value=%d", _crash_on_decision_call_number); - } - - if (variant_options->HasMember("crash_type")) - { - PPK_ASSERT_ERROR((*variant_options)["crash_type"].IsString(), - "Invalid options: 'crash_type' should be a string"); - std::string crash_type_str = (*variant_options)["crash_type"].GetString(); - _crash_type = crash_type_from_string(crash_type_str); - } - - LOG_SCOPE_FUNCTION(INFO); - LOG_F(INFO, "crash_on_start: %d", _crash_on_start); - LOG_F(INFO, "crash_on_end: %d", _crash_on_end); - LOG_F(INFO, "crash_on_decision_call: %d", _crash_on_decision_call); - LOG_F(INFO, "crash_on_decision_call_number: %d", _crash_on_decision_call_number); - LOG_F(INFO, "crash_type: %s", crash_type_to_string(_crash_type).c_str()); -} - -Crasher::~Crasher() -{ - -} - -void Crasher::on_simulation_start(double date, const rapidjson::Value &batsim_config) -{ - if (_crash_on_start) - crash(_crash_type); - - Sequencer::on_simulation_start(date, batsim_config); -} - -void Crasher::on_simulation_end(double date) -{ - if (_crash_on_end) - crash(_crash_type); - - Sequencer::on_simulation_end(date); -} - -void Crasher::make_decisions(double date, - SortableJobOrder::UpdateInformation *update_info, - SortableJobOrder::CompareInformation *compare_info) -{ - ++_decision_call_number; - if (_crash_on_decision_call && _decision_call_number >= _crash_on_decision_call_number) - crash(_crash_type); - - Sequencer::make_decisions(date, update_info, compare_info); -} - -void Crasher::crash(Crasher::CrashType type) -{ - switch(type) - { - case CrashType::SEGMENTATION_FAULT: - raise(SIGSEGV); - case CrashType::INFINITE_LOOP: - { for (;;); } - case CrashType::TERMINATE_PROCESSUS_SUCCESS: - exit(EXIT_SUCCESS); - case CrashType::TERMINATE_PROCESSUS_FAILURE: - exit(EXIT_FAILURE); - case CrashType::ABORT: - abort(); - case CrashType::SUSPEND_PROCESS: - raise(SIGTSTP); - } -} diff --git a/src/algo/crasher.hpp b/src/algo/crasher.hpp deleted file mode 100644 index e954433d995e5d53a8d60e45b6151a7870e0cf51..0000000000000000000000000000000000000000 --- a/src/algo/crasher.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include "sequencer.hpp" - -#include "../locality.hpp" -#include <intervalset.hpp> - -class Workload; -class SchedulingDecision; - -/** - * @brief The amazing and gorgeous Crasher scheduler - * @details This scheduler is meant to crash! - * It allows various types of crash, - * and allows to define when the crash should occur. - */ - -class Crasher : public Sequencer -{ -public: - enum class CrashType - { - SEGMENTATION_FAULT, - INFINITE_LOOP, - TERMINATE_PROCESSUS_SUCCESS, - TERMINATE_PROCESSUS_FAILURE, - ABORT, - SUSPEND_PROCESS, - }; - - CrashType crash_type_from_string(const std::string & str); - std::string crash_type_to_string(CrashType type); - -public: - Crasher(Workload * workload, SchedulingDecision * decision, Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options); - - virtual ~Crasher(); - - void on_simulation_start(double date, const rapidjson::Value & batsim_config); - - void on_simulation_end(double date); - - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); - -private: - static void crash(CrashType type); - -private: - bool _crash_on_start = false; - bool _crash_on_end = true; - - bool _crash_on_decision_call = false; - int _crash_on_decision_call_number = 0; - int _decision_call_number = -1; - CrashType _crash_type = CrashType::SEGMENTATION_FAULT; -}; diff --git a/src/algo/easy_bf_fast.cpp b/src/algo/easy_bf_fast.cpp deleted file mode 100644 index c74d25a4e99a0245ed4fd2c9d51d45aaf4316f92..0000000000000000000000000000000000000000 --- a/src/algo/easy_bf_fast.cpp +++ /dev/null @@ -1,292 +0,0 @@ -#include "easy_bf_fast.hpp" - -//#include <loguru.hpp> - -#include "../pempek_assert.hpp" - -EasyBackfillingFast::EasyBackfillingFast(Workload *workload, - SchedulingDecision *decision, Queue *queue, ResourceSelector *selector, - double rjms_delay, rapidjson::Document *variant_options) : - ISchedulingAlgorithm(workload, decision, queue, selector, rjms_delay, - variant_options) -{} - -EasyBackfillingFast::~EasyBackfillingFast() -{} - -void EasyBackfillingFast::on_simulation_start(double date, - const rapidjson::Value &batsim_config) -{ - (void) date; - (void) batsim_config; - - _available_machines.insert(IntervalSet::ClosedInterval(0, _nb_machines - 1)); - _nb_available_machines = _nb_machines; - PPK_ASSERT_ERROR(_available_machines.size() == (unsigned int) _nb_machines); -} - -void EasyBackfillingFast::on_simulation_end(double date) -{ - (void) date; -} - -void EasyBackfillingFast::make_decisions(double date, - SortableJobOrder::UpdateInformation *update_info, - SortableJobOrder::CompareInformation *compare_info) -{ - (void) update_info; - (void) compare_info; - - // This algorithm is a fast version of EASY backfilling. - // It is meant to be fast in the usual case, not to handle corner cases - // (use the other easy backfilling available in batsched for this purpose). - // It is not meant to be easily readable or hackable ;). - - // This fast EASY backfilling variant in a few words: - // - only handles the FCFS queue order - // - only handles the basic resource selection policy - // - only handles finite jobs (no switchoff), with walltimes - // - only handles one priority job (the first of the queue) - // - only handles time as floating-point (-> precision errors). - - // Warning: you might obtain different outputs than with easy_bf. This is - // due to the fact that this version only keeps track of the priority job - // expected start time and the number of machines available then, while - // easy_bf keeps track of a full 2D schedule of the future. easy_bf_fast - // will sometimes be a little more greedy in backfilling. - - bool job_ended = false; - - // Handle newly finished jobs - for (const std::string & ended_job_id : _jobs_ended_recently) - { - job_ended = true; - - Job * finished_job = (*_workload)[ended_job_id]; - const Allocation & alloc = _current_allocations[ended_job_id]; - - // Update data structures - _available_machines.insert(alloc.machines); - _nb_available_machines += finished_job->nb_requested_resources; - _horizons.erase(alloc.horizon_it); - _current_allocations.erase(ended_job_id); - } - - // If jobs have finished, let's execute jobs as long as they are priority - if (job_ended) - { - if (_priority_job != nullptr) - { - Allocation alloc; - FinishedHorizonPoint point; - - if (_priority_job->nb_requested_resources <= _nb_available_machines) - { - //LOG_F(INFO, "Priority job fits!"); - alloc.machines = _available_machines.left( - _priority_job->nb_requested_resources); - _decision->add_execute_job(_priority_job->id, alloc.machines, - date); - - point.nb_released_machines = _priority_job->nb_requested_resources; - point.date = date + (double)_priority_job->walltime; - alloc.horizon_it = insert_horizon_point(point); - - // Update data structures - _available_machines -= alloc.machines; - _nb_available_machines -= _priority_job->nb_requested_resources; - _current_allocations[_priority_job->id] = alloc; - _priority_job = nullptr; - - // Execute the whole queue until a priority job cannot fit - for (auto job_it = _pending_jobs.begin(); - job_it != _pending_jobs.end(); ) - { - Job * pending_job = *job_it; - if (pending_job->nb_requested_resources <= _nb_available_machines) - { - alloc.machines = _available_machines.left( - pending_job->nb_requested_resources); - _decision->add_execute_job(pending_job->id, - alloc.machines, date); - - point.nb_released_machines = pending_job->nb_requested_resources; - point.date = date + (double)pending_job->walltime; - alloc.horizon_it = insert_horizon_point(point); - - // Update data structures - _available_machines -= alloc.machines; - _nb_available_machines -= pending_job->nb_requested_resources; - _current_allocations[pending_job->id] = alloc; - job_it = _pending_jobs.erase(job_it); - } - else - { - // The job becomes priority! - _priority_job = pending_job; - update_priority_job_expected_earliest_start_time(); - _pending_jobs.erase(job_it); - - // Stop first queue traversal. - break; - } - } - } - - // Backfill jobs that does not hinder priority job. - if (_nb_available_machines > 0) - { - // Update priority job expected starting time (might have changed if a recently ended job - // completed before its walltime) - if (_priority_job != nullptr) - update_priority_job_expected_earliest_start_time(); - - for (auto job_it = _pending_jobs.begin(); - job_it != _pending_jobs.end(); ) - { - const Job * pending_job = *job_it; - // Can the job be executed now (without hindering priority job)? - if (pending_job->nb_requested_resources <= _nb_available_machines && - (date + pending_job->walltime <= _priority_job_expected_start_time || - pending_job->nb_requested_resources <= _remaining_resources_at_priority_job_start)) - { - // Yes, it can be backfilled! - alloc.machines = _available_machines.left( - pending_job->nb_requested_resources); - _decision->add_execute_job(pending_job->id, - alloc.machines, date); - - point.nb_released_machines = pending_job->nb_requested_resources; - point.date = date + (double)pending_job->walltime; - alloc.horizon_it = insert_horizon_point(point); - - // Update data structures - _available_machines -= alloc.machines; - _nb_available_machines -= pending_job->nb_requested_resources; - _current_allocations[pending_job->id] = alloc; - job_it = _pending_jobs.erase(job_it); - if(date + pending_job->walltime > _priority_job_expected_start_time) - _remaining_resources_at_priority_job_start -= pending_job->nb_requested_resources; - - // Directly get out of the backfilling loop if all machines are busy. - if (_nb_available_machines <= 0) - break; - } - else - { - ++job_it; - } - } - } - } - } - - // Handle newly released jobs - for (const std::string & new_job_id : _jobs_released_recently) - { - Job * new_job = (*_workload)[new_job_id]; - - - // Is the job valid on this platform? - if (new_job->nb_requested_resources > _nb_machines) - { - _decision->add_reject_job(new_job_id, date); - } - else if (!new_job->has_walltime) - { - _decision->add_reject_job(new_job_id, date); - } - - // Can the job be executed right now? - else if (new_job->nb_requested_resources <= _nb_available_machines) - { - //LOG_F(INFO, "There are enough available resources (%d) to execute job %s", _nb_available_machines, new_job->id.c_str()); - // Can it be executed now (without hindering priority job)? - if (_priority_job == nullptr || - date + new_job->walltime <= _priority_job_expected_start_time || - new_job->nb_requested_resources <= _remaining_resources_at_priority_job_start) - { - //LOG_F(INFO, "Job %s can be started right away!", new_job->id.c_str()); - // Yes, the job can be executed right away! - Allocation alloc; - - alloc.machines = _available_machines.left( - new_job->nb_requested_resources); - _decision->add_execute_job(new_job_id, alloc.machines, date); - - FinishedHorizonPoint point; - point.nb_released_machines = new_job->nb_requested_resources; - point.date = date + (double)new_job->walltime; - alloc.horizon_it = insert_horizon_point(point); - - // Update data structures - _available_machines -= alloc.machines; - _nb_available_machines -= new_job->nb_requested_resources; - _current_allocations[new_job_id] = alloc; - if(_priority_job != nullptr && date + new_job->walltime > _priority_job_expected_start_time) - _remaining_resources_at_priority_job_start -= new_job->nb_requested_resources; - } - else - { - // No, the job cannot be executed (hinders priority job.) - /*LOG_F(INFO, "Not enough time to execute job %s (walltime=%g, priority job expected starting time=%g)", - new_job->id.c_str(), (double)new_job->walltime, _priority_job_expected_start_time);*/ - _pending_jobs.push_back(new_job); - } - } - else - { - // The job is too big to fit now. - - if (_priority_job == nullptr) - { - // The job becomes priority. - _priority_job = new_job; - update_priority_job_expected_earliest_start_time(); - } - else - { - // The job is queued up. - _pending_jobs.push_back(new_job); - } - } - } -} - -void EasyBackfillingFast::update_priority_job_expected_earliest_start_time() -{ - int nb_available = _nb_available_machines; - int required = _priority_job->nb_requested_resources; - - for (auto it = _horizons.begin(); it != _horizons.end(); ++it) - { - nb_available += it->nb_released_machines; - - if (nb_available >= required) - { - _priority_job_expected_start_time = it->date; - _remaining_resources_at_priority_job_start = nb_available - required; - return; - } - } - - PPK_ASSERT_ERROR(false, "The job will never be executable."); - return; -} - -std::list<EasyBackfillingFast::FinishedHorizonPoint>::iterator EasyBackfillingFast::insert_horizon_point(const EasyBackfillingFast::FinishedHorizonPoint &point) -{ - // The data structure is sorted, we can therefore traverse it in order - // until finding an insertion point. - for (auto it = _horizons.begin(); it != _horizons.end(); ++it) - { - if (point.date < it->date) - { - // Insertion point is before the current iterator. - return _horizons.insert(it, point); - } - } - - // Insertion point not found. Insertion at end. - return _horizons.insert(_horizons.end(), point); -} diff --git a/src/algo/easy_bf_fast.hpp b/src/algo/easy_bf_fast.hpp deleted file mode 100644 index 5a449bce0fa3499005309d8d9dc5b90600aa198a..0000000000000000000000000000000000000000 --- a/src/algo/easy_bf_fast.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include <unordered_map> -#include <list> - -#include "../isalgorithm.hpp" -#include "../json_workload.hpp" -#include "../locality.hpp" - -class EasyBackfillingFast : public ISchedulingAlgorithm -{ -public: - EasyBackfillingFast(Workload * workload, SchedulingDecision * decision, - Queue * queue, ResourceSelector * selector, - double rjms_delay, - rapidjson::Document * variant_options); - virtual ~EasyBackfillingFast(); - - virtual void on_simulation_start(double date, - const rapidjson::Value & batsim_config); - - virtual void on_simulation_end(double date); - - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); - -private: - struct FinishedHorizonPoint - { - double date; - int nb_released_machines; - }; - - struct Allocation - { - IntervalSet machines; - std::list<FinishedHorizonPoint>::iterator horizon_it; - }; - -private: - void update_priority_job_expected_earliest_start_time(); - std::list<FinishedHorizonPoint>::iterator insert_horizon_point(const FinishedHorizonPoint & point); - -private: - // Machines currently available - IntervalSet _available_machines; - int _nb_available_machines = -1; - - // Pending jobs (queue; without the priority job) - std::list<Job *> _pending_jobs; - - // Allocations of running jobs - std::unordered_map<std::string, Allocation> _current_allocations; - - // When running jobs are expected to finish. - // Always sorted by increasing date. - std::list<FinishedHorizonPoint> _horizons; - - // At any time, null if there is no priority job (no waiting job) - Job * _priority_job = nullptr; - double _priority_job_expected_start_time = -1; - int _remaining_resources_at_priority_job_start = -1; -}; diff --git a/src/algo/easy_bf_plot_liquid_load_horizon.cpp b/src/algo/easy_bf_plot_liquid_load_horizon.cpp deleted file mode 100644 index 338f8ffedc5e14ce97a183120a4bb113cdc8b16f..0000000000000000000000000000000000000000 --- a/src/algo/easy_bf_plot_liquid_load_horizon.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "easy_bf_plot_liquid_load_horizon.hpp" - -#include "../pempek_assert.hpp" - -using namespace std; - -EasyBackfillingPlotLiquidLoadHorizon::EasyBackfillingPlotLiquidLoadHorizon(Workload * workload, - SchedulingDecision * decision, - Queue * queue, - ResourceSelector * selector, - double rjms_delay, - rapidjson::Document * variant_options) : - EasyBackfilling(workload, decision, queue, selector, rjms_delay, variant_options) -{ - - PPK_ASSERT_ERROR(variant_options->HasMember("trace_output_filename"), - "Invalid options JSON object: Member 'trace_output_filename' cannot be found"); - PPK_ASSERT_ERROR((*variant_options)["trace_output_filename"].IsString(), - "Invalid options JSON object: Member 'trace_output_filename' must be a string"); - string trace_output_filename = (*variant_options)["trace_output_filename"].GetString(); - - _output_file.open(trace_output_filename); - PPK_ASSERT_ERROR(_output_file.is_open(), "Couldn't open file %s", trace_output_filename.c_str()); - - string buf = "date,nb_jobs_in_queue,load_in_queue,liquid_load_horizon\n"; - //string buf = "date,nb_jobs_in_queue,load_in_queue,liquid_load_horizon,qt_mean_wt\n"; - _output_file.write(buf.c_str(), buf.size()); -} - -EasyBackfillingPlotLiquidLoadHorizon::~EasyBackfillingPlotLiquidLoadHorizon() -{ - _output_file.close(); -} - -void EasyBackfillingPlotLiquidLoadHorizon::make_decisions(double date, - SortableJobOrder::UpdateInformation *update_info, - SortableJobOrder::CompareInformation *compare_info) -{ - for (const string & new_job_id : _jobs_released_recently) - { - const Job * new_job = (*_workload)[new_job_id]; - PPK_ASSERT_ERROR(new_job->has_walltime, - "This scheduler only supports jobs with walltimes."); - } - - EasyBackfilling::make_decisions(date, update_info, compare_info); - write_current_metrics_in_file(date); -} - -void EasyBackfillingPlotLiquidLoadHorizon::write_current_metrics_in_file(double date) -{ - Rational liquid_load_horizon = compute_liquid_load_horizon(_schedule, _queue, date); - - /*Rational queueing_theory_period = 60*60*24*10; - estimator.remove_old(date - queueing_theory_period); - Rational qt_mean_wt = estimator.estimate_waiting_time(queueing_theory_period);*/ - - const int buf_size = 256; - int nb_printed; - char * buf = (char *) malloc(sizeof(char) * buf_size); - - nb_printed = snprintf(buf, buf_size, "%g,%d,%g,%g\n", date, _queue->nb_jobs(), - (double) _queue->compute_load_estimation(), - (double) liquid_load_horizon); - //(double) qt_mean_wt); - PPK_ASSERT_ERROR(nb_printed < buf_size - 1, - "Buffer too small, some information might have been lost!"); - _output_file.write(buf, strlen(buf)); - - free(buf); -} - -Rational EasyBackfillingPlotLiquidLoadHorizon::compute_liquid_load_horizon(const Schedule &schedule, - const Queue *queue, - Rational starting_time) -{ - // Let's check whether the starting_time is valid - PPK_ASSERT_ERROR(starting_time >= schedule.first_slice_begin()); - PPK_ASSERT_ERROR(starting_time < schedule.infinite_horizon()); - - // Let's compute the total load (area) in the queue - Rational load_to_distribute = queue->compute_load_estimation(); - - // Let's fill the queue load into the schedule by fluidifying it - auto slice_it = schedule.find_last_time_slice_before_date(starting_time, false); - Rational current_time = starting_time; - - while (load_to_distribute > 0 && slice_it != schedule.end()) - { - const Schedule::TimeSlice & slice = *slice_it; - - // If the starting time is in the middle of the schedule, the whole - // time slice length is not to be considered. - Rational amount_of_time_to_consider = max(Rational(0), slice.end - max(starting_time, slice.begin)); - Rational slice_empty_area = slice.available_machines.size() * amount_of_time_to_consider; - - if (slice_empty_area <= load_to_distribute) - { - load_to_distribute -= slice_empty_area; - current_time = slice.end; - ++slice_it; - } - else - { - PPK_ASSERT_ERROR(slice.available_machines.size() > 0); - Rational amount_of_time_needed_to_fill_last_slice = load_to_distribute / slice.available_machines.size(); - current_time += amount_of_time_needed_to_fill_last_slice; - load_to_distribute = 0; - } - } - - // Degenerate case: all the machines are probably in a sleep state - if (load_to_distribute > 0) - current_time = schedule.infinite_horizon(); - - Rational ret_value = current_time - starting_time; - PPK_ASSERT_ERROR(ret_value >= 0); - return ret_value; -} diff --git a/src/algo/easy_bf_plot_liquid_load_horizon.hpp b/src/algo/easy_bf_plot_liquid_load_horizon.hpp deleted file mode 100644 index 29f536bd394368e5307c40854bd53cfe5dae38d7..0000000000000000000000000000000000000000 --- a/src/algo/easy_bf_plot_liquid_load_horizon.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include <fstream> - -#include "easy_bf.hpp" -#include "../queueing_theory_waiting_time_estimator.hpp" - -class EasyBackfillingPlotLiquidLoadHorizon : public EasyBackfilling -{ -public: - EasyBackfillingPlotLiquidLoadHorizon(Workload * workload, - SchedulingDecision * decision, - Queue * queue, - ResourceSelector * selector, - double rjms_delay, - rapidjson::Document * variant_options); - - virtual ~EasyBackfillingPlotLiquidLoadHorizon(); - - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); - -public: - void write_current_metrics_in_file(double date); - -public: - static Rational compute_liquid_load_horizon(const Schedule & schedule, - const Queue * queue, - Rational starting_time); - -private: - std::ofstream _output_file; - //QueueingTheoryWaitingTimeEstimator estimator; -}; - diff --git a/src/algo/energy_bf.cpp b/src/algo/energy_bf.cpp deleted file mode 100644 index 7f3cb7687583b66c78a1e7fec5fd54f7addeb143..0000000000000000000000000000000000000000 --- a/src/algo/energy_bf.cpp +++ /dev/null @@ -1,1373 +0,0 @@ -#include "energy_bf.hpp" - -#include <boost/algorithm/string.hpp> -#include <boost/regex.hpp> - -#include <loguru.hpp> - -#include "../pempek_assert.hpp" - -using namespace std; - -EnergyBackfilling::MachineInformation::MachineInformation(int machine_id) : - machine_number(machine_id) -{ - PPK_ASSERT_ERROR(machine_id >= 0); - create_selector(); -} - -EnergyBackfilling::MachineInformation::~MachineInformation() -{ - free_jobs(); - free_selector(); -} - -void EnergyBackfilling::MachineInformation::create_jobs(double rjms_delay, - Rational ensured_sleep_time_lower_bound, - Rational ensured_sleep_time_upper_bound) -{ - PPK_ASSERT_ERROR(rjms_delay >= 0); - PPK_ASSERT_ERROR(ensured_sleep_time_lower_bound <= ensured_sleep_time_upper_bound); - PPK_ASSERT_ERROR(switch_on_job == nullptr); - PPK_ASSERT_ERROR(switch_off_job == nullptr); - PPK_ASSERT_ERROR(ensured_sleep_job == nullptr); - PPK_ASSERT_ERROR(potential_sleep_job == nullptr); - PPK_ASSERT_ERROR(machine_number > -1); - PPK_ASSERT_ERROR(switch_on_seconds > 0); - PPK_ASSERT_ERROR(switch_off_seconds > 0); - PPK_ASSERT_ERROR(switch_on_energy > 0); - PPK_ASSERT_ERROR(switch_off_energy > 0); - PPK_ASSERT_ERROR(idle_epower > 0); - PPK_ASSERT_ERROR(sleep_epower >= 0); - PPK_ASSERT_ERROR(idle_epower > sleep_epower); - - switch_on_job = new Job; - switch_on_job->id = "fakejob_son_" + to_string(machine_number); - switch_on_job->nb_requested_resources = 1; - switch_on_job->submission_time = 0; - switch_on_job->walltime = (double) switch_on_seconds + rjms_delay; - - switch_off_job = new Job; - switch_off_job->id = "fakejob_soff_" + to_string(machine_number); - switch_off_job->nb_requested_resources = 1; - switch_off_job->submission_time = 0; - switch_off_job->walltime = (double) switch_off_seconds + rjms_delay; - - /* Let us determine the minimum time that should be waited to avoid pure loss of energy via switchON + switchOFF. - * When a machine is switched OFF then ON, the time can be split in 3 parts : - * - a, the switching OFF part. In this part, the power is pOFF - * - s, the asleep part. In this part, the power is pSLEEP - * - b, the switching ON part. In this part, the power is pON - * - * pIDLE is the power used by the machine in idle state. - * We want to ensure that the switch OFF then ON does not take more energy than staying in idle state: - * a * pOFF + s * pSLEEP + b * pON <= (a+s+b) * pIDLE - * (a * pOFF) + (s * pSLEEP) + (b * pON) <= ((a+b) * pIDLE) + (s * pIDLE) - * (s * pSLEEP) - (s * pIDLE) <= ((a+b) * pIDLE) - (a * pOFF) - (b * pON) - * s * (pSLEEP - pIDLE) <= ((a+b) * pIDLE) - (a * pOFF) - (b * pON) - * s >= (((a+b) * pIDLE) - (a * pOFF) - (b * pON)) / (pSLEEP - pIDLE) because (pSLEEP - pIDLE) is negative <=> pIDLE > pSLEEP - */ - - Rational minimum_sleeping_time = ((switch_off_seconds + switch_on_seconds + 2 * rjms_delay) * idle_epower - switch_off_energy - switch_on_energy) / (sleep_epower - idle_epower); - - minimum_sleeping_time = min(max(minimum_sleeping_time, - ensured_sleep_time_lower_bound), - ensured_sleep_time_upper_bound); - - ensured_sleep_job = new Job; - ensured_sleep_job->id = "fakejob_esleep_" + to_string(machine_number); - ensured_sleep_job->nb_requested_resources = 1; - ensured_sleep_job->submission_time = 0; - ensured_sleep_job->walltime = (double) minimum_sleeping_time; - - potential_sleep_job = new Job; - potential_sleep_job->id = "fakejob_psleep_" + to_string(machine_number); - potential_sleep_job->nb_requested_resources = 1; - potential_sleep_job->submission_time = 0; - potential_sleep_job->walltime = std::numeric_limits<double>::max(); -} - -void EnergyBackfilling::MachineInformation::free_jobs() -{ - if (switch_on_job) - { - delete switch_on_job; - switch_on_job = nullptr; - } - - if (switch_off_job) - { - delete switch_off_job; - switch_off_job = nullptr; - } - - if (ensured_sleep_job) - { - delete ensured_sleep_job; - ensured_sleep_job = nullptr; - } - - if (potential_sleep_job) - { - delete potential_sleep_job; - potential_sleep_job = nullptr; - } -} - -void EnergyBackfilling::MachineInformation::create_selector() -{ - PPK_ASSERT_ERROR(limited_resource_selector == nullptr); - PPK_ASSERT_ERROR(machine_number >= 0); - - limited_resource_selector = new LimitedRangeResourceSelector(IntervalSet(machine_number)); -} - -void EnergyBackfilling::MachineInformation::free_selector() -{ - if (limited_resource_selector != nullptr) - { - delete limited_resource_selector; - limited_resource_selector = nullptr; - } -} - - - -string EnergyBackfilling::machine_state_to_string(const EnergyBackfilling::MachineState &state) -{ - switch (state) - { - case AWAKE: return "awake"; - case ASLEEP: return "asleep"; - case SWITCHING_OFF: return "switching_off"; - case SWITCHING_ON: return "switching_on"; - } - - PPK_ASSERT_ERROR(false, "Unhandled case"); - return "unhandled_case"; -} - - - -EnergyBackfilling::EnergyBackfilling(Workload * workload, SchedulingDecision * decision, Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options) : - ISchedulingAlgorithm(workload, decision, queue, selector, rjms_delay, variant_options) -{ -} - -EnergyBackfilling::~EnergyBackfilling() -{ - clear_machine_informations(); -} - -void EnergyBackfilling::on_simulation_start(double date, const rapidjson::Value & batsim_config) -{ - _schedule = Schedule(_nb_machines, date); - (void) batsim_config; - - generate_machine_informations(_nb_machines); - - _all_machines = IntervalSet::ClosedInterval(0, _nb_machines - 1); - PPK_ASSERT_ERROR((int)(_all_machines.size()) == _nb_machines); - - _awake_machines.insert(_all_machines); -} - -void EnergyBackfilling::on_simulation_end(double date) -{ - (void) date; - // TODO: do something about this? -} - -void EnergyBackfilling::on_machine_state_changed(double date, IntervalSet machines, int new_state) -{ - if (_debug) - { - LOG_F(1, "on_machine_state_changed beginning, schedule : %s", _schedule.to_string().c_str()); - } - - // Let's update the current schedule to take the machine state change into account - IntervalSet switched_off_machines; - - // Let's remove all related switch jobs from the online schedule - for (auto machine_it = machines.elements_begin(); machine_it != machines.elements_end(); ++machine_it) - { - int machine_id = *machine_it; - MachineInformation * machine_info = _machine_informations.at(machine_id); - - PPK_ASSERT_ERROR(new_state == machine_info->compute_pstate || - new_state == machine_info->sleep_pstate, - "Machine %d just changed its pstate to %d, " - "but this pstate is unknown. compute_pstate=%d, " - "sleep_pstate=%d.", machine_id, new_state, - machine_info->compute_pstate, - machine_info->sleep_pstate); - - if (new_state == machine_info->compute_pstate) - { - // A Switch ON has been acknowledged. Let's remove the switch job from the schedule - bool switch_on_job_removed = _schedule.remove_job_if_exists(machine_info->switch_on_job); - PPK_ASSERT_ERROR(switch_on_job_removed, - "Machine %d has just been switched ON, but there was " - "no switch ON job for it in the schedule...\n%s", - machine_id, _schedule.to_string().c_str()); - - // Update machine state - PPK_ASSERT_ERROR(_switching_on_machines.contains(machine_id), - "Machine %d has just been switched ON, but the " - "machine was not marked as a switching ON one. " - "switching_on_machines=%s", - machine_id, - _switching_on_machines.to_string_brackets().c_str()); - _switching_on_machines.remove(machine_id); - - PPK_ASSERT_ERROR(!_awake_machines.contains(machine_id), - "Machine %d has just been switched ON, but the " - "machine was already marked as an awake machine... " - "awake_machines=%s", - machine_id, - _awake_machines.to_string_brackets().c_str()); - _awake_machines.insert(machine_id); - } - else - { - // A Switch OFF has been acknowledged. Let's remove the switch job from the schedule - bool switch_off_job_removed = _schedule.remove_job_if_exists(machine_info->switch_off_job); - PPK_ASSERT_ERROR(switch_off_job_removed, - "Machine %d has just been switched OFF, but there was " - "no switch OFF job for it in the schedule...\n%s", - machine_id, _schedule.to_string().c_str()); - - // Update machine state - PPK_ASSERT_ERROR(_switching_off_machines.contains(machine_id), - "Machine %d has just been switched OFF, but the " - "machine was not marked as a switching OFF one. " - "switching_off_machines=%s", - machine_id, - _switching_off_machines.to_string_brackets().c_str()); - _switching_off_machines.remove(machine_id); - - PPK_ASSERT_ERROR(!_asleep_machines.contains(machine_id), - "Machine %d has just been switched OFF, but the " - "machine was already marked as an asleep one... " - "asleep_machines=%s", - machine_id, _asleep_machines.to_string_brackets().c_str()); - _asleep_machines.insert(machine_id); - - if (_schedule.contains_job(machine_info->ensured_sleep_job)) - _non_wakable_asleep_machines.insert(machine_id); - else - { - PPK_ASSERT_ERROR(_schedule.contains_job(machine_info->potential_sleep_job), - "Machine %d has just been switched OFF, but there " - "was no ensured nor potential sleep job in the schedule " - "for this machine.\n%s", machine_id, - _schedule.to_string().c_str()); - _wakable_asleep_machines.insert(machine_id); - } - - // Let's mark that the sleep jobs of this machine should be translated to the left - switched_off_machines.insert(machine_id); - } - } - - if (_debug) - { - LOG_F(1, "on_machine_state_changed, before sleep jobs translation. %s", _schedule.to_string().c_str()); - } - - // Let's translate to the left (-> present) the sleep jobs that comes from newly asleep machines - for (auto machine_it = switched_off_machines.elements_begin(); machine_it != switched_off_machines.elements_end(); ++machine_it) - { - int machine_id = *machine_it; - MachineInformation * machine_info = _machine_informations.at(machine_id); - - // Let's remove previously existing sleep jobs - _schedule.remove_job_if_exists(machine_info->ensured_sleep_job); - - bool removed_potential_sleep = _schedule.remove_job_if_exists(machine_info->potential_sleep_job); - PPK_ASSERT_ERROR(removed_potential_sleep, - "Machine %d has just been switched OFF, but there was no " - "potential sleep job in the schedule for this machine.\n%s", - machine_id, _schedule.to_string().c_str()); - - // Let's insert them back in the schedule at the right place - sedate_machine_without_switch(_schedule, machine_id, date); - } - - if (_debug) - { - LOG_F(1, "on_machine_state_changed before update_first_slice, schedule : %s", _schedule.to_string().c_str()); - } -} - -void EnergyBackfilling::on_requested_call(double date) -{ - (void) date; - PPK_ASSERT_ERROR(_nb_call_me_later_running > 0, - "Received a REQUESTED_CALL message from Batsim while there " - "was no running call_me_later request."); - _nb_call_me_later_running--; -} - -void EnergyBackfilling::generate_machine_informations(int nb_machines) -{ - PPK_ASSERT_ERROR(nb_machines > 0); - - // Let's parse the options file - PPK_ASSERT_ERROR(_variant_options->HasMember("power_compute"), "Invalid options JSON object: Member 'power_compute' cannot be found"); - PPK_ASSERT_ERROR((*_variant_options)["power_compute"].IsNumber(), "Invalid options JSON object: Member 'power_compute' must be a number"); - Rational power_compute = (*_variant_options)["power_compute"].GetDouble(); - PPK_ASSERT_ERROR(power_compute > 0, "Invalid options JSON object: Member 'power_compute' must be strictly positive (got %g)", (double)power_compute); - - PPK_ASSERT_ERROR(_variant_options->HasMember("power_idle"), "Invalid options JSON object: Member 'power_idle' cannot be found"); - PPK_ASSERT_ERROR((*_variant_options)["power_idle"].IsNumber(), "Invalid options JSON object: Member 'power_idle' must be a number"); - Rational power_idle = (*_variant_options)["power_idle"].GetDouble(); - PPK_ASSERT_ERROR(power_idle > 0, "Invalid options JSON object: Member 'power_idle' must be strictly positive (got %g)", (double)power_idle); - - PPK_ASSERT_ERROR(_variant_options->HasMember("power_sleep"), "Invalid options JSON object: Member 'power_sleep' cannot be found"); - PPK_ASSERT_ERROR((*_variant_options)["power_sleep"].IsNumber(), "Invalid options JSON object: Member 'power_sleep' must be a number"); - Rational power_sleep = (*_variant_options)["power_sleep"].GetDouble(); - PPK_ASSERT_ERROR(power_sleep > 0, "Invalid options JSON object: Member 'power_sleep' must be strictly positive (got %g)", (double)power_sleep); - - - PPK_ASSERT_ERROR(_variant_options->HasMember("pstate_compute"), "Invalid options JSON object: Member 'pstate_compute' cannot be found"); - PPK_ASSERT_ERROR((*_variant_options)["pstate_compute"].IsInt(), "Invalid options JSON object: Member 'pstate_compute' must be integral"); - int pstate_compute = (*_variant_options)["pstate_compute"].GetInt(); - PPK_ASSERT_ERROR(pstate_compute >= 0, "Invalid options JSON object: Member 'pstate_compute' value must be positive (got %d)", pstate_compute); - - PPK_ASSERT_ERROR(_variant_options->HasMember("pstate_sleep"), "Invalid options JSON object: Member 'pstate_sleep' cannot be found"); - PPK_ASSERT_ERROR((*_variant_options)["pstate_sleep"].IsInt(), "Invalid options JSON object: Member 'pstate_sleep' must be integral"); - int pstate_sleep = (*_variant_options)["pstate_sleep"].GetInt(); - PPK_ASSERT_ERROR(pstate_sleep >= 0, "Invalid options JSON object: Member 'pstate_sleep' value must be positive (got %d)", pstate_sleep); - - - PPK_ASSERT_ERROR(_variant_options->HasMember("time_switch_on"), "Invalid options JSON object: Member 'time_switch_on' cannot be found"); - PPK_ASSERT_ERROR((*_variant_options)["time_switch_on"].IsNumber(), "Invalid options JSON object: Member 'time_switch_on' must be a number"); - Rational time_switch_on = (*_variant_options)["time_switch_on"].GetDouble(); - PPK_ASSERT_ERROR(time_switch_on > 0, "Invalid options JSON object: Member 'time_switch_on' value must be strictly positive (got %g)", (double)time_switch_on); - - PPK_ASSERT_ERROR(_variant_options->HasMember("time_switch_off"), "Invalid options JSON object: Member 'time_switch_off' cannot be found"); - PPK_ASSERT_ERROR((*_variant_options)["time_switch_off"].IsNumber(), "Invalid options JSON object: Member 'time_switch_off' must be a number"); - Rational time_switch_off = (*_variant_options)["time_switch_off"].GetDouble(); - PPK_ASSERT_ERROR(time_switch_off > 0, "Invalid options JSON object: Member 'time_switch_off' value must be strictly positive (got %g)", (double)time_switch_off); - - - PPK_ASSERT_ERROR(_variant_options->HasMember("energy_switch_on"), "Invalid options JSON object: Member 'energy_switch_on' cannot be found"); - PPK_ASSERT_ERROR((*_variant_options)["energy_switch_on"].IsNumber(), "Invalid options JSON object: Member 'energy_switch_on' must be a number"); - Rational energy_switch_on = (*_variant_options)["energy_switch_on"].GetDouble(); - PPK_ASSERT_ERROR(energy_switch_on > 0, "Invalid options JSON object: Member 'energy_switch_on' value must be strictly positive (got %g)", (double)energy_switch_on); - - PPK_ASSERT_ERROR(_variant_options->HasMember("energy_switch_off"), "Invalid options JSON object: Member 'energy_switch_off' cannot be found"); - PPK_ASSERT_ERROR((*_variant_options)["energy_switch_off"].IsNumber(), "Invalid options JSON object: Member 'energy_switch_off' must be a number"); - Rational energy_switch_off = (*_variant_options)["energy_switch_off"].GetDouble(); - PPK_ASSERT_ERROR(energy_switch_off > 0, "Invalid options JSON object: Member 'energy_switch_off' value must be strictly positive (got %g)", (double)energy_switch_off); - - - Rational ensured_sleep_time_lower_bound = 0; - if (_variant_options->HasMember("ensured_sleep_time_lower_bound")) - { - PPK_ASSERT_ERROR((*_variant_options)["ensured_sleep_time_lower_bound"].IsNumber(), - "Invalid options JSON object: Member 'ensured_sleep_time_lower_bound' is not a number"); - ensured_sleep_time_lower_bound = (*_variant_options)["ensured_sleep_time_lower_bound"].GetDouble(); - PPK_ASSERT_ERROR(ensured_sleep_time_lower_bound >= 0, - "Invalid options JSON object: Member 'ensured_sleep_time_lower_bound' must " - "be positive (got %g)", (double) ensured_sleep_time_lower_bound); - } - - Rational ensured_sleep_time_upper_bound = 1e8; - if (_variant_options->HasMember("ensured_sleep_time_upper_bound")) - { - PPK_ASSERT_ERROR((*_variant_options)["ensured_sleep_time_upper_bound"].IsNumber(), - "Invalid options JSON object: Member 'ensured_sleep_time_upper_bound' is not a number"); - ensured_sleep_time_upper_bound = (*_variant_options)["ensured_sleep_time_upper_bound"].GetDouble(); - PPK_ASSERT_ERROR(ensured_sleep_time_upper_bound >= 0, - "Invalid options JSON object: Member 'ensured_sleep_time_upper_bound' must " - "be positive (got %g)", (double) ensured_sleep_time_upper_bound); - } - - PPK_ASSERT_ERROR(ensured_sleep_time_lower_bound <= ensured_sleep_time_upper_bound, - "Invalid options JSON object: ensured_sleep_time_lower_bound (%g) must be " - "lower than or equal to ensured_sleep_time_upper_bound (%g), which is not " - "the case here.", (double) ensured_sleep_time_lower_bound, - (double) ensured_sleep_time_upper_bound); - - for (int i = 0; i < nb_machines; ++i) - { - MachineInformation * minfo = new MachineInformation(i); - - minfo->compute_pstate = pstate_compute; - minfo->sleep_pstate = pstate_sleep; - minfo->compute_epower = power_compute; - minfo->idle_epower = power_idle; - minfo->sleep_epower = power_sleep; - minfo->switch_on_energy = energy_switch_on; - minfo->switch_off_energy = energy_switch_off; - minfo->switch_on_seconds = time_switch_on; - minfo->switch_off_seconds = time_switch_off; - minfo->switch_on_electrical_power = energy_switch_on / time_switch_on; - minfo->switch_off_electrical_power = energy_switch_off / time_switch_off; - - minfo->create_jobs(_rjms_delay, ensured_sleep_time_lower_bound, - ensured_sleep_time_upper_bound); - - _machine_informations[minfo->machine_number] = minfo; - } - - LOG_F(INFO, "Ensured sleep length of the first machine : %g seconds.", - (double) _machine_informations[0]->ensured_sleep_job->walltime); -} - -void EnergyBackfilling::clear_machine_informations() -{ - for (auto mit : _machine_informations) - { - MachineInformation * minfo = mit.second; - delete minfo; - } - - _machine_informations.clear(); -} - -void EnergyBackfilling::make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info) -{ - // Let's remove finished jobs from the schedule - for (const string & ended_job_id : _jobs_ended_recently) - { - const Job * ended_job = (*_workload)[ended_job_id]; - ++_nb_jobs_completed; - - PPK_ASSERT_ERROR(_schedule.contains_job(ended_job), - "Invalid schedule: job '%s' just finished, " - "but it not in the schedule...\n%s", - ended_job_id.c_str(), _schedule.to_string().c_str()); - PPK_ASSERT_ERROR(!_queue->contains_job(ended_job), - "Job '%s' just ended, but it is still in the " - "queue...\nQueue : %s", - ended_job_id.c_str(), - _queue->to_string().c_str()); - - // Let's remove the finished job from the schedule - _schedule.remove_job(ended_job); - } - - // Let's handle recently released jobs - for (const string & new_job_id : _jobs_released_recently) - { - const Job * new_job = (*_workload)[new_job_id]; - ++_nb_jobs_submitted; - - PPK_ASSERT_ERROR(!_schedule.contains_job(new_job), - "Invalid schedule: job '%s' already exists.\n%s", - new_job->id.c_str(), _schedule.to_string().c_str()); - PPK_ASSERT_ERROR(!_queue->contains_job(new_job), - "Job '%s' is already in the queue!\nQueue:%s", - new_job->id.c_str(), _queue->to_string().c_str()); - - if (new_job->nb_requested_resources > _nb_machines) - { - _decision->add_reject_job(new_job_id, date); - ++_nb_jobs_completed; - } - else - _queue->append_job(new_job, update_info); - } - - // Let's update the first slice of the schedule - update_first_slice_taking_sleep_jobs_into_account(date); - - // Let's update the sorting of the queue - _queue->sort_queue(update_info, compare_info); - - Schedule current_schedule = _schedule; - - if (_debug) - { - LOG_F(1, "Schedule before put_jobs_into_schedule: %s", current_schedule.to_string().c_str()); - } - - put_jobs_into_schedule(current_schedule); - - if (_debug) - { - LOG_F(1, "Schedule before sedate_machines_at_the_furthest_moment: %s", current_schedule.to_string().c_str()); - } - - sedate_machines_at_the_furthest_moment(current_schedule, _awake_machines); - - make_decisions_of_schedule(current_schedule); -} - -void EnergyBackfilling::make_decisions_of_schedule(const Schedule &schedule, - bool run_call_me_later_on_nothing_to_do) -{ - bool did_something = false; - - PPK_ASSERT_ERROR(schedule.nb_slices() > 0); - const Schedule::TimeSlice & slice = *schedule.begin(); - PPK_ASSERT_ERROR(slice.begin == _schedule.first_slice_begin()); - - map<int, IntervalSet> state_switches_to_do; - - for (auto mit : slice.allocated_jobs) - { - const Job * job = mit.first; - const IntervalSet & job_machines = mit.second; - - // If the job is a fake one - if (is_fake_job(job->id)) - { - PPK_ASSERT_ERROR(job_machines.size() == 1); - MachineInformation * machine_info = _machine_informations.at(job_machines.first_element()); - int machine_id = machine_info->machine_number; - - if (is_switch_on_job(job->id)) - { - // If the switch ON is new - if (!_schedule.contains_job(job)) - { - // Let's remove the sleep jobs from the online schedule - bool ensured_removed = _schedule.remove_job_if_exists(machine_info->ensured_sleep_job); - bool potential_removed = _schedule.remove_job_if_exists(machine_info->potential_sleep_job); - PPK_ASSERT_ERROR(ensured_removed || potential_removed); - - // Let's insert the switch ON into the online schedule - Schedule::JobAlloc alloc = _schedule.add_job_first_fit(job, machine_info->limited_resource_selector); - PPK_ASSERT_ERROR(alloc.begin == slice.begin); - - // Let's register that this machine should be switched ON now - state_switches_to_do[machine_info->compute_pstate].insert(machine_info->machine_number); - - // Let's update machine states - PPK_ASSERT_ERROR(_asleep_machines.contains(machine_id)); - _asleep_machines.remove(machine_id); - - PPK_ASSERT_ERROR(_wakable_asleep_machines.contains(machine_id)); - _wakable_asleep_machines.remove(machine_id); - - PPK_ASSERT_ERROR(!_switching_on_machines.contains(machine_id)); - _switching_on_machines.insert(machine_info->machine_number); - } - } - else if (is_switch_off_job(job->id)) - { - // If the switch OFF is new - if (!_schedule.contains_job(job)) - { - // Let's sedate the machine into the online schedule - sedate_machine(_schedule, machine_info->machine_number, _schedule.begin()); - - // Let's register that this machine should be switched OFF now - state_switches_to_do[machine_info->sleep_pstate].insert(machine_info->machine_number); - - // Let's update machine states - PPK_ASSERT_ERROR(_awake_machines.contains(machine_id)); - _awake_machines.remove(machine_id); - - PPK_ASSERT_ERROR(!_switching_off_machines.contains(machine_id)); - _switching_off_machines.insert(machine_info->machine_number); - } - } - } - else // The job is a real one - { - // If the job is a new one - if (_queue->contains_job(job)) - { - // Let's append it to the online schedule - LimitedRangeResourceSelector selector(job_machines); - Schedule::JobAlloc alloc = _schedule.add_job_first_fit(job, &selector); - PPK_ASSERT_ERROR(alloc.started_in_first_slice); - PPK_ASSERT_ERROR(alloc.begin == slice.begin); - - // Let's tell the RJMS this job should be executed now - _decision->add_execute_job(job->id, job_machines, (double)alloc.begin); - did_something = true; - - // Let's remove it from the queue - _queue->remove_job(job); - } - } - } - - // Let's make state switch decisions now - for (auto mit : state_switches_to_do) - { - int target_pstate = mit.first; - const IntervalSet & machines = mit.second; - - _decision->add_set_resource_state(machines, target_pstate, (double) slice.begin); - did_something = true; - } - - if (run_call_me_later_on_nothing_to_do) - { - if (!did_something && (_nb_jobs_completed < _workload->nb_jobs()) && (_nb_call_me_later_running == 0)) - { - // To avoid Batsim's deadlock, we should tell it to wait that we are ready. - - PPK_ASSERT_ERROR(_schedule.nb_slices() >= 1); - _decision->add_call_me_later((double) _schedule.begin()->end + 1, (double) _schedule.begin()->begin); - _nb_call_me_later_running++; - } - } -} - -void EnergyBackfilling::update_first_slice_taking_sleep_jobs_into_account(Rational date) -{ - PPK_ASSERT_ERROR(_schedule.nb_slices() > 0); - - if (_debug) - LOG_F(1, "update_first_slice... Date=%f\n%s", - (double) date, _schedule.to_string().c_str()); - - // Since we are not only using "real" jobs, the usual assumption that a slice cannot - // end prematurely (because of walltimes being greater than execution times) does not stand. - - // We are sure that "Switch ON" and "Switch OFF" jobs behave the same that usual walltime jobs. - // However this is not the case for the two sleeping jobs (ensured and potential). - - // Ensured sleeping jobs are used to ensure that a machine is not awaken too soon, which - // would cause a pure loss of energy (w.r.t. letting the machine idle). Hence, those jobs - // only exists in the scheduler view and Batsim won't tell when these jobs are finished. - // Thus, finition of those jobs must be detected by the scheduler. - - // Potential sleeping jobs are used to make sure an asleep machine is not used for - // computing jobs. These jobs have an infinite length and their termination should - // not be a problem. - - - // Hence, the update is valid if: - // - No slice ended prematurely - // - If slices ended prematurely, they were only related to ensured->potential sleep jobs. - - auto slice_it = _schedule.begin(); - bool slices_have_been_ended = false; - - // Complex tests will only be done if some slices ended prematurely - if (date >= slice_it->end) - { - slices_have_been_ended = true; - - // Let's make sure all jobs but ensured-sleep related ones remain in the same - // state in the scheduling, from the first slice to the current date - IntervalSet sleeping_machines; - set<string> non_sleep_jobs; - - const Schedule::TimeSlice & first_slice = *slice_it; - for (const auto & mit : first_slice.allocated_jobs) - { - const Job * job = mit.first; - if (!is_ensured_sleep_job(job->id) && !is_potential_sleep_job(job->id)) - non_sleep_jobs.insert(job->id); - else - sleeping_machines.insert(mit.second); - } - - while (slice_it->end <= date) - { - set<string> non_sleep_jobs_in_slice; - IntervalSet sleeping_machines_in_slice; - const Schedule::TimeSlice & slice = *slice_it; - - // Let's check that aside from ensured sleep jobs, the jobs - // are the same in the involved slices (those ended + the merge slice) - for (const auto & mit : slice.allocated_jobs) - { - const Job * job = mit.first; - if (!is_ensured_sleep_job(job->id) && !is_potential_sleep_job(job->id)) - { - PPK_ASSERT_ERROR(non_sleep_jobs.find(job->id) != non_sleep_jobs.end(), - "A time slice ended prematurely, which is only " - "allowed if the ended slices only exist because of " - "ensured sleep jobs, which is not the case here, " - "since job '%s' is not in all involved slices. Date=%g. %s", - job->id.c_str(), - (double) date, - _schedule.to_string().c_str()); - non_sleep_jobs_in_slice.insert(job->id); - } - else - sleeping_machines_in_slice.insert(mit.second); - - } - - PPK_ASSERT_ERROR(non_sleep_jobs_in_slice == non_sleep_jobs, - "A time slice ended prematurely, which is only allowed " - "if the ended slices only exist because of ensured->potential " - "sleep jobs, which is not the case here, because other jobs " - "appeared/vanished in slice '%s'. Date=%g.\n" - "Non sleep jobs in first slice: %s\n" - "Non sleep jobs in current slice: %s\n%s", - slice.to_string().c_str(), - (double) date, - boost::algorithm::join(non_sleep_jobs, ",").c_str(), - boost::algorithm::join(non_sleep_jobs_in_slice, ",").c_str(), - _schedule.to_string().c_str()); - - PPK_ASSERT_ERROR(sleeping_machines_in_slice == sleeping_machines, - "A time slice ended prematurely, which is only allowed " - "if the ended slices only exist because of ensured->potential " - "sleep jobs, which is not the case here, since the sleeping " - "machines are not the same in the first slice (%s) and in" - "the current one (%s).", - sleeping_machines.to_string_brackets().c_str(), - sleeping_machines_in_slice.to_string_brackets().c_str()); - - if (_debug) - { - LOG_F(1, "The slice will be removed because everything seems fine.\n" - "sleeping_machines = %s\nnon_sleep_jobs=%s\n%s", - sleeping_machines.to_string_brackets().c_str(), - boost::algorithm::join(non_sleep_jobs, ",").c_str(), - slice.to_string().c_str()); - } - - ++slice_it; - } - } - - // The schedule seems to be fine. - _schedule.update_first_slice_removing_remaining_jobs(date); - - if (slices_have_been_ended) - { - // Let's update whether machines are wakable. - slice_it = _schedule.begin(); - - IntervalSet wakable_machines_now, non_wakable_machines_now; - - for (const auto mit : slice_it->allocated_jobs) - { - const Job * job = mit.first; - const IntervalSet & alloc = mit.second; - if (is_ensured_sleep_job(job->id)) - non_wakable_machines_now.insert(alloc); - else if (is_potential_sleep_job(job->id)) - wakable_machines_now.insert(alloc); - } - - // Let's make sure these machines are valid. - PPK_ASSERT_ERROR((wakable_machines_now & non_wakable_machines_now) == IntervalSet::empty_interval_set(), - "Invalid schedule update: the new wakable and non-wakable machines are not" - "distinct. New wakable: %s. New non wakable: %s.", - wakable_machines_now.to_string_brackets().c_str(), - non_wakable_machines_now.to_string_brackets().c_str()); - PPK_ASSERT_ERROR((wakable_machines_now + non_wakable_machines_now) == _asleep_machines, - "Invalid schedule update: the asleep machines have changed. " - "Asleep machines before: %s. Asleep machines now: %s.", - _asleep_machines.to_string_brackets().c_str(), - (wakable_machines_now + non_wakable_machines_now).to_string_brackets().c_str()); - - _wakable_asleep_machines = wakable_machines_now; - _non_wakable_asleep_machines = non_wakable_machines_now; - } - -} - -void EnergyBackfilling::put_jobs_into_schedule(Schedule &schedule) const -{ - Rational initial_infinite_horizon = schedule.infinite_horizon(); - - for (auto job_it = _queue->begin(); job_it != _queue->end(); ++job_it) - { - const Job * job = (*job_it)->job; - - // Let's try to put the job into the schedule at the first available slot. - Schedule::JobAlloc alloc = schedule.add_job_first_fit(job, _selector, false); - - // Because of sleeping machines, the allocation might be impossible. - // If this happens, some machines must be awaken - if (!alloc.has_been_inserted) - { - // The nodes we will try to switch ON are the ones in a potential sleep state between - // the finite horizon and the infinite one. Let's find which ones should be awakened. - auto slice_it = schedule.end(); - --slice_it; - const Schedule::TimeSlice & last_slice = *slice_it; - - // Let's compute which machines can be awakened in the last slice - IntervalSet machines_that_can_be_awakened = compute_potentially_awaken_machines(last_slice); - - // Let's find which machines to awaken - IntervalSet machines_to_awaken; - _selector->select_resources_to_awaken_to_make_job_fit(job, last_slice.available_machines, machines_that_can_be_awakened, machines_to_awaken); - PPK_ASSERT_ERROR(machines_to_awaken.size() > 0); - - // Let's find the first moment in time (the earliest) when all the machines to awaken can be awakened - Rational earliest_awakening_time = find_earliest_moment_to_awaken_machines(schedule, machines_to_awaken); - - // Let's modify the future schedule of the machines which should be used for computing the job - // 1. Let's reduce the length (or remove) the potential sleep jobs they have - // 2. Let's insert a wake up job for each sleeping machine - for (auto machine_it = machines_to_awaken.elements_begin(); machine_it != machines_to_awaken.elements_end(); ++machine_it) - { - int machine_id = *machine_it; - awaken_machine(schedule, machine_id, earliest_awakening_time); - } - - if (_debug) - { - LOG_F(1, "schedule before job insertion: %s", schedule.to_string().c_str()); - LOG_F(1, "job : (id='%s', walltime=%g)", job->id.c_str(), (double) job->walltime); - } - - // Let's finally insert the job - auto job_alloc = schedule.add_job_first_fit_after_time(job, earliest_awakening_time, _selector); - - if (_debug) - { - LOG_F(1, "schedule after job insertion: %s", schedule.to_string().c_str()); - } - - // Let's make sure the machine awakening was not useless - PPK_ASSERT_ERROR((job_alloc.used_machines & machines_to_awaken) != IntervalSet::empty_interval_set()); - - // Let's make sure the infinite horizon has not been changed - PPK_ASSERT_ERROR(initial_infinite_horizon == schedule.infinite_horizon()); - } - } -} - -Rational EnergyBackfilling::sedate_machines_at_the_furthest_moment(Schedule &schedule, const IntervalSet &machines_to_sedate) const -{ - PPK_ASSERT_ERROR(schedule.nb_slices() >= 1); - - auto time_slice_it = schedule.end(); - --time_slice_it; - - PPK_ASSERT_ERROR(time_slice_it->end == schedule.infinite_horizon()); - PPK_ASSERT_ERROR(time_slice_it->begin == schedule.finite_horizon()); - - Rational earliest_sedating_date = schedule.infinite_horizon(); - - // Let's store awaken_machines into one variable - IntervalSet awaken_machines = time_slice_it->available_machines; // Since sleep jobs targets the infinite horizon, the sleeping machines are not available in the last time slice - - while ((machines_to_sedate & awaken_machines) != IntervalSet::empty_interval_set()) - { - if (time_slice_it != schedule.begin()) - --time_slice_it; - - // Let's find which machines which should be sedated just after the current time slice. - // Since time slices are traversed from future to past, we know that if a machine is still awaken, - // it does nothing in the future. Then, if a machine in the awaken_machines set does something in the - // current time slice, it should be sedated at the end of the time slice currently being traversed. - IntervalSet machines_to_sedate_now; - - if (_debug) - { - LOG_F(1, "Schedule : %s", schedule.to_string().c_str()); - LOG_F(1, "Current time slice : %s", time_slice_it->to_string().c_str()); - } - - - for (auto mit : time_slice_it->allocated_jobs) - { - const Job * job = mit.first; - const IntervalSet & job_machines = mit.second; - - // If the job is a fake one - if (is_fake_job(job->id)) - { - PPK_ASSERT_ERROR(job_machines.size() == 1); - - int machine_id = job_machines.first_element(); - boost::regex e("fakejob_(.*)_(\\d+)"); - - boost::match_results<std::string::const_iterator> results; - bool matched = boost::regex_match(job->id, results, e); - PPK_ASSERT_ERROR(matched); - - int machine_id_from_job_id = stoi(results[2]); - PPK_ASSERT_ERROR(machine_id == machine_id_from_job_id); - - // If the fake job is a switch ON one - if (results[1] == "son") - machines_to_sedate_now.insert(machine_id); - } - else - machines_to_sedate_now.insert(job_machines); - } - - // Only machines that are awaken should be sedated now - machines_to_sedate_now &= awaken_machines; - - // Only machines that need to be sedated should be sedated now - machines_to_sedate_now &= machines_to_sedate; - - // Let's sedate all the machines that must be - for (auto machine_it = machines_to_sedate_now.elements_begin(); machine_it != machines_to_sedate_now.elements_end(); ++machine_it) - { - int machine_id = *machine_it; - sedate_machine(schedule, machine_id, time_slice_it, false); - earliest_sedating_date = time_slice_it->end; - // Fortunately, inserting after the current time slice can only change the iterators after (-> future) the current iterator, - // not the ones before (-> past), which makes the traversal work - } - - // Let's mark machines that will be sedated as not awaken - awaken_machines -= machines_to_sedate_now; - - if (time_slice_it == schedule.begin()) - { - // If we reached the schedule's beginning, all awaken machines that should be sedated should be put into sleep now. - machines_to_sedate_now = awaken_machines & machines_to_sedate; - - for (auto machine_it = machines_to_sedate_now.elements_begin(); machine_it != machines_to_sedate_now.elements_end(); ++machine_it) - { - int machine_id = *machine_it; - sedate_machine(schedule, machine_id, time_slice_it, true); - earliest_sedating_date = time_slice_it->begin; - // In this case, the sleep jobs should be inserted into the given time slice, not just after it - } - - awaken_machines -= machines_to_sedate_now; - - PPK_ASSERT_ERROR((awaken_machines & machines_to_sedate) == IntervalSet::empty_interval_set()); - } - } - - return earliest_sedating_date; -} - -void EnergyBackfilling::sedate_machine(Schedule &schedule, - int machine_id, - std::list<Schedule::TimeSlice>::iterator time_slice, - bool insert_in_slice) const -{ - // Let's retrieve the MachineInformation - MachineInformation * minfo = _machine_informations.at(machine_id); - - if (_debug) - { - LOG_F(1, "\n-----\n"); - LOG_F(1, "Machine to sedate: %d", machine_id); - LOG_F(1, "Schedule before switch_off_alloc : %s", schedule.to_string().c_str()); - } - - // Let's add the switch OFF job into the schedule - Schedule::JobAlloc switch_off_alloc = schedule.add_job_first_fit_after_time_slice(minfo->switch_off_job, time_slice, minfo->limited_resource_selector); - - if (_debug) - { - LOG_F(1, "Schedule after switch_off_alloc : %s", schedule.to_string().c_str()); - } - - if (insert_in_slice) - PPK_ASSERT_ERROR(switch_off_alloc.begin == time_slice->begin, - "switch_off_alloc.begin = %g, time_slice->begin = %g,", - (double) switch_off_alloc.begin, (double) time_slice->begin); - else - PPK_ASSERT_ERROR(switch_off_alloc.begin == time_slice->end, - "switch_off_alloc.begin = %g, time_slice->end = %g,", - (double) switch_off_alloc.begin, (double) time_slice->end); - - sedate_machine_without_switch(schedule, machine_id, switch_off_alloc.end); -} - -void EnergyBackfilling::sedate_machine_without_switch(Schedule &schedule, - int machine_id, - Rational when_it_should_start) const -{ - // Let's retrieve the MachineInformation - MachineInformation * minfo = _machine_informations.at(machine_id); - - Rational when_ensured_sleep_job_finishes = when_it_should_start; - - if (minfo->ensured_sleep_job->walltime > 0) - { - // Let's add the ensured sleep job into the schedule, right after the previous one - Schedule::JobAlloc ensured_sleep_alloc = schedule.add_job_first_fit_after_time(minfo->ensured_sleep_job, when_it_should_start, minfo->limited_resource_selector); - - if (_debug) - { - LOG_F(1, "Schedule after ensured_sleep_alloc : %s", schedule.to_string().c_str()); - } - - PPK_ASSERT_ERROR(ensured_sleep_alloc.begin == when_it_should_start, - "ensured_sleep_alloc.begin = %g, when_it_should_start = %g", - (double) ensured_sleep_alloc.begin, (double) when_it_should_start); - - when_ensured_sleep_job_finishes = ensured_sleep_alloc.end; - } - - // Let's change the walltime of the potential sleep job to make sure it perfectly reaches the infinite horizon - minfo->potential_sleep_job->walltime = schedule.infinite_horizon() - when_ensured_sleep_job_finishes; - Rational infinite_horizon_before = schedule.infinite_horizon(); - - // Let's add the potential sleep job into the schedule, right after the previous one - Schedule::JobAlloc potential_sleep_alloc = schedule.add_job_first_fit_after_time(minfo->potential_sleep_job, when_it_should_start, minfo->limited_resource_selector); - - if (_debug) - { - LOG_F(1, "Schedule after potential_sleep_alloc : %s", schedule.to_string().c_str()); - } - - PPK_ASSERT_ERROR(potential_sleep_alloc.begin == when_ensured_sleep_job_finishes); - PPK_ASSERT_ERROR(potential_sleep_alloc.end == schedule.infinite_horizon()); - PPK_ASSERT_ERROR(schedule.infinite_horizon() == infinite_horizon_before); -} - -void EnergyBackfilling::awaken_machine(Schedule &schedule, int machine_id, Rational awakening_date) const -{ - MachineInformation * machine_info = _machine_informations.at(machine_id); - - // Let's find when the potential sleep job starts - auto potential_job_beginning_slice = schedule.find_last_occurence_of_job(machine_info->potential_sleep_job, schedule.begin()); - Rational previously_potential_job_beginning_time = potential_job_beginning_slice->begin; - - // Let's remove the previous potential sleep job from the schedule - schedule.remove_job_last_occurence(machine_info->potential_sleep_job); - - // If the machine can still have a potential sleep job (of reduced length), one is added into the schedule - Rational potential_sleep_maximum_length = awakening_date - previously_potential_job_beginning_time; - PPK_ASSERT_ERROR(potential_sleep_maximum_length >= 0); - - if (_debug) - { - LOG_F(1, "EnergyBackfilling::awaken_machine.\n" - "potential_sleep_maximum_length = %f\n%s", - (double) potential_sleep_maximum_length, - schedule.to_string().c_str()); - } - - if (potential_sleep_maximum_length > 0) - { - Job * job = machine_info->potential_sleep_job; - job->walltime = potential_sleep_maximum_length; - auto potential_sleep_alloc = schedule.add_job_first_fit_after_time(job, previously_potential_job_beginning_time, machine_info->limited_resource_selector); - PPK_ASSERT_ERROR(potential_sleep_alloc.begin == previously_potential_job_beginning_time); - - if (_debug) - { - LOG_F(1, "EnergyBackfilling::awaken_machine.\n" - "The potential job has been inserted back into the schedule.\n%s", - schedule.to_string().c_str()); - } - } - - // Let's wake the machine up - auto wake_up_alloc = schedule.add_job_first_fit_after_time(machine_info->switch_on_job, previously_potential_job_beginning_time, machine_info->limited_resource_selector); - PPK_ASSERT_ERROR(wake_up_alloc.begin == awakening_date); - - if (_debug) - { - LOG_F(1, "EnergyBackfilling::awaken_machine after wake up\n%s", - schedule.to_string().c_str()); - } -} - -Rational EnergyBackfilling::awaken_machine_as_soon_as_possible(Schedule &schedule, int machine_id) const -{ - MachineInformation * machine_info = _machine_informations.at(machine_id); - - // Let's find when the potential sleep job starts - auto potential_job_beginning_slice = schedule.find_last_occurence_of_job(machine_info->potential_sleep_job, schedule.begin()); - PPK_ASSERT_ERROR(potential_job_beginning_slice != schedule.end(), - "Cannot awaken machine %d: The machine is not sleeping\n", machine_id); - - Rational insertion_time = potential_job_beginning_slice->begin; - - awaken_machine(schedule, machine_id, insertion_time); - return insertion_time; -} - -EnergyBackfilling::ScheduleMetrics EnergyBackfilling::compute_metrics_of_schedule(const Schedule &schedule, Rational min_job_length) const -{ - PPK_ASSERT_ERROR(schedule.nb_slices() > 0); - ScheduleMetrics ret; - - ret.makespan = schedule.finite_horizon(); - - struct JobInfo - { - bool traversed_left = false; - bool traversed_right = false; - - Rational submission_time; - Rational starting_time; - Rational ending_time; - - Rational waiting_time; - Rational turnaround_time; - Rational slowdown; - Rational bounded_slowdown; - }; - - map<string, JobInfo> jobs_info; - - // Past -> Future traversal to find starting times - for (auto time_slice_it = schedule.begin(); time_slice_it != schedule.end(); ++time_slice_it) - { - const Schedule::TimeSlice & slice = *time_slice_it; - - for (auto mit : slice.allocated_jobs) - { - const Job * job = mit.first; - - if (!is_fake_job(job->id)) - { - if (jobs_info.count(job->id) == 0) - jobs_info[job->id] = JobInfo(); - - JobInfo & info = jobs_info[job->id]; - - // If the job has not been taken into account - if (!info.traversed_right) - { - info.submission_time = job->submission_time; - info.starting_time = slice.begin; - - info.traversed_right = true; - } - } - } - } - - // Future -> Past traversal to find expected ending times - bool should_continue_traversal = true; - auto last_slice = schedule.end(); - --last_slice; - for (auto time_slice_it = last_slice; should_continue_traversal; --time_slice_it) - { - const Schedule::TimeSlice & slice = *time_slice_it; - - for (auto mit : slice.allocated_jobs) - { - const Job * job = mit.first; - - if (! is_fake_job(job->id)) - { - JobInfo & info = jobs_info[job->id]; - - // If the job has not been taken into account - if (!info.traversed_left) - { - info.ending_time = slice.end; - - // Now that both starting and end times are known, metrics can be computed - info.waiting_time = info.starting_time - info.submission_time; - info.turnaround_time = info.ending_time - info.submission_time; - info.slowdown = info.turnaround_time / (info.ending_time - info.starting_time); - info.bounded_slowdown = info.turnaround_time / std::max(min_job_length, Rational(info.ending_time - info.starting_time)); - - // Let's compute the sum of the metrics there - ret.mean_waiting_time += info.waiting_time; - ret.mean_turnaround_time += info.turnaround_time; - ret.mean_slowdown += info.slowdown; - ret.mean_bounded_slowdown += info.bounded_slowdown; - - // The same with the max of each metrics - ret.max_waiting_time = std::max(ret.max_waiting_time, info.waiting_time); - ret.max_turnaround_time = std::max(ret.max_turnaround_time, info.turnaround_time); - ret.max_slowdown = std::max(ret.max_slowdown, info.slowdown); - ret.max_bounded_slowdown = std::max(ret.max_bounded_slowdown, info.bounded_slowdown); - - info.traversed_left = true; - } - } - } - - if (time_slice_it == schedule.begin()) - should_continue_traversal = false; - } - - // Let's divide the sum of each metrics by the number of jobs to get the mean - if (jobs_info.size() > 0) - { - ret.mean_waiting_time /= jobs_info.size(); - ret.mean_turnaround_time /= jobs_info.size(); - ret.mean_slowdown /= jobs_info.size(); - ret.mean_bounded_slowdown /= jobs_info.size(); - } - - return ret; -} - -IntervalSet EnergyBackfilling::compute_potentially_awaken_machines(const Schedule::TimeSlice &time_slice) -{ - // Computes the machines that can be awaken in one time slice. - // These machines are the one on which potential sleep jobs are allocated - - IntervalSet res; - - for (auto mit : time_slice.allocated_jobs) - { - const Job * job = mit.first; - const IntervalSet & job_machines = mit.second; - - if (is_potential_sleep_job(job->id)) - res.insert(job_machines); - } - - return res; -} - -IntervalSet EnergyBackfilling::compute_sleeping_machines(const Schedule::TimeSlice &time_slice) -{ - // Computes the machines that are sleeping in one time slice: - // - those in ensured sleep - // - those in potential sleep - - IntervalSet res; - - for (auto mit : time_slice.allocated_jobs) - { - const Job * job = mit.first; - const IntervalSet & job_machines = mit.second; - - if (is_potential_sleep_job(job->id) || is_ensured_sleep_job(job->id)) - res.insert(job_machines); - } - - return res; -} - -Rational EnergyBackfilling::find_earliest_moment_to_awaken_machines(Schedule &schedule, const IntervalSet &machines_to_awaken) const -{ - // Let's traverse the schedule Future -> Past - // As soon as we find any machine to awaken not computing a potential sleep job, - // the moment of time we seek is found. - PPK_ASSERT_ERROR(schedule.nb_slices() > 0); - - auto slice_it = schedule.end(); // Finite horizon -> Infinite horizon - --slice_it; - - // Let's make sure this method is called correctly by checking that all machines are sleeping in the last time slice - IntervalSet sleeping_machines = compute_potentially_awaken_machines(*slice_it); - PPK_ASSERT_ERROR((sleeping_machines & machines_to_awaken) == machines_to_awaken); - - do - { - if (slice_it != schedule.begin()) - --slice_it; - - sleeping_machines = compute_potentially_awaken_machines(*slice_it); - - // If all the machines are not in a potential sleep yet - if ((sleeping_machines & machines_to_awaken) != machines_to_awaken) - return slice_it->end; - - } while (slice_it != schedule.begin()); - - return schedule.first_slice_begin(); -} - -Rational EnergyBackfilling::estimate_energy_of_schedule(const Schedule &schedule, Rational horizon) const -{ - PPK_ASSERT_ERROR(horizon < schedule.infinite_horizon()); - Rational energy = 0; - - // Let's iterate the slices - for (auto slice_it = schedule.begin(); (slice_it->begin < horizon) && (slice_it != schedule.end()); ++slice_it) - { - const Schedule::TimeSlice & slice = *slice_it; - - Rational length = slice.length; - if (slice_it->end > horizon) - length = horizon - slice_it->begin; - - IntervalSet idle_machines = _all_machines; - - for (auto mit : slice.allocated_jobs) - { - const Job * job = mit.first; - const IntervalSet & mr = mit.second; - - idle_machines -= mr; - Rational job_power_in_slice = 0; - - if (is_fake_job(job->id)) - { - if (is_switch_on_job(job->id)) - for (auto machine_it = mr.elements_begin(); machine_it != mr.elements_end(); ++machine_it) - job_power_in_slice += _machine_informations.at(*machine_it)->switch_on_electrical_power; - else if (is_switch_off_job(job->id)) - for (auto machine_it = mr.elements_begin(); machine_it != mr.elements_end(); ++machine_it) - job_power_in_slice += _machine_informations.at(*machine_it)->switch_off_electrical_power; - else if (is_ensured_sleep_job(job->id)) - for (auto machine_it = mr.elements_begin(); machine_it != mr.elements_end(); ++machine_it) - job_power_in_slice += _machine_informations.at(*machine_it)->sleep_epower; - else if (is_potential_sleep_job(job->id)) - for (auto machine_it = mr.elements_begin(); machine_it != mr.elements_end(); ++machine_it) - job_power_in_slice += _machine_informations.at(*machine_it)->sleep_epower; - else - PPK_ASSERT_ERROR(false); - } - else - { - for (auto machine_it = mr.elements_begin(); machine_it != mr.elements_end(); ++machine_it) - job_power_in_slice += _machine_informations.at(*machine_it)->compute_epower; - } - - energy += job_power_in_slice * length; - } - - // Let's add the energy of idle machines - for (auto machine_it = idle_machines.elements_begin(); machine_it != idle_machines.elements_end(); ++ machine_it) - { - int machine_id = *machine_it; - MachineInformation * minfo = _machine_informations.at(machine_id); - - energy += minfo->idle_epower * length; - } - } - - return energy; -} - -bool EnergyBackfilling::is_switch_on_job(const std::string & job_id) -{ - return boost::starts_with(job_id, "fakejob_son_"); -} - -bool EnergyBackfilling::is_switch_off_job(const std::string & job_id) -{ - return boost::starts_with(job_id, "fakejob_soff_"); -} - -bool EnergyBackfilling::is_ensured_sleep_job(const std::string & job_id) -{ - return boost::starts_with(job_id, "fakejob_esleep_"); -} - -bool EnergyBackfilling::is_potential_sleep_job(const std::string & job_id) -{ - return boost::starts_with(job_id, "fakejob_psleep_"); -} - -bool EnergyBackfilling::is_fake_job(const std::string & job_id) -{ - return boost::starts_with(job_id, "fakejob_"); -} - -bool EnergyBackfilling::contains_any_fake_job(const Schedule &schedule) -{ - for (auto slice_it = schedule.begin(); slice_it != schedule.end(); ++slice_it) - { - for (auto mit : slice_it->allocated_jobs) - { - const Job * job = mit.first; - if (is_fake_job(job->id)) - return true; - } - } - return false; -} - -bool EnergyBackfilling::contains_any_nonfake_job(const Schedule &schedule) -{ - for (auto slice_it = schedule.begin(); slice_it != schedule.end(); ++slice_it) - { - for (auto mit : slice_it->allocated_jobs) - { - const Job * job = mit.first; - if (!is_fake_job(job->id)) - return true; - } - } - return false; -} diff --git a/src/algo/energy_bf.hpp b/src/algo/energy_bf.hpp deleted file mode 100644 index cba51a36e7d5a13eb00f023f5d56b498f546d133..0000000000000000000000000000000000000000 --- a/src/algo/energy_bf.hpp +++ /dev/null @@ -1,180 +0,0 @@ -#pragma once - -#include <list> -#include <map> - -#include "../isalgorithm.hpp" -#include "../json_workload.hpp" -#include "../locality.hpp" -#include "../schedule.hpp" - -class EnergyBackfilling : public ISchedulingAlgorithm -{ -public: - enum MachineState - { - AWAKE, - ASLEEP, - SWITCHING_OFF, - SWITCHING_ON - }; - - struct ScheduleMetrics - { - Rational makespan = 0; - - Rational mean_waiting_time = 0; - Rational max_waiting_time = 0; - - Rational mean_turnaround_time = 0; - Rational max_turnaround_time = 0; - - Rational mean_slowdown = 0; - Rational max_slowdown = 0; - - Rational mean_bounded_slowdown = 0; - Rational max_bounded_slowdown = 0; - }; - - std::string machine_state_to_string(const MachineState & state); - - struct MachineInformation - { - MachineInformation(int machine_number); - ~MachineInformation(); - - void create_jobs(double rjms_delay, - Rational ensured_sleep_time_lower_bound, - Rational ensured_sleep_time_upper_bound); - void free_jobs(); - - private: - void create_selector(); - void free_selector(); - - public: - int machine_number = -1; //! The machine number the MachineInformation corresponds to - LimitedRangeResourceSelector * limited_resource_selector = nullptr; - MachineState state = AWAKE; - - int compute_pstate = 0; - int sleep_pstate = 1; - - Rational compute_epower = 0; - Rational idle_epower = 0; - Rational sleep_epower = 0; - - Rational switch_on_seconds = 0; - Rational switch_on_energy = 0; - Rational switch_on_electrical_power = 0; - - Rational switch_off_seconds = 0; - Rational switch_off_energy = 0; - Rational switch_off_electrical_power = 0; - - Job * switch_on_job = nullptr; //! This job corresponds to the switching ON state of the machine - Job * switch_off_job = nullptr; //! This job corresponds to the switching OFF state of the machine - - Job * ensured_sleep_job = nullptr; //! This job corresponds to the sleeping state of the machine. It cannot be stopped. It is used to avoid pure loss of energy via too frequent switches OFF and ON - Job * potential_sleep_job = nullptr; //! This job corresponds to the sleeping state of the machine. It can be stopped if a job cannot fit in the schedule otherwise - }; - -public: - EnergyBackfilling(Workload * workload, SchedulingDecision * decision, Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options); - virtual ~EnergyBackfilling(); - - virtual void on_simulation_start(double date, const rapidjson::Value & batsim_config); - - virtual void on_simulation_end(double date); - - virtual void on_machine_state_changed(double date, IntervalSet machines, int newState); - - virtual void on_requested_call(double date); - - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); - -protected: - void generate_machine_informations(int nb_machines); - void clear_machine_informations(); - - void make_decisions_of_schedule(const Schedule & schedule, bool run_call_me_later_on_nothing_to_do = true); - - void update_first_slice_taking_sleep_jobs_into_account(Rational date); - - void put_jobs_into_schedule(Schedule & schedule) const; - - /** - * @brief Sedates machines as soon as they remain idle. - * @param[in] schedule The Schedule into which the sedatings should be done - * @param[in] machines_to_sedate The machines to sedate - * @return The earliest sedating decision date - */ - Rational sedate_machines_at_the_furthest_moment(Schedule & schedule, - const IntervalSet & machines_to_sedate) const; - void sedate_machine(Schedule & schedule, - int machine_id, - std::list<Schedule::TimeSlice>::iterator time_slice, - bool insert_in_slice = true) const; - void sedate_machine_without_switch(Schedule & schedule, - int machine_id, - Rational when_it_should_start) const; - - void awaken_machine(Schedule & schedule, - int machine_id, - Rational awakening_date) const; - - /** - * @brief Awakens a machine as soon as possible - * @param[in] schedule The Schedule into which the awakening should be done - * @param[in] machine_id The machine to awaken - * @return The moment at which the machine should be awakened - */ - Rational awaken_machine_as_soon_as_possible(Schedule & schedule, - int machine_id) const; - - ScheduleMetrics compute_metrics_of_schedule(const Schedule & schedule, Rational min_job_length = 30) const; - - /** - * @brief Computes the machines that can be awakened inside a given time slice. - * @details These machines are the ones on which potential sleep jobs are allocated - * @param[in] time_slice The time slice - * @return The machines that can be awaked inside the given time slice - */ - static IntervalSet compute_potentially_awaken_machines(const Schedule::TimeSlice & time_slice); - static IntervalSet compute_sleeping_machines(const Schedule::TimeSlice & time_slice); - - Rational find_earliest_moment_to_awaken_machines(Schedule & schedule, const IntervalSet & machines_to_awaken) const; - - Rational estimate_energy_of_schedule(const Schedule & schedule, Rational horizon) const; - - static bool is_switch_on_job(const std::string & job_id); - static bool is_switch_off_job(const std::string & job_id); - static bool is_ensured_sleep_job(const std::string & job_id); - static bool is_potential_sleep_job(const std::string & job_id); - static bool is_fake_job(const std::string & job_id); - - static bool contains_any_fake_job(const Schedule & schedule); - static bool contains_any_nonfake_job(const Schedule & schedule); - -protected: - Schedule _schedule; - bool _debug = false; - - int _nb_call_me_later_running = 0; - - int _nb_jobs_submitted = 0; - int _nb_jobs_completed = 0; - - std::map<int, MachineInformation*> _machine_informations; - - IntervalSet _all_machines; //!< All the machines that can be used for computing jobs - IntervalSet _switching_on_machines; //!< The machines currently being switched ON - IntervalSet _switching_off_machines; //!< The machines currently being switched OFF - IntervalSet _asleep_machines; //!< The machines currently in a sleepy state. They cannot be used to compute jobs now. This is the union of _wakable_asleep_machines and _non_wakable_asleep_machines - IntervalSet _wakable_asleep_machines; //!< Subset of _asleep_machines. Those machines have been sleeping for enough time, they can be awakened. - IntervalSet _non_wakable_asleep_machines; //!< Subset of _asleep_machines. Those machines have NOT been sleeping for enough time, they cannot be awakened yet. - IntervalSet _awake_machines; //!< The machines currently in a computation pstate. They can represent the machines which compute jobs, or idle machines. -}; diff --git a/src/algo/energy_bf_dicho.cpp b/src/algo/energy_bf_dicho.cpp deleted file mode 100644 index b8fb35718c0bca51066e8ab54281f8b718588895..0000000000000000000000000000000000000000 --- a/src/algo/energy_bf_dicho.cpp +++ /dev/null @@ -1,292 +0,0 @@ -#include "energy_bf_dicho.hpp" - -#include <loguru.hpp> - -#include "../pempek_assert.hpp" - -using namespace std; - -EnergyBackfillingDichotomy::EnergyBackfillingDichotomy(Workload *workload, SchedulingDecision *decision, Queue *queue, - ResourceSelector *selector, double rjms_delay, - rapidjson::Document *variant_options) : - EnergyBackfilling(workload, decision, queue, selector, rjms_delay, variant_options) -{ - _str_to_comparison_type["switch_on"] = SWITCH_ON; - _str_to_comparison_type["remove_sleep_jobs"] = REMOVE_SLEEP_JOBS; - - // Let's get the tolerated slowdown loss ratio from the variant options - PPK_ASSERT_ERROR(variant_options->HasMember("tolerated_slowdown_loss_ratio"), - "Invalid options JSON object: Member 'tolerated_slowdown_loss_ratio' cannot be found"); - PPK_ASSERT_ERROR((*variant_options)["tolerated_slowdown_loss_ratio"].IsNumber(), - "Invalid options JSON object: Member 'tolerated_slowdown_loss_ratio' should be a number"); - _tolerated_slowdown_loss_ratio = (*variant_options)["tolerated_slowdown_loss_ratio"].GetDouble(); - PPK_ASSERT_ERROR(_tolerated_slowdown_loss_ratio >= 0, - "Invalid options JSON object: Member 'tolerated_slowdown_loss_ratio' should be positive or null (got %g)", - (double) _tolerated_slowdown_loss_ratio); - - if (variant_options->HasMember("comparison_type")) - { - PPK_ASSERT_ERROR((*variant_options)["comparison_type"].IsString(), - "Invalid options JSON object: Member 'comparison_type' should be a string"); - string comp_type = (*variant_options)["comparison_type"].GetString(); - PPK_ASSERT_ERROR(_str_to_comparison_type.count(comp_type) == 1, - "Invalid options JSON object: invalid 'comparison_type' value (%s)", - comp_type.c_str()); - _comparison_type = _str_to_comparison_type.at(comp_type); - } -} - -EnergyBackfillingDichotomy::~EnergyBackfillingDichotomy() -{ - -} - -void EnergyBackfillingDichotomy::make_decisions(double date, - SortableJobOrder::UpdateInformation *update_info, - SortableJobOrder::CompareInformation *compare_info) -{ - // Let's remove finished jobs from the schedule - for (const string & ended_job_id : _jobs_ended_recently) - { - const Job * ended_job = (*_workload)[ended_job_id]; - ++_nb_jobs_completed; - - PPK_ASSERT_ERROR(_schedule.contains_job(ended_job), - "Invalid schedule: job '%s' just finished, " - "but it not in the schedule...\n%s", - ended_job_id.c_str(), _schedule.to_string().c_str()); - PPK_ASSERT_ERROR(!_queue->contains_job(ended_job), - "Job '%s' just ended, but it is still in the " - "queue...\nQueue : %s", - ended_job_id.c_str(), - _queue->to_string().c_str()); - - // Let's remove the finished job from the schedule - _schedule.remove_job(ended_job); - } - - // Let's handle recently released jobs - for (const string & new_job_id : _jobs_released_recently) - { - const Job * new_job = (*_workload)[new_job_id]; - ++_nb_jobs_submitted; - - PPK_ASSERT_ERROR(!_schedule.contains_job(new_job), - "Invalid schedule: job '%s' already exists.\n%s", - new_job->id.c_str(), _schedule.to_string().c_str()); - PPK_ASSERT_ERROR(!_queue->contains_job(new_job), - "Job '%s' is already in the queue!\nQueue:%s", - new_job->id.c_str(), _queue->to_string().c_str()); - - if (new_job->nb_requested_resources > _nb_machines) - { - _decision->add_reject_job(new_job_id, date); - ++_nb_jobs_completed; - } - else - _queue->append_job(new_job, update_info); - } - - // Let's update the first slice of the schedule - update_first_slice_taking_sleep_jobs_into_account(date); - - // Let's update the sorting of the queue - _queue->sort_queue(update_info, compare_info); - - // Let's find the schedule which: - // - minimises the estimated energy consumption - // - respects the slowdown constraint: not worse in avg bds (for jobs in schedule) than - // tolerated_slowdown_loss_ratio times the bds of the schedule in which all nodes are awaken first. - - // ******************************************************************* - // Let's compute global informations about the current online schedule - // ******************************************************************* - const Schedule::TimeSlice & online_first_slice = *_schedule.begin(); - IntervalSet sleeping_machines = compute_sleeping_machines(online_first_slice); - IntervalSet awakenable_sleeping_machines = compute_potentially_awaken_machines(online_first_slice); - const IntervalSet & available_machines = online_first_slice.available_machines; - - // **************************************************************** - // Let's first compute the schedule in which all nodes are awakened - // **************************************************************** - Schedule awakened_schedule = _schedule; - - if (_comparison_type == SWITCH_ON) - { - // Let's awaken every machine at the earliest moment for each machine - for (auto machine_it = sleeping_machines.elements_begin(); machine_it != sleeping_machines.elements_end(); ++machine_it) - { - int machine_id = *machine_it; - Rational wake_up_moment = find_earliest_moment_to_awaken_machines(awakened_schedule, IntervalSet(machine_id)); - awaken_machine(awakened_schedule, machine_id, wake_up_moment); - } - } - else if (_comparison_type == REMOVE_SLEEP_JOBS) - { - // Let's simply remove sleep jobs from the schedule! - for (auto machine_it = sleeping_machines.elements_begin(); machine_it != sleeping_machines.elements_end(); ++machine_it) - { - int machine_id = *machine_it; - MachineInformation *minfo = _machine_informations.at(machine_id); - while (awakened_schedule.remove_job_if_exists(minfo->ensured_sleep_job)); - while (awakened_schedule.remove_job_if_exists(minfo->potential_sleep_job)); - } - } - - // Let's add jobs into the schedule - put_jobs_into_schedule(awakened_schedule); - - // ********************************************************************************* - // Let's determine if the online schedule respects our avg bds constraint. - // If so, we should try to sedate machines. Otherwise, we should try to awaken some. - // ********************************************************************************* - Schedule online_schedule = _schedule; - put_jobs_into_schedule(online_schedule); - - //Rational online_awakened_comparison_horizon = max(awakened_schedule.finite_horizon(), online_schedule.finite_horizon()); - - // Let's compute the jobs metrics and energy of the two schedules - ScheduleMetrics online_sched_metrics = compute_metrics_of_schedule(online_schedule); - ScheduleMetrics awakened_sched_metrics = compute_metrics_of_schedule(awakened_schedule); - //Rational online_sched_energy = estimate_energy_of_schedule(online_schedule, online_awakened_comparison_horizon); - //Rational awakened_sched_energy = estimate_energy_of_schedule(awakened_schedule, online_awakened_comparison_horizon); - - bool should_sedate_machines = false; - if (online_sched_metrics.mean_slowdown <= awakened_sched_metrics.mean_slowdown * _tolerated_slowdown_loss_ratio) - should_sedate_machines = true; - - // ***************************************************************************************** - // Let's do a dichotomy on the number of machines to awaken/sedate to find the best solution - // ***************************************************************************************** - - LOG_F(INFO, "should_sedate_machines=%d", should_sedate_machines); - - if (should_sedate_machines) - { - // Sleeping machines are not available since they compute "fake" jobs - IntervalSet sedatable_machines = available_machines; - - int nb_to_sedate_min = 1; // Online schedule is nb_to_sedate = 0 - int nb_to_sedate_max = sedatable_machines.size(); - - Schedule best_schedule = online_schedule; - ScheduleMetrics best_sched_metrics = online_sched_metrics; - //Rational best_sched_energy = online_sched_energy; - int best_nb_to_sedate = 0; - - // Dichotomy - while (nb_to_sedate_min < nb_to_sedate_max) - { - // Select machines to sedate - int nb_to_sedate = (nb_to_sedate_min + nb_to_sedate_max) / 2; - - IntervalSet machines_to_sedate; - _selector->select_resources_to_sedate(nb_to_sedate, available_machines, sedatable_machines, machines_to_sedate); - - // Create the schedule with the desired sedated machines - Schedule schedule = _schedule; - - for (auto machine_it = machines_to_sedate.elements_begin(); machine_it != machines_to_sedate.elements_end(); ++machine_it) - { - int machine_id = *machine_it; - sedate_machine(schedule, machine_id, schedule.begin()); - } - - put_jobs_into_schedule(schedule); - - // Let's compute jobs metrics about the current schedule - ScheduleMetrics sched_metrics = compute_metrics_of_schedule(schedule); - - // If the schedule respects the avg slowdown constraint - if (sched_metrics.mean_slowdown <= awakened_sched_metrics.mean_slowdown * _tolerated_slowdown_loss_ratio) - { - // Let's compute the energy of both schedules to compare them - Rational comparison_horizon = max(best_schedule.finite_horizon(), schedule.finite_horizon()); - Rational best_sched_energy = estimate_energy_of_schedule(best_schedule, comparison_horizon); - Rational sched_energy = estimate_energy_of_schedule(schedule, comparison_horizon); - - LOG_F(INFO, "Current schedule respects the mean slowdown constraint. " - "(best_energy, curr_energy) : (%g, %g)", - (double)best_sched_energy, (double)sched_energy); - - // Let's update the best solution if needed - if ((sched_energy < best_sched_energy) || - ((sched_energy == best_sched_energy) && (nb_to_sedate > best_nb_to_sedate))) - { - best_schedule = schedule; - best_sched_metrics = sched_metrics; - best_sched_energy = sched_energy; - best_nb_to_sedate = nb_to_sedate; - } - - nb_to_sedate_min = nb_to_sedate + 1; - } - else - nb_to_sedate_max = nb_to_sedate - 1; - } - - // Let's apply the decisions of the best schedule - make_decisions_of_schedule(best_schedule); - } - else - { - int nb_to_awaken_min = 1; // online schedule is nb_to_awaken = 0 - int nb_to_awaken_max = awakenable_sleeping_machines.size(); - - Schedule best_schedule = online_schedule; - ScheduleMetrics best_sched_metrics = online_sched_metrics; - int best_nb_to_awaken = 0; - - // Dichotomy - while (nb_to_awaken_min < nb_to_awaken_max) - { - // Select machines to awaken - int nb_to_awaken = (nb_to_awaken_min + nb_to_awaken_max) / 2; - IntervalSet machines_to_awaken; - _selector->select_resources_to_awaken(nb_to_awaken, available_machines, awakenable_sleeping_machines, machines_to_awaken); - - // Create the schedule with the desired awakened machines - Schedule schedule = _schedule; - - for (auto machine_it = machines_to_awaken.elements_begin(); machine_it != machines_to_awaken.elements_end(); ++machine_it) - { - int machine_id = *machine_it; - Rational wake_up_date = find_earliest_moment_to_awaken_machines(schedule, IntervalSet(machine_id)); - awaken_machine(schedule, machine_id, wake_up_date); - } - - put_jobs_into_schedule(schedule); - - ScheduleMetrics sched_metrics = compute_metrics_of_schedule(schedule); - - // If the schedule respects our avg slowdown constraint - if (sched_metrics.mean_slowdown <= awakened_sched_metrics.mean_slowdown * _tolerated_slowdown_loss_ratio) - { - // Let's compute the energy of both schedules to compare them - Rational comparison_horizon = max(best_schedule.finite_horizon(), schedule.finite_horizon()); - Rational best_sched_energy = estimate_energy_of_schedule(best_schedule, comparison_horizon); - Rational sched_energy = estimate_energy_of_schedule(schedule, comparison_horizon); - - LOG_F(INFO, "Current schedule respects the mean slowdown constraint. " - "(best_energy, curr_energy) : (%g, %g)", - (double)best_sched_energy, (double)sched_energy); - - // Let's update the best solution if needed - if ((sched_energy < best_sched_energy) || - ((sched_energy == best_sched_energy) && (nb_to_awaken < best_nb_to_awaken))) - { - best_schedule = schedule; - best_sched_metrics = sched_metrics; - best_nb_to_awaken = nb_to_awaken; - } - - nb_to_awaken_max = nb_to_awaken - 1; - } - else - nb_to_awaken_min = nb_to_awaken + 1; - } - - // Let's apply the decisions of the best schedule - make_decisions_of_schedule(best_schedule); - } -} diff --git a/src/algo/energy_bf_dicho.hpp b/src/algo/energy_bf_dicho.hpp deleted file mode 100644 index a1031d6b894784b91d9d87d706e974b191fe39f7..0000000000000000000000000000000000000000 --- a/src/algo/energy_bf_dicho.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "energy_bf.hpp" - -class EnergyBackfillingDichotomy : public EnergyBackfilling -{ - enum AwakeningComparisonType - { - SWITCH_ON, - REMOVE_SLEEP_JOBS - }; - -public: - EnergyBackfillingDichotomy(Workload * workload, SchedulingDecision * decision, Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options); - virtual ~EnergyBackfillingDichotomy(); - -protected: - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); - -private: - Rational _tolerated_slowdown_loss_ratio; - AwakeningComparisonType _comparison_type = REMOVE_SLEEP_JOBS; - - std::map<std::string, AwakeningComparisonType> _str_to_comparison_type; -}; diff --git a/src/algo/energy_bf_idle_sleeper.cpp b/src/algo/energy_bf_idle_sleeper.cpp deleted file mode 100644 index 0bc195be469cf7f90bb96f79a1bd966f1cedcfb9..0000000000000000000000000000000000000000 --- a/src/algo/energy_bf_idle_sleeper.cpp +++ /dev/null @@ -1,239 +0,0 @@ -#include "energy_bf_idle_sleeper.hpp" - -#include <loguru.hpp> - -#include "../pempek_assert.hpp" - -using namespace std; - -EnergyBackfillingIdleSleeper::EnergyBackfillingIdleSleeper(Workload *workload, - SchedulingDecision *decision, - Queue *queue, - ResourceSelector *selector, - double rjms_delay, - rapidjson::Document *variant_options) : - EnergyBackfillingMonitoringInertialShutdown(workload, decision, queue, selector, rjms_delay, variant_options) -{ -} - -EnergyBackfillingIdleSleeper::~EnergyBackfillingIdleSleeper() -{ - -} - -void EnergyBackfillingIdleSleeper::on_monitoring_stage(double date) -{ - update_first_slice_taking_sleep_jobs_into_account(date); - _inertial_schedule = _schedule; - - update_idle_states(date, _inertial_schedule, _all_machines, _idle_machines, - _machines_idle_start_date); - make_idle_sleep_decisions(date); -} - -void EnergyBackfillingIdleSleeper::select_idle_machines_to_sedate(Rational current_date, - const IntervalSet &idle_machines, - const IntervalSet &machines_awake_soon, - const Job *priority_job, - const std::map<int, Rational> idle_machines_start_date, - Rational minimum_idle_time_to_sedate, - IntervalSet &machines_to_sedate, - bool take_priority_job_into_account) -{ - int nb_awake_soon = machines_awake_soon.size(); - - int nb_needed_for_priority_job = 0; - if (priority_job != nullptr && take_priority_job_into_account) - nb_needed_for_priority_job = priority_job->nb_requested_resources; - - Rational sedate_thresh = current_date - minimum_idle_time_to_sedate; - IntervalSet sedatable_idle_machines; - - for (auto machine_it = idle_machines.elements_begin(); - machine_it != idle_machines.elements_end(); - ++machine_it) - { - const int machine_id = *machine_it; - if (idle_machines_start_date.at(machine_id) <= sedate_thresh) - sedatable_idle_machines.insert(machine_id); - } - - // To avoid blocking the priority job with switches OFF, let's reduce - // the number of machines to switch OFF if needed. - int nb_machines_to_sedate = max(0, min(nb_awake_soon - nb_needed_for_priority_job, - (int)sedatable_idle_machines.size())); - - machines_to_sedate.clear(); - if (nb_machines_to_sedate > 0) - machines_to_sedate = sedatable_idle_machines.left(nb_machines_to_sedate); -} - -void EnergyBackfillingIdleSleeper::select_idle_machines_to_awaken(const Queue *queue, - const Schedule &schedule, - ResourceSelector * priority_job_selector, - const IntervalSet &idle_machines, - AwakeningPolicy policy, - int maximum_nb_machines_to_awaken, - IntervalSet &machines_to_awaken, - bool take_priority_job_into_account) -{ - PPK_ASSERT_ERROR(maximum_nb_machines_to_awaken >= 0); - machines_to_awaken.clear(); - - // If there are no jobs to compute, there is nothing more to do. - if (queue->nb_jobs() <= 0) - return; - - // If we only awaken for the priority job, this is already done by Inertial Shutdown. - if (policy == AWAKEN_FOR_PRIORITY_JOB_ONLY) - return; - - Schedule schedule_copy = schedule; - - // Let's try to backfill some jobs into the awakenable machines, and wake them up if needed. - IntervalSet awakable_machines = compute_potentially_awaken_machines(*schedule_copy.begin()); - - if (awakable_machines.size() > (unsigned int)maximum_nb_machines_to_awaken) - awakable_machines = awakable_machines.left(maximum_nb_machines_to_awaken); - - IntervalSet usable_machines = idle_machines + awakable_machines; - IntervalSet usable_idle_machines = idle_machines; - - // Let's find the priority job and related stuff to avoid penalizing the priority job - - const Job * priority_job; - bool priority_job_needs_awakenings; - Schedule::JobAlloc priority_job_alloc; - IntervalSet priority_job_reserved_machines; - IntervalSet machines_that_can_be_used_by_the_priority_job; - compute_priority_job_and_related_stuff(schedule_copy, queue, priority_job, - priority_job_selector, - priority_job_needs_awakenings, - priority_job_alloc, - priority_job_reserved_machines, - machines_that_can_be_used_by_the_priority_job); - - if (policy == AWAKEN_FOR_ALL_JOBS_RESPECTING_PRIORITY_JOB && take_priority_job_into_account) - { - // Let's remove the priority_job_reserved_machines from the usable_machines - usable_idle_machines -= priority_job_reserved_machines; - usable_machines -= priority_job_reserved_machines; - } - - auto job_it = queue->begin(); - while (usable_machines.size() > 0 && job_it != queue->end()) - { - const Job * job = (*job_it)->job; - if (job->nb_requested_resources <= (int)usable_machines.size()) - { - IntervalSet machines_to_use_for_this_job; - - if (usable_idle_machines.size() > 0) - { - int nb_idle_machines_to_use = min((int)usable_idle_machines.size(), job->nb_requested_resources); - machines_to_use_for_this_job = usable_idle_machines.left(nb_idle_machines_to_use); - usable_idle_machines -= machines_to_use_for_this_job; - } - - machines_to_use_for_this_job += usable_machines.left(job->nb_requested_resources - (int)machines_to_use_for_this_job.size()); - IntervalSet machines_to_awaken_for_this_job = (awakable_machines & machines_to_use_for_this_job); - - usable_machines -= machines_to_use_for_this_job; - awakable_machines -= machines_to_awaken_for_this_job; - machines_to_awaken += machines_to_awaken_for_this_job; - } - - ++job_it; - } - - PPK_ASSERT_ERROR((int)machines_to_awaken.size() <= maximum_nb_machines_to_awaken); -} - -void EnergyBackfillingIdleSleeper::update_idle_states(Rational current_date, - const Schedule & schedule, - const IntervalSet & all_machines, - IntervalSet & idle_machines, - map<int,Rational> & machines_idle_start_date) -{ - PPK_ASSERT_ERROR(schedule.nb_slices() > 0); - PPK_ASSERT_ERROR(schedule.first_slice_begin() == Rational(current_date)); - - const Schedule::TimeSlice & slice = *schedule.begin(); - - IntervalSet machines_newly_available = (slice.available_machines & (all_machines - idle_machines)); - - for (auto machine_it = machines_newly_available.elements_begin(); - machine_it != machines_newly_available.elements_end(); - ++machine_it) - { - int machine_id = *machine_it; - machines_idle_start_date[machine_id] = current_date; - } - - IntervalSet machines_newly_busy = ((all_machines - slice.available_machines) & idle_machines); - - for (auto machine_it = machines_newly_busy.elements_begin(); - machine_it != machines_newly_busy.elements_end(); - ++machine_it) - { - int machine_id = *machine_it; - machines_idle_start_date[machine_id] = schedule.infinite_horizon(); - } - - PPK_ASSERT_ERROR((machines_newly_available & machines_newly_busy) == IntervalSet::empty_interval_set(), - "machines_newly_available=%s. machines_newly_busy=%s", - machines_newly_available.to_string_brackets().c_str(), - machines_newly_busy.to_string_brackets().c_str()); - - PPK_ASSERT_ERROR((machines_newly_available & idle_machines) == IntervalSet::empty_interval_set(), - "machines_newly_available=%s. _idle_machines=%s", - machines_newly_available.to_string_brackets().c_str(), - idle_machines.to_string_brackets().c_str()); - - PPK_ASSERT_ERROR((machines_newly_busy & idle_machines) == machines_newly_busy, - "machines_newly_busy=%s. _idle_machines=%s", - machines_newly_busy.to_string_brackets().c_str(), - idle_machines.to_string_brackets().c_str()); - - idle_machines += machines_newly_available; - idle_machines -= machines_newly_busy; -} - -void EnergyBackfillingIdleSleeper::make_idle_sleep_decisions(double date) -{ - const Job * priority_job = _queue->first_job_or_nullptr(); - IntervalSet machines_awake_soon = (_awake_machines + _switching_on_machines - _switching_off_machines) - + _machines_to_awaken - _machines_to_sedate; - - IntervalSet machines_to_sedate; - select_idle_machines_to_sedate(date, _idle_machines, machines_awake_soon, - priority_job, _machines_idle_start_date, - _needed_amount_of_idle_time_to_be_sedated, - machines_to_sedate); - - if (machines_to_sedate.size() > 0) - { - _machines_to_sedate += machines_to_sedate; - _nb_machines_sedated_for_being_idle += machines_to_sedate.size(); - - IntervalSet machines_sedated_this_turn, machines_awakened_this_turn; - handle_queued_switches(_inertial_schedule, _machines_to_sedate, _machines_to_awaken, - machines_sedated_this_turn, machines_awakened_this_turn); - - if (machines_sedated_this_turn.size() > 0) - LOG_F(INFO, "Date=%g. Those machines should be put to sleep now: %s", - date, machines_sedated_this_turn.to_string_brackets().c_str()); - if (machines_awakened_this_turn.size() > 0) - LOG_F(INFO, "Date=%g. Those machines should be awakened now: %s", - date, machines_awakened_this_turn.to_string_brackets().c_str()); - - _machines_to_sedate -= machines_sedated_this_turn; - _machines_to_awaken -= machines_awakened_this_turn; - _machines_sedated_since_last_monitoring_stage_inertia = machines_sedated_this_turn; - _machines_awakened_since_last_monitoring_stage_inertia = machines_awakened_this_turn; - - PPK_ASSERT_ERROR((_machines_to_awaken & _machines_to_sedate) == IntervalSet::empty_interval_set()); - - make_decisions_of_schedule(_inertial_schedule, false); - } -} diff --git a/src/algo/energy_bf_idle_sleeper.hpp b/src/algo/energy_bf_idle_sleeper.hpp deleted file mode 100644 index cf45001fdc141a7881b925f80c4a0be428171ca0..0000000000000000000000000000000000000000 --- a/src/algo/energy_bf_idle_sleeper.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include "energy_bf_monitoring_inertial_shutdown.hpp" - -#include <map> - -class EnergyBackfillingIdleSleeper : public EnergyBackfillingMonitoringInertialShutdown -{ -public: - enum AwakeningPolicy - { - AWAKEN_FOR_PRIORITY_JOB_ONLY, - AWAKEN_FOR_ALL_JOBS_WITHOUT_RESPECTING_PRIORITY_JOB, - AWAKEN_FOR_ALL_JOBS_RESPECTING_PRIORITY_JOB - }; - -public: - EnergyBackfillingIdleSleeper(Workload * workload, SchedulingDecision * decision, - Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options); - - virtual ~EnergyBackfillingIdleSleeper(); - - virtual void on_monitoring_stage(double date); - -public: - /** - * @brief Selects which machines should be sedated for being idle for too long - * @param[in] current_date The current date - * @param[in] idle_machines The machines currently idle - * @param[in] machines_awake_soon The machines that will be awake soon (those awake now and those switching ON) - * @param[in] priority_job The priority job (nullptr if there is no priority job) - * @param[in] idle_machines_start_date A map which associates machine_ids to the starting time of their idle state - * @param[in] minimum_idle_time_to_sedate The machines must be idle for a longer time than idle_sedate_thresh to be sedated - * @param[out] machines_to_sedate The machines that can be sedated for being idle for too long (output parameter) - */ - static void select_idle_machines_to_sedate(Rational current_date, - const IntervalSet & idle_machines, - const IntervalSet & machines_awake_soon, - const Job * priority_job, - const std::map<int, Rational> idle_machines_start_date, - Rational minimum_idle_time_to_sedate, - IntervalSet & machines_to_sedate, - bool take_priority_job_into_account = true); - - /** - * @brief Selects which machines should be awakened to compute some jobs - * @param[in] queue The queue which contains jobs (or not) - * @param[in] schedule The current schedule. It should not be modified by this function. - * @param[in,out] priority_job_selector The ResourceSelector to insert the priority_job into the schedule - * @param[in] idle_machines The machines currently idle - * @param[in] policy The AwakeningPolicy to apply - * @param[in] maximum_nb_machines_to_awaken The maximum number of machines to awaken for this call - * @param[out] machines_to_awaken The machines that can be awakened to execute jobs (output parameter) - */ - static void select_idle_machines_to_awaken(const Queue *queue, - const Schedule &schedule, - ResourceSelector * priority_job_selector, - const IntervalSet &idle_machines, - AwakeningPolicy policy, - int maximum_nb_machines_to_awaken, - IntervalSet &machines_to_awaken, - bool take_priority_job_into_account = true); - - /** - * @brief Updates whichever machines are idle and their idle starting time if needed, - * based on the first slice of the given schedule - * @param[in] current_date The current date - * @param[in] schedule The current schedule. Its first slice must reflect the platform usage - * @param[in] all_machines All computing machines - * @param[in,out] idle_machines The machines currently being idle - * @param[in,out] machines_idle_start_date The starting time of the idle period of the idle machines - */ - static void update_idle_states(Rational current_date, - const Schedule & schedule, - const IntervalSet & all_machines, - IntervalSet & idle_machines, - std::map<int,Rational> & machines_idle_start_date); - -protected: - void make_idle_sleep_decisions(double date); -}; diff --git a/src/algo/energy_bf_machine_subpart_sleeper.cpp b/src/algo/energy_bf_machine_subpart_sleeper.cpp deleted file mode 100644 index 5425d3c5b4d916f0facd239e065dd66785feaf76..0000000000000000000000000000000000000000 --- a/src/algo/energy_bf_machine_subpart_sleeper.cpp +++ /dev/null @@ -1,271 +0,0 @@ -#include "energy_bf_machine_subpart_sleeper.hpp" - -#include <loguru.hpp> - -#include "energy_bf_idle_sleeper.hpp" -#include "../pempek_assert.hpp" - - -using namespace std; - -EnergyBackfillingMachineSubpartSleeper::EnergyBackfillingMachineSubpartSleeper(Workload *workload, SchedulingDecision *decision, - Queue *queue, ResourceSelector *selector, - double rjms_delay, rapidjson::Document *variant_options) : - EnergyBackfillingMonitoringInertialShutdown(workload, decision, queue, selector, rjms_delay, variant_options) -{ - PPK_ASSERT_ERROR(variant_options->HasMember("fraction_of_machines_to_let_awake"), - "Invalid options JSON object: Member 'fraction_of_machines_to_let_awake' cannot be found"); - PPK_ASSERT_ERROR((*variant_options)["fraction_of_machines_to_let_awake"].IsNumber(), - "Invalid options JSON object: Member 'fraction_of_machines_to_let_awake' must be a number"); - _fraction_of_machines_to_let_awake = (*variant_options)["fraction_of_machines_to_let_awake"].GetDouble(); - - PPK_ASSERT_ERROR(_fraction_of_machines_to_let_awake >= 0 && _fraction_of_machines_to_let_awake <= 1, - "Invalid options JSON object: Member 'fraction_of_machines_to_let_awake' has an invalid " - "value (%g)", (double) _fraction_of_machines_to_let_awake); - - LOG_SCOPE_FUNCTION(INFO); - LOG_F(INFO, "Fraction of machines to let awake: %g", - (double) _fraction_of_machines_to_let_awake); -} - -EnergyBackfillingMachineSubpartSleeper::~EnergyBackfillingMachineSubpartSleeper() -{ - -} - -void EnergyBackfillingMachineSubpartSleeper::on_monitoring_stage(double date) -{ - update_first_slice_taking_sleep_jobs_into_account(date); - _inertial_schedule = _schedule; - - // Let's check whether the asleep machines are consistent - IntervalSet machines_asleep_soon = (_asleep_machines + _switching_off_machines - _switching_on_machines) - + _machines_to_sedate - _machines_to_awaken; - PPK_ASSERT_ERROR((int)machines_asleep_soon.size() == _nb_machines_sedated_by_inertia + - _nb_machines_sedated_for_being_idle, - "Asleep machines inconsistency. nb_asleep_soon=%d (%s). nb_sedated_inertia=%d. " - "nb_sedated_idle=%d\n", - (int)machines_asleep_soon.size(), machines_asleep_soon.to_string_brackets().c_str(), - _nb_machines_sedated_by_inertia, _nb_machines_sedated_for_being_idle); - - // Let's clear the machines to sedate, to possibly take new ones which would be sedated before - if (_machines_to_sedate.size() > 0) - { - // The machines sedated for being idle should never be done in the future, so - // cancelled switches OFF are inertial ones. However, for coherency's sake, - // the number of idle-sedated machines might also be decreased if there is no - // inertially-sedated machines left. - int nb_cancelled_switches_off = (int) _machines_to_sedate.size(); - int nb_cancelled_inertially_sedated_machines = min(nb_cancelled_switches_off, - _nb_machines_sedated_by_inertia); - - int nb_cancelled_idle_sedated_machines = 0; - if (nb_cancelled_inertially_sedated_machines < nb_cancelled_switches_off) - nb_cancelled_idle_sedated_machines = nb_cancelled_switches_off - - nb_cancelled_inertially_sedated_machines; - - PPK_ASSERT_ERROR(nb_cancelled_inertially_sedated_machines + - nb_cancelled_idle_sedated_machines == nb_cancelled_switches_off); - - _nb_machines_sedated_by_inertia -= nb_cancelled_inertially_sedated_machines; - _nb_machines_sedated_for_being_idle -= nb_cancelled_idle_sedated_machines; - - PPK_ASSERT_ERROR(_nb_machines_sedated_by_inertia >= 0); - PPK_ASSERT_ERROR(_nb_machines_sedated_for_being_idle >= 0); - - _machines_to_sedate.clear(); - - // Let's check whether the asleep machines are consistent - machines_asleep_soon = (_asleep_machines + _switching_off_machines - _switching_on_machines) - + _machines_to_sedate - _machines_to_awaken; - PPK_ASSERT_ERROR((int)machines_asleep_soon.size() == _nb_machines_sedated_by_inertia + - _nb_machines_sedated_for_being_idle, - "Asleep machines inconsistency. nb_asleep_soon=%d (%s). nb_sedated_inertia=%d. " - "nb_sedated_idle=%d\n", - (int)machines_asleep_soon.size(), machines_asleep_soon.to_string_brackets().c_str(), - _nb_machines_sedated_by_inertia, _nb_machines_sedated_for_being_idle); - } - - const Job * priority_job = _queue->first_job_or_nullptr(); - IntervalSet machines_awake_soon = (_awake_machines + _switching_on_machines - _switching_off_machines) - + _machines_to_awaken - _machines_to_sedate; - int nb_machines_to_let_awakened = (int) (_fraction_of_machines_to_let_awake * _all_machines.size()); - if (_inertial_shutdown_debug) - LOG_F(1, "Date=%g. nb_machines_to_let_awakened=%d", date, nb_machines_to_let_awakened); - - IntervalSet machines_that_can_be_used_by_the_priority_job; - Schedule::JobAlloc priority_job_alloc; - IntervalSet priority_job_reserved_machines; - bool priority_job_needs_awakenings = false; - - if (_inertial_shutdown_debug) - LOG_F(1, "Schedule without priority_job.%s", _inertial_schedule.to_string().c_str()); - - compute_priority_job_and_related_stuff(_inertial_schedule, _queue, priority_job, - _selector, - priority_job_needs_awakenings, - priority_job_alloc, - priority_job_reserved_machines, - machines_that_can_be_used_by_the_priority_job); - - if (!priority_job_needs_awakenings) - { - if (priority_job != nullptr) - nb_machines_to_let_awakened = max(nb_machines_to_let_awakened, priority_job->nb_requested_resources); - - int nb_machines_to_sedate = (int)machines_awake_soon.size() - nb_machines_to_let_awakened; - if (_inertial_shutdown_debug) - LOG_F(1, "Date=%g. nb_machines_to_sedate=%d", date, nb_machines_to_sedate); - if (nb_machines_to_sedate > 0) - { - if (_inertial_shutdown_debug) - LOG_F(1, "Date=%g. nb_machines_to_sedate=%d. machines_awake_soon=%s. " - "machines_that_can_be_used_by_the_priority_job=%s", - date, nb_machines_to_sedate, - machines_awake_soon.to_string_brackets().c_str(), - machines_that_can_be_used_by_the_priority_job.to_string_brackets().c_str()); - - // Let's "steal" some machines sedated for being idle if needed - int nb_idle_machines_to_steal = 0; - if (_nb_machines_sedated_for_being_idle > 0) - { - nb_idle_machines_to_steal = min(_nb_machines_sedated_for_being_idle, - (int) nb_machines_to_sedate); - _nb_machines_sedated_by_inertia += nb_idle_machines_to_steal; - _nb_machines_sedated_for_being_idle -= nb_idle_machines_to_steal; - } - - IntervalSet machines_to_sedate; - select_machines_to_sedate(nb_machines_to_sedate, machines_awake_soon, - machines_that_can_be_used_by_the_priority_job, - machines_to_sedate, priority_job); - LOG_F(INFO, "Date=%g. Machines to sedate were %s", date, - _machines_to_sedate.to_string_brackets().c_str()); - LOG_F(INFO, "Date=%g. Machines asleep soon were %s", date, - machines_asleep_soon.to_string_brackets().c_str()); - - if (_inertial_shutdown_debug) - LOG_F(1, "Date=%g. machines_to_sedate=%s", - date, machines_to_sedate.to_string_brackets().c_str()); - - _machines_to_sedate += machines_to_sedate; - _nb_machines_sedated_by_inertia += (int) machines_to_sedate.size(); - - LOG_F(INFO, "Machines to sedate are now %s", _machines_to_sedate.to_string_brackets().c_str()); - - IntervalSet machines_sedated_this_turn, machines_awakened_this_turn, empty_range; - handle_queued_switches(_inertial_schedule, _machines_to_sedate, - empty_range, machines_sedated_this_turn, - machines_awakened_this_turn); - - PPK_ASSERT_ERROR(machines_awakened_this_turn == IntervalSet::empty_interval_set()); - - if (machines_sedated_this_turn.size() > 0) - LOG_F(INFO, "Date=%g. Those machines should be put to sleep now: %s", - date, machines_sedated_this_turn.to_string_brackets().c_str()); - - _machines_to_sedate -= machines_sedated_this_turn; - - PPK_ASSERT_ERROR((_machines_to_awaken & _machines_to_sedate) == IntervalSet::empty_interval_set()); - - make_decisions_of_schedule(_inertial_schedule, false); - - // Let's make sure the asleep machines are still consistent - machines_asleep_soon = (_asleep_machines + _switching_off_machines - _switching_on_machines) - + _machines_to_sedate - _machines_to_awaken; - PPK_ASSERT_ERROR((int)machines_asleep_soon.size() == _nb_machines_sedated_by_inertia + - _nb_machines_sedated_for_being_idle, - "Asleep machines inconsistency. nb_asleep_soon=%d (%s). nb_sedated_inertia=%d. " - "nb_sedated_idle=%d\n", - (int)machines_asleep_soon.size(), machines_asleep_soon.to_string_brackets().c_str(), - _nb_machines_sedated_by_inertia, _nb_machines_sedated_for_being_idle); - } - - // Let's now try to sedate the machines which have been idle for too long - EnergyBackfillingIdleSleeper::update_idle_states(date, _inertial_schedule, _all_machines, - _idle_machines, _machines_idle_start_date); - machines_awake_soon = _awake_machines + _switching_on_machines + - _machines_to_awaken - _machines_to_sedate; - - // Guard to prevent switch ON/OFF cycles - if (_switching_on_machines.size() == 0 && _machines_to_awaken.size() == 0) - { - IntervalSet machines_to_sedate_for_being_idle; - EnergyBackfillingIdleSleeper::select_idle_machines_to_sedate(date, - _idle_machines, machines_awake_soon, - priority_job, _machines_idle_start_date, - _needed_amount_of_idle_time_to_be_sedated, - machines_to_sedate_for_being_idle); - - if (machines_to_sedate_for_being_idle.size() > 0) - { - // Let's handle queue switches - IntervalSet machines_sedated_this_turn; - IntervalSet machines_awakened_this_turn; - IntervalSet empty_range; - - handle_queued_switches(_inertial_schedule, machines_to_sedate_for_being_idle, empty_range, - machines_sedated_this_turn, machines_awakened_this_turn); - - // A subset of those machines can be sedated (not all of them because it might be jobs - // in the future on some resources) - PPK_ASSERT_ERROR((machines_sedated_this_turn & machines_to_sedate_for_being_idle) == - machines_sedated_this_turn, - "The sedated machines are not the expected ones.Sedated=%s.\nExpected=subset of %s", - machines_sedated_this_turn.to_string_brackets().c_str(), - machines_to_sedate_for_being_idle.to_string_brackets().c_str()); - - PPK_ASSERT_ERROR(machines_awakened_this_turn == IntervalSet::empty_interval_set(), - "The awakened machines are not the expected ones.Awakened=%s.\nExpected=%s", - machines_awakened_this_turn.to_string_brackets().c_str(), - IntervalSet::empty_interval_set().to_string_brackets().c_str()); - - LOG_F(INFO, "Date=%g. Those machines should be put to sleep now for being idle: %s", - date, machines_sedated_this_turn.to_string_brackets().c_str()); - - - PPK_ASSERT_ERROR((_machines_to_awaken & _machines_to_sedate) == IntervalSet::empty_interval_set()); - - if (_inertial_shutdown_debug) - { - LOG_F(1, "Date=%g. Before make_decisions_of_schedule. %s", - date, _inertial_schedule.to_string().c_str()); - write_schedule_debug("_on_monitoring_before_make_decisions_of_schedule"); - } - - // Let's finally make the idle decisions! - make_decisions_of_schedule(_inertial_schedule, false); - - _nb_machines_sedated_for_being_idle += machines_sedated_this_turn.size(); - PPK_ASSERT_ERROR(_nb_machines_sedated_for_being_idle <= _nb_machines); - - // Let's make sure the asleep machines are still consistent - machines_asleep_soon = (_asleep_machines + _switching_off_machines - _switching_on_machines) - + _machines_to_sedate - _machines_to_awaken; - PPK_ASSERT_ERROR((int)machines_asleep_soon.size() == _nb_machines_sedated_by_inertia + - _nb_machines_sedated_for_being_idle, - "Asleep machines inconsistency. nb_asleep_soon=%d (%s). nb_sedated_inertia=%d. " - "nb_sedated_idle=%d\n", - (int)machines_asleep_soon.size(), machines_asleep_soon.to_string_brackets().c_str(), - _nb_machines_sedated_by_inertia, _nb_machines_sedated_for_being_idle); - } - } - } - - PPK_ASSERT_ERROR(_nb_machines_sedated_by_inertia <= _nb_machines - nb_machines_to_let_awakened); - - // Let's update the reason why machines are sedated if needed - if (_nb_machines_sedated_for_being_idle > nb_machines_to_let_awakened) - { - int nb_machines_to_transfer = _nb_machines_sedated_for_being_idle - nb_machines_to_let_awakened; - PPK_ASSERT_ERROR(nb_machines_to_transfer > 0); - - _nb_machines_sedated_for_being_idle -= nb_machines_to_transfer; - _nb_machines_sedated_by_inertia += nb_machines_to_transfer; - - PPK_ASSERT_ERROR(_nb_machines_sedated_for_being_idle >= 0); - PPK_ASSERT_ERROR(_nb_machines_sedated_by_inertia <= _nb_machines); - } - - PPK_ASSERT_ERROR(_nb_machines_sedated_for_being_idle <= nb_machines_to_let_awakened); -} diff --git a/src/algo/energy_bf_machine_subpart_sleeper.hpp b/src/algo/energy_bf_machine_subpart_sleeper.hpp deleted file mode 100644 index 5c5ddf9fee29ccc659e46837fc080d6f7bb01908..0000000000000000000000000000000000000000 --- a/src/algo/energy_bf_machine_subpart_sleeper.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "energy_bf_monitoring_inertial_shutdown.hpp" - -class EnergyBackfillingMachineSubpartSleeper : public EnergyBackfillingMonitoringInertialShutdown -{ -public: - EnergyBackfillingMachineSubpartSleeper(Workload * workload, SchedulingDecision * decision, - Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options); - virtual ~EnergyBackfillingMachineSubpartSleeper(); - - virtual void on_monitoring_stage(double date); - -private: - Rational _fraction_of_machines_to_let_awake; -}; diff --git a/src/algo/energy_bf_monitoring_inertial_shutdown.cpp b/src/algo/energy_bf_monitoring_inertial_shutdown.cpp deleted file mode 100644 index 3da4934be937b2fc4f0129044ada05332f18a765..0000000000000000000000000000000000000000 --- a/src/algo/energy_bf_monitoring_inertial_shutdown.cpp +++ /dev/null @@ -1,1498 +0,0 @@ -#include "energy_bf_monitoring_inertial_shutdown.hpp" -#include "easy_bf_plot_liquid_load_horizon.hpp" - -#include <boost/regex.hpp> - -#include <loguru.hpp> - -#include "../pempek_assert.hpp" -#include "energy_bf_idle_sleeper.hpp" - -using namespace std; - -EnergyBackfillingMonitoringInertialShutdown::EnergyBackfillingMonitoringInertialShutdown(Workload *workload, - SchedulingDecision *decision, Queue *queue, ResourceSelector *selector, - double rjms_delay, rapidjson::Document *variant_options) : - EnergyBackfillingMonitoringPeriod(workload, decision, queue, selector, rjms_delay, variant_options) -{ - PPK_ASSERT_ERROR(variant_options->HasMember("trace_output_filename"), - "Invalid options JSON object: Member 'trace_output_filename' cannot be found"); - PPK_ASSERT_ERROR((*variant_options)["trace_output_filename"].IsString(), - "Invalid options JSON object: Member 'trace_output_filename' must be a string"); - string trace_output_filename = (*variant_options)["trace_output_filename"].GetString(); - - _output_file.open(trace_output_filename); - PPK_ASSERT_ERROR(_output_file.is_open(), "Couldn't open file %s", trace_output_filename.c_str()); - - string buf = "date,nb_jobs_in_queue,first_job_size,priority_job_expected_waiting_time,load_in_queue,liquid_load_horizon\n"; - _output_file.write(buf.c_str(), buf.size()); - - if (variant_options->HasMember("allow_future_switches")) - { - PPK_ASSERT_ERROR((*variant_options)["allow_future_switches"].IsBool(), - "Invalid options JSON object: Member 'allow_future_switches' must be a boolean"); - - _allow_future_switches = (*variant_options)["allow_future_switches"].GetBool(); - } - - if (variant_options->HasMember("upper_llh_threshold")) - { - PPK_ASSERT_ERROR((*variant_options)["upper_llh_threshold"].IsNumber(), - "Invalid options JSON object: Member 'upper_llh_threshold' must be a number"); - _upper_llh_threshold = (*variant_options)["upper_llh_threshold"].GetDouble(); - - PPK_ASSERT_ERROR(_upper_llh_threshold >= 0, - "Invalid options JSON object: Member 'upper_llh_threshold' must be non-negative."); - } - - if (variant_options->HasMember("inertial_alteration")) - { - PPK_ASSERT_ERROR((*variant_options)["inertial_alteration"].IsString(), - "Invalid options JSON object: Member 'inertial_alteration' must be a string"); - - string inertial_alteration = (*variant_options)["inertial_alteration"].GetString(); - - boost::regex r("\\s*(x|p)\\s*(\\d+|\\d+\\.\\d+)\\s*"); - - boost::match_results<std::string::const_iterator> results; - bool matched = boost::regex_match(inertial_alteration, results, r); - PPK_ASSERT_ERROR(matched, "Invalid options JSON object: Member 'inertial_alteration' has an invalid format"); - - string alteration_type_string = results[1]; - string alteration_number_string = results[2]; - - PPK_ASSERT_ERROR(alteration_type_string.size() == 1); - char alteration_type_char = alteration_type_string[0]; - double alteration_number = std::stod(alteration_number_string); - - if (alteration_type_char == 'p') - _alteration_type = SUM; - else if (alteration_type_char == 'x') - _alteration_type = PRODUCT; - else - PPK_ASSERT_ERROR(false); - - PPK_ASSERT_ERROR(alteration_number >= 0, - "Invalid options JSON object: Member 'inertial_alteration' " - "has an invalid value ('%s' -> %g)", - alteration_number_string.c_str(), - alteration_number); - _inertial_alteration_number = alteration_number; - } - - if (variant_options->HasMember("idle_time_to_sedate")) - { - _needed_amount_of_idle_time_to_be_sedated = (*variant_options)["idle_time_to_sedate"].GetDouble(); - - PPK_ASSERT_ERROR(_needed_amount_of_idle_time_to_be_sedated >= 0, - "Invalid options JSON object: Member 'idle_time_to_sedate' " - "has an invalid value (%g)", _needed_amount_of_idle_time_to_be_sedated); - } - - if (variant_options->HasMember("sedate_idle_on_classical_events")) - { - _sedate_idle_on_classical_events = (*variant_options)["sedate_idle_on_classical_events"].GetBool(); - } - - LOG_SCOPE_FUNCTION(INFO); - LOG_F(INFO, "needed amount of idle time to be sedated: %g", _needed_amount_of_idle_time_to_be_sedated); - LOG_F(INFO, "Sedate on classical events: %s", _sedate_idle_on_classical_events ? "true" : "false"); -} - -void EnergyBackfillingMonitoringInertialShutdown::on_simulation_start(double date, const rapidjson::Value & batsim_config) -{ - EnergyBackfillingMonitoringPeriod::on_simulation_start(date, batsim_config); - - _inertial_schedule = _schedule; - - for (int i = 0; i < _nb_machines; ++i) - _machines_idle_start_date[i] = date; - - _idle_machines = _all_machines; -} -// break energy_bf_monitoring_inertial_shutdown.cpp:119 if date >= 3300 -void EnergyBackfillingMonitoringInertialShutdown::make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info) -{ - PPK_ASSERT_ERROR(_nb_machines_sedated_for_being_idle >= 0 && - _nb_machines_sedated_for_being_idle <= (int)_all_machines.size(), - "Invalid nb_machines_sedated_for_being_idle value: %d\n", - _nb_machines_sedated_for_being_idle); - - if (!_jobs_ended_recently.empty()) - { - // Let's remove finished jobs from the schedule - for (const string & ended_job_id : _jobs_ended_recently) - { - const Job * ended_job = (*_workload)[ended_job_id]; - ++_nb_jobs_completed; - - PPK_ASSERT_ERROR(_schedule.contains_job(ended_job), - "Invalid schedule: job '%s' just finished, " - "but it not in the schedule...\n%s", - ended_job_id.c_str(), _schedule.to_string().c_str()); - PPK_ASSERT_ERROR(!_queue->contains_job(ended_job), - "Job '%s' just ended, but it is still in the " - "queue...\nQueue : %s", - ended_job_id.c_str(), - _queue->to_string().c_str()); - - // Let's remove the finished job from the schedule - _schedule.remove_job(ended_job); - } - - // Stop sending CALL_ME_LATER if all jobs have been executed. - if (_no_more_static_job_to_submit_received && - _queue->is_empty() && - !EnergyBackfilling::contains_any_nonfake_job(_schedule)) - _stop_sending_call_me_later = true; - } - - // Let's update the first slice of the schedule - update_first_slice_taking_sleep_jobs_into_account(date); - - // We can now (after updating the first slice AND before adding new jobs into the queue), - // compute a LLH value to know how much the LLH decreased since the last computation. - Rational llh = EasyBackfillingPlotLiquidLoadHorizon::compute_liquid_load_horizon(_inertial_schedule, - _queue, date); - - Rational trapezoid_area = ((_last_llh_value + llh) / 2) * (date - _last_llh_date); - PPK_ASSERT_ERROR(trapezoid_area >= 0, - "Invalid area computed (%g): Cannot be negative.", - (double) trapezoid_area); - - // In order to be visualized properly, this value is stored as date-epsilon - Rational time_diff_from_last_llh_computation = date - _last_llh_date; - - if (time_diff_from_last_llh_computation > 0) - { - Rational epsilon = min(Rational(1e-6), time_diff_from_last_llh_computation / 2); - if (_write_output_file) - { - int first_job_size = -1; - const Job * first_job = _queue->first_job_or_nullptr(); - if (first_job != nullptr) - first_job_size = first_job->nb_requested_resources; - - write_output_file((double)(Rational(date) - epsilon), _queue->nb_jobs(), - first_job_size, - (double) _queue->compute_load_estimation(), - (double) llh); - } - } - - _llh_integral_since_last_monitoring_stage += trapezoid_area; - _last_llh_value = llh; - _last_llh_date = date; - - // Let's handle recently released jobs - for (const string & new_job_id : _jobs_released_recently) - { - const Job * new_job = (*_workload)[new_job_id]; - PPK_ASSERT_ERROR(new_job->has_walltime, - "This scheduler only supports jobs with walltimes."); - ++_nb_jobs_submitted; - - PPK_ASSERT_ERROR(!_schedule.contains_job(new_job), - "Invalid schedule: job '%s' already exists.\n%s", - new_job->id.c_str(), _schedule.to_string().c_str()); - PPK_ASSERT_ERROR(!_queue->contains_job(new_job), - "Job '%s' is already in the queue!\nQueue:%s", - new_job->id.c_str(), _queue->to_string().c_str()); - - if (new_job->nb_requested_resources > _nb_machines) - { - _decision->add_reject_job(new_job_id, date); - ++_nb_jobs_completed; - } - else - _queue->append_job(new_job, update_info); - } - - // Let's update the sorting of the queue - _queue->sort_queue(update_info, compare_info); - - // Simple Easy Backfilling - _inertial_schedule = _schedule; - const Job * priority_job = nullptr; - IntervalSet priority_job_reserved_machines; - bool priority_job_can_be_started_soon = true; - - Rational time_to_wake_up = (*_variant_options)["time_switch_on"].GetDouble(); - Rational soon_horizon = time_to_wake_up * 2 + date; - - if (_inertial_shutdown_debug) - { - LOG_SCOPE_FUNCTION(1); - LOG_F(1, "Date=%g. Beginning of make_decisions. Queue: %s.\n%s", - date, _queue->to_string().c_str(), _inertial_schedule.to_string().c_str()); - write_schedule_debug("_make_decisions_begin"); - } - else - { - LOG_SCOPE_FUNCTION(INFO); - } - - set<const Job *> allocated_jobs; - - IntervalSet machines_sedated_this_turn, machines_awakened_this_turn; - handle_queued_switches(_inertial_schedule, _machines_to_sedate, _machines_to_awaken, - machines_sedated_this_turn, machines_awakened_this_turn); - - if (_queue->is_empty()) - { - _machines_to_sedate -= machines_sedated_this_turn; - _machines_to_awaken -= machines_awakened_this_turn; - - _priority_job_starting_time_expectancy = date; - } - else - { - bool priority_job_launched_now = true; - - auto job_it = _queue->begin(); - while (priority_job_launched_now && job_it != _queue->end()) - { - const Job * job = (*job_it)->job; - priority_job = job; - - if (_inertial_shutdown_debug) - LOG_F(1, "Date=%g. make_decisions, priority loop, trying to insert priority job '%s'.%s", - date, priority_job->id.c_str(), _inertial_schedule.to_string().c_str()); - - Schedule::JobAlloc alloc = _inertial_schedule.add_job_first_fit(priority_job, _selector, false); - - if (alloc.has_been_inserted) - { - priority_job_reserved_machines = alloc.used_machines; - - if (alloc.started_in_first_slice) - { - priority_job = nullptr; - priority_job_reserved_machines.clear(); - _priority_job_starting_time_expectancy = alloc.begin; - - priority_job_launched_now = true; - allocated_jobs.insert(job); - } - else - { - priority_job_launched_now = false; - - _priority_job_starting_time_expectancy = compute_priority_job_starting_time_expectancy(_inertial_schedule, priority_job); - priority_job_can_be_started_soon = _priority_job_starting_time_expectancy <= soon_horizon; - priority_job_can_be_started_soon = true; // REMOVEME. Forces the previous behaviour. - } - } - else - { - priority_job_launched_now = false; - - // The priority job couldn't be inserted into the Schedule... It means some machines must be awakened first. - - // Before doing anything else, let's compute when the priority job would start if all machines were to be awakened. - _priority_job_starting_time_expectancy = compute_priority_job_starting_time_expectancy(_inertial_schedule, priority_job); - priority_job_can_be_started_soon = _priority_job_starting_time_expectancy <= soon_horizon; - priority_job_can_be_started_soon = true; // REMOVEME. Forces the previous behaviour. - - // If the job can be started "soon", we must make sure is is runnable. - if (priority_job_can_be_started_soon) - { - // Let's first check whether this was caused by a decision made this turn, so we can cancel the decision before applying it. - // To do so, we'll first remove the fake jobs of the machines which have been switched OFF this turn, hopefully to create empty room - // for the priority job - LOG_F(INFO, "In order to make priority job '%s' fit, canceling the switches OFF of machines %s", - priority_job->id.c_str(), - _machines_to_sedate.to_string_brackets().c_str()); - - for (auto mit = _machines_to_sedate.elements_begin(); mit != _machines_to_sedate.elements_end(); ++mit) - { - int machine_id = *mit; - MachineInformation * minfo = _machine_informations[machine_id]; - - _inertial_schedule.remove_job_last_occurence(minfo->switch_off_job); - if (minfo->ensured_sleep_job->walltime > 0) - _inertial_schedule.remove_job_last_occurence(minfo->ensured_sleep_job); - _inertial_schedule.remove_job_last_occurence(minfo->potential_sleep_job); - } - - alloc = _inertial_schedule.add_job_first_fit(priority_job, _selector, false); - if (alloc.has_been_inserted) - { - // The job could fit by only cancelling some switches OFF \o/ - // Let's perserve the switches OFF that do not disturb the priority job. - IntervalSet non_disturbing_machines_switches_off = _machines_to_sedate - alloc.used_machines; - IntervalSet disturbing_machines_switches_off = (_machines_to_sedate & alloc.used_machines); - - // The machines sedated for being idle should never be done in the future, so - // cancelled switches OFF are inertial ones. However, for coherency's sake, - // the number of idle-sedated machines might also be decreased if there is no - // inertially-sedated machines left. - int nb_cancelled_switches_off = (int) disturbing_machines_switches_off.size(); - int nb_cancelled_inertially_sedated_machines = min(nb_cancelled_switches_off, - _nb_machines_sedated_by_inertia); - - int nb_cancelled_idle_sedated_machines = 0; - if (nb_cancelled_inertially_sedated_machines < nb_cancelled_switches_off) - nb_cancelled_idle_sedated_machines = nb_cancelled_switches_off - - nb_cancelled_inertially_sedated_machines; - - PPK_ASSERT_ERROR(nb_cancelled_inertially_sedated_machines + - nb_cancelled_idle_sedated_machines == nb_cancelled_switches_off); - - _nb_machines_sedated_by_inertia -= nb_cancelled_inertially_sedated_machines; - _nb_machines_sedated_for_being_idle -= nb_cancelled_idle_sedated_machines; - - PPK_ASSERT_ERROR(_nb_machines_sedated_by_inertia >= 0); - PPK_ASSERT_ERROR(_nb_machines_sedated_for_being_idle >= 0); - - LOG_F(INFO, "The priority job '%s' could be inserted by cancelling some switches OFF (%s). " - "Let's reinsert a subpart of these switches off : %s", - priority_job->id.c_str(), - disturbing_machines_switches_off.to_string_brackets().c_str(), - non_disturbing_machines_switches_off.to_string_brackets().c_str()); - - // Let's put these machines to sleep. - IntervalSet thrash, machines_sedated_this_turn_tmp, empty_range; - handle_queued_switches(_inertial_schedule, non_disturbing_machines_switches_off, - empty_range, machines_sedated_this_turn_tmp, thrash); - - PPK_ASSERT_ERROR((machines_sedated_this_turn_tmp & - non_disturbing_machines_switches_off) == machines_sedated_this_turn_tmp, - "The machines that have been sedated are not the expected ones.\n" - "sedated=%s\nexpected=subset of %s", - machines_sedated_this_turn_tmp.to_string_brackets().c_str(), - non_disturbing_machines_switches_off.to_string_brackets().c_str()); - - PPK_ASSERT_ERROR(thrash == IntervalSet::empty_interval_set(), - "The machines that have been awakeend by this call are not the " - "expected ones.\nawakened=%s\nexpected=%s", - thrash.to_string_elements().c_str(), - IntervalSet::empty_interval_set().to_string_brackets().c_str()); - - machines_sedated_this_turn -= disturbing_machines_switches_off; - _machines_to_sedate -= disturbing_machines_switches_off; - } - else - { - LOG_F(INFO, "Cancelling some switches OFF was not enough... Some machines must be awakened."); - - // Cancelling some switches OFF was not enough... Some machines must be awakened. - // Let's determine how many of them should be awakened. - IntervalSet machines_awake_in_the_prediction_schedule = _awake_machines + - _switching_on_machines + _machines_to_awaken; // no machines to sedate, because they were already canceled - - if (_inertial_shutdown_debug) - { - LOG_F(1, "_awake_machines: %s", _awake_machines.to_string_brackets().c_str()); - LOG_F(1, "_switching_on_machines: %s", _switching_on_machines.to_string_brackets().c_str()); - LOG_F(1, "_switching_off_machines: %s", _switching_off_machines.to_string_brackets().c_str()); - LOG_F(1, "_machines_to_awaken: %s", _machines_to_awaken.to_string_brackets().c_str()); - LOG_F(1, "_machines_to_sedate: %s", _machines_to_sedate.to_string_brackets().c_str()); - - LOG_F(1, "Awake machines in prediction schedule: %s (size=%d)", - machines_awake_in_the_prediction_schedule.to_string_brackets().c_str(), - (int)machines_awake_in_the_prediction_schedule.size()); - } - - int minimum_nb_machines_to_awaken = job->nb_requested_resources - (int)machines_awake_in_the_prediction_schedule.size(); - PPK_ASSERT_ERROR(minimum_nb_machines_to_awaken > 0, - "Schedule seems to be inconsistent.\n" - "job->nb_requested_resources=%d. machines_awake_in_the_prediction_schedule=%d\n" - "awake machines: %s. machines being switched ON: %s. Machines to awaken:%s\n%s", - job->nb_requested_resources, - (int)machines_awake_in_the_prediction_schedule.size(), - _awake_machines.to_string_brackets().c_str(), - _switching_on_machines.to_string_brackets().c_str(), - _machines_to_awaken.to_string_brackets().c_str(), - _inertial_schedule.to_string().c_str()); - - LOG_F(INFO, "Date=%g. make_decisions. Would like to awaken %d machines to execute job '%s', " - "which requests %d resources.", - date, minimum_nb_machines_to_awaken, priority_job->id.c_str(), - priority_job->nb_requested_resources); - - // Let's select which machines should be awakened - IntervalSet machines_to_awaken_to_make_priority_job_fit; - select_machines_to_awaken(minimum_nb_machines_to_awaken, _asleep_machines + _switching_off_machines, - machines_to_awaken_to_make_priority_job_fit); - - // Let's awaken the machines in the schedule - IntervalSet thrash, machines_really_awakened_to_make_priority_job_fit; - handle_queued_switches(_inertial_schedule, IntervalSet(), - machines_to_awaken_to_make_priority_job_fit, - thrash, machines_really_awakened_to_make_priority_job_fit); - PPK_ASSERT_ERROR(thrash == IntervalSet::empty_interval_set(), - "The machines that have been sedated by this call are not the " - "expected ones.\nsedated=%s\nexpected=%s", - thrash.to_string_elements().c_str(), - IntervalSet::empty_interval_set().to_string_brackets().c_str()); - PPK_ASSERT_ERROR((machines_really_awakened_to_make_priority_job_fit & - machines_to_awaken_to_make_priority_job_fit) == - machines_really_awakened_to_make_priority_job_fit, - "The machines that have been awakened by this call are not the" - "expected ones.\nawakened=%s\nexpected=subset of %s", - machines_really_awakened_to_make_priority_job_fit.to_string_brackets().c_str(), - machines_to_awaken_to_make_priority_job_fit.to_string_brackets().c_str()); - - // Now the priority job should fit the platform. Let's insert it into it - alloc = _inertial_schedule.add_job_first_fit(priority_job, _selector, false); - PPK_ASSERT_ERROR(alloc.has_been_inserted, - "Cannot insert the priority job, which should not happen now! " - "priority_job='%s', nb_res=%d\n%s", - priority_job->id.c_str(), priority_job->nb_requested_resources, - _inertial_schedule.to_string().c_str()); - PPK_ASSERT_ERROR(alloc.begin > _inertial_schedule.first_slice_begin()); - - // Let's update which machines are sedated now. - // The machines sedated for being idle should never be done in the future, so - // cancelled switches OFF and newly awakened machines are inertial ones. - // However, for coherency's sake, - // the number of idle-sedated machines might also be decreased if there is no - // inertially-sedated machines left. - int nb_cancelled_switches_off = (int)_machines_to_sedate.size(); - int nb_cancelled_inertially_sedated_machines = min(nb_cancelled_switches_off, - _nb_machines_sedated_by_inertia); - - int nb_cancelled_idle_sedated_machines = 0; - if (nb_cancelled_inertially_sedated_machines < nb_cancelled_switches_off) - nb_cancelled_idle_sedated_machines = nb_cancelled_switches_off - - nb_cancelled_inertially_sedated_machines; - - PPK_ASSERT_ERROR(nb_cancelled_inertially_sedated_machines + - nb_cancelled_idle_sedated_machines == nb_cancelled_switches_off); - - _nb_machines_sedated_by_inertia -= nb_cancelled_inertially_sedated_machines; - _nb_machines_sedated_for_being_idle -= nb_cancelled_idle_sedated_machines; - - PPK_ASSERT_ERROR(_nb_machines_sedated_by_inertia >= 0); - PPK_ASSERT_ERROR(_nb_machines_sedated_for_being_idle >= 0); - - // Update after awakenings - int nb_awakened_machines = (int) machines_to_awaken_to_make_priority_job_fit.size(); - int nb_awakened_inertially_sedated_machines = min(nb_awakened_machines, - _nb_machines_sedated_by_inertia); - - int nb_awakened_idle_sedated_machines = 0; - if (nb_awakened_inertially_sedated_machines < nb_awakened_machines) - nb_awakened_idle_sedated_machines = nb_awakened_machines - - nb_awakened_inertially_sedated_machines; - - PPK_ASSERT_ERROR(nb_awakened_idle_sedated_machines + - nb_awakened_inertially_sedated_machines == nb_awakened_machines); - - _nb_machines_sedated_by_inertia -= nb_awakened_inertially_sedated_machines; - _nb_machines_sedated_for_being_idle -= nb_awakened_idle_sedated_machines; - - PPK_ASSERT_ERROR(_nb_machines_sedated_by_inertia >= 0); - PPK_ASSERT_ERROR(_nb_machines_sedated_for_being_idle >= 0); - - machines_sedated_this_turn -= _machines_to_sedate; - _machines_to_sedate.clear(); - machines_awakened_this_turn += machines_really_awakened_to_make_priority_job_fit; - _machines_to_awaken += (machines_to_awaken_to_make_priority_job_fit - machines_really_awakened_to_make_priority_job_fit); - } - } - } - - job_it++; - } // end of priority job management loop - - _machines_to_sedate -= machines_sedated_this_turn; - _machines_to_awaken -= machines_awakened_this_turn; - _machines_sedated_since_last_monitoring_stage_inertia += machines_sedated_this_turn; - _machines_awakened_since_last_monitoring_stage_inertia += machines_awakened_this_turn; - - PPK_ASSERT_ERROR((_machines_to_awaken & _machines_to_sedate) == IntervalSet::empty_interval_set(), - "The machines to awaken and those to sedate should be distinct...\n" - "machines_to_awaken=%s\nmachines_to_sedate=%s", - _machines_to_awaken.to_string_brackets().c_str(), - _machines_to_sedate.to_string_brackets().c_str()); - - if (machines_sedated_this_turn.size() > 0) - LOG_F(INFO, "Date=%g. Those machines should be put to sleep now: %s", date, - machines_sedated_this_turn.to_string_brackets().c_str()); - - if (machines_awakened_this_turn.size() > 0) - LOG_F(INFO, "Date=%g. Those machines should be awakened now: %s", date, - machines_awakened_this_turn.to_string_brackets().c_str()); - - if (_inertial_shutdown_debug) - { - LOG_F(1, "Date=%g. After priority job loop. %s", - date, _inertial_schedule.to_string().c_str()); - write_schedule_debug("_make_decisions_after_priority_job_loop"); - } - - int nb_machines_available_now = (int) _inertial_schedule.begin()->available_machines.size(); - - job_it = _queue->begin(); - while (job_it != _queue->end() && nb_machines_available_now > 0) - { - const Job * job = (*job_it)->job; - if (job != priority_job && // Not the priority job - allocated_jobs.count(job) == 0 && // Not already scheduled - job->nb_requested_resources <= nb_machines_available_now) // Thin enough to fit the first hole - { - Schedule::JobAlloc alloc = _inertial_schedule.add_job_first_fit(job, _selector, false); - if (alloc.has_been_inserted) - { - if (alloc.started_in_first_slice) - { - allocated_jobs.insert(job); - nb_machines_available_now -= job->nb_requested_resources; - PPK_ASSERT_ERROR(nb_machines_available_now >= 0); - } - else - _inertial_schedule.remove_job(job); - } - } - - job_it++; - } - } - - if (_inertial_shutdown_debug) - { - LOG_F(1, "Date=%g. End of make_decisions. %s", date, _inertial_schedule.to_string().c_str()); - write_schedule_debug("_make_decisions_end"); - } - - // Let's make the first decision parts (executing new jobs / awakening some machines for the priority job) - make_decisions_of_schedule(_inertial_schedule, false); - - IntervalSet machines_asleep_soon = (_asleep_machines + _switching_off_machines - _switching_on_machines) - + _machines_to_sedate - _machines_to_awaken; - PPK_ASSERT_ERROR((int)machines_asleep_soon.size() == _nb_machines_sedated_by_inertia + - _nb_machines_sedated_for_being_idle, - "Asleep machines inconsistency. nb_asleep_soon=%d (%s). nb_sedated_inertia=%d. " - "nb_sedated_idle=%d\n", - (int)machines_asleep_soon.size(), machines_asleep_soon.to_string_brackets().c_str(), - _nb_machines_sedated_by_inertia, _nb_machines_sedated_for_being_idle); - - // Let's make sure all the jobs we marked as executed were removed from the queue - for (const Job * allocated_job : allocated_jobs) - PPK_ASSERT_ERROR(!_queue->contains_job(allocated_job), - "Inconsistency: job '%s' is marked as allocated but it has not " - "been removed from the queue.", allocated_job->id.c_str()); - - // Let's awake sedated idle machines if needed - EnergyBackfillingIdleSleeper::update_idle_states(date, _inertial_schedule, _all_machines, - _idle_machines, _machines_idle_start_date); - - IntervalSet machines_to_awaken_for_being_idle; - EnergyBackfillingIdleSleeper::select_idle_machines_to_awaken(_queue, - _inertial_schedule, _selector, - _idle_machines, - EnergyBackfillingIdleSleeper::AwakeningPolicy::AWAKEN_FOR_ALL_JOBS_RESPECTING_PRIORITY_JOB, - _nb_machines_sedated_for_being_idle, - machines_to_awaken_for_being_idle, - true); // <--------------------------------------------------------------------------------- Choice to make. - - if (machines_to_awaken_for_being_idle.size() > 0) - { - _machines_to_awaken += machines_to_awaken_for_being_idle; - - // The awakened machines are the previously idle ones - _nb_machines_sedated_for_being_idle -= machines_to_awaken_for_being_idle.size(); - PPK_ASSERT_ERROR(_nb_machines_sedated_for_being_idle >= 0, - "Invalid nb_machines_sedated_for_being_idle value: %d\n", - _nb_machines_sedated_for_being_idle); - - IntervalSet machines_sedated_this_turn, machines_awakened_this_turn, empty_range; - machines_sedated_this_turn.clear(); - machines_awakened_this_turn.clear(); - handle_queued_switches(_inertial_schedule, empty_range, machines_to_awaken_for_being_idle, - machines_sedated_this_turn, machines_awakened_this_turn); - - PPK_ASSERT_ERROR(machines_awakened_this_turn == machines_to_awaken_for_being_idle, - "Unexpected awakened machines.\n Awakened=%s.\nExpected=%s.", - machines_awakened_this_turn.to_string_brackets().c_str(), - machines_to_awaken_for_being_idle.to_string_brackets().c_str()); - - PPK_ASSERT_ERROR(machines_sedated_this_turn == IntervalSet::empty_interval_set(), - "The sedated machines are not the expected ones.Sedated=%s.\nExpected=%s", - machines_sedated_this_turn.to_string_brackets().c_str(), - IntervalSet::empty_interval_set().to_string_brackets().c_str()); - - LOG_F(INFO, "Date=%g. Those machines should be awakened now (previously idle ones): %s", - date, machines_awakened_this_turn.to_string_brackets().c_str()); - - _machines_to_awaken -= machines_awakened_this_turn; - - PPK_ASSERT_ERROR((_machines_to_awaken & _machines_to_sedate) == IntervalSet::empty_interval_set()); - - // Let's make the new decisions (switches ON)! - make_decisions_of_schedule(_inertial_schedule, false); - - machines_asleep_soon = (_asleep_machines + _switching_off_machines - _switching_on_machines) - + _machines_to_sedate - _machines_to_awaken; - PPK_ASSERT_ERROR((int)machines_asleep_soon.size() == _nb_machines_sedated_by_inertia + - _nb_machines_sedated_for_being_idle, - "Asleep machines inconsistency. nb_asleep_soon=%d (%s). nb_sedated_inertia=%d. " - "nb_sedated_idle=%d\n", - (int)machines_asleep_soon.size(), machines_asleep_soon.to_string_brackets().c_str(), - _nb_machines_sedated_by_inertia, _nb_machines_sedated_for_being_idle); - } - else if (_sedate_idle_on_classical_events && - // Guard to prevent switch ON/OFF cycles - _switching_on_machines.size() == 0 && _machines_to_awaken.size() == 0) - { - // If no machine has been awakened for being idle AND - // if we should sedate on classical events AND - // if no machine should be awakened (currently or in the future), - // we can try to sedate idle machines now. - - // Let's try to sedate the machines which have been idle for too long - EnergyBackfillingIdleSleeper::update_idle_states(date, _inertial_schedule, _all_machines, - _idle_machines, _machines_idle_start_date); - - IntervalSet machines_awake_soon = (_awake_machines + _switching_on_machines - _switching_off_machines) - + _machines_to_awaken - _machines_to_sedate; - - IntervalSet machines_to_sedate_for_being_idle; - EnergyBackfillingIdleSleeper::select_idle_machines_to_sedate(date, - _idle_machines, machines_awake_soon, - priority_job, _machines_idle_start_date, - _needed_amount_of_idle_time_to_be_sedated, - machines_to_sedate_for_being_idle, - true); // <------------------------------------------------- Choice to make. - - if (machines_to_sedate_for_being_idle.size() > 0) - { - // Let's handle queue switches - IntervalSet empty_range; - machines_sedated_this_turn.clear(); - machines_awakened_this_turn.clear(); - handle_queued_switches(_inertial_schedule, machines_to_sedate_for_being_idle, empty_range, - machines_sedated_this_turn, machines_awakened_this_turn); - - - PPK_ASSERT_ERROR((machines_sedated_this_turn & machines_to_sedate_for_being_idle) == - machines_sedated_this_turn, - "The sedated machines are not the expected ones.Sedated=%s.\nExpected=subset of %s", - machines_sedated_this_turn.to_string_brackets().c_str(), - machines_to_sedate_for_being_idle.to_string_brackets().c_str()); - - PPK_ASSERT_ERROR(machines_awakened_this_turn == IntervalSet::empty_interval_set(), - "The awakened machines are not the expected ones.Awakened=%s.\nExpected=%s", - machines_awakened_this_turn.to_string_brackets().c_str(), - IntervalSet::empty_interval_set().to_string_brackets().c_str()); - - LOG_F(INFO, "Date=%g. Those machines should be put to sleep now for being idle: %s", - date, machines_sedated_this_turn.to_string_brackets().c_str()); - - PPK_ASSERT_ERROR((_machines_to_awaken & _machines_to_sedate) == IntervalSet::empty_interval_set()); - - if (_inertial_shutdown_debug) - { - LOG_F(1, "Date=%g. Before make_decisions_of_schedule. %s", - date, _inertial_schedule.to_string().c_str()); - write_schedule_debug("_on_monitoring_before_make_decisions_of_schedule"); - } - - // Let's finally make the decisions! - make_decisions_of_schedule(_inertial_schedule, false); - - _nb_machines_sedated_for_being_idle += machines_sedated_this_turn.size(); - PPK_ASSERT_ERROR(_nb_machines_sedated_for_being_idle <= (int)_all_machines.size()); - } - } - - // Now that the decisions have been made, let's compute the LLH again. - llh = EasyBackfillingPlotLiquidLoadHorizon::compute_liquid_load_horizon(_inertial_schedule, - _queue, date); - if (_write_output_file) - { - int first_job_size = -1; - const Job * first_job = _queue->first_job_or_nullptr(); - if (first_job != nullptr) - first_job_size = first_job->nb_requested_resources; - - write_output_file(date, _queue->nb_jobs(), - first_job_size, - (double) _queue->compute_load_estimation(), - (double) llh); - } - - _last_llh_value = llh; - _last_llh_date = date; - - machines_asleep_soon = (_asleep_machines + _switching_off_machines - _switching_on_machines) - + _machines_to_sedate - _machines_to_awaken; - PPK_ASSERT_ERROR((int)machines_asleep_soon.size() == _nb_machines_sedated_by_inertia + - _nb_machines_sedated_for_being_idle, - "Asleep machines inconsistency. nb_asleep_soon=%d (%s). nb_sedated_inertia=%d. " - "nb_sedated_idle=%d\n", - (int)machines_asleep_soon.size(), machines_asleep_soon.to_string_brackets().c_str(), - _nb_machines_sedated_by_inertia, _nb_machines_sedated_for_being_idle); -} - - - - - - - -// break energy_bf_monitoring_inertial_shutdown.cpp:691 if date >= 3600 -void EnergyBackfillingMonitoringInertialShutdown::on_monitoring_stage(double date) -{ - PPK_ASSERT_ERROR(_nb_machines_sedated_for_being_idle >= 0 && - _nb_machines_sedated_for_being_idle <= (int)_all_machines.size(), - "Invalid nb_machines_sedated_for_being_idle value: %d\n", - _nb_machines_sedated_for_being_idle); - LOG_SCOPE_FUNCTION(INFO); - - // Let's update the first slice of the schedule - update_first_slice_taking_sleep_jobs_into_account(date); - - // Let's compute a first LLH value - Rational llh = EasyBackfillingPlotLiquidLoadHorizon::compute_liquid_load_horizon(_inertial_schedule, - _queue, date); - - Rational trapezoid_area = ((_last_llh_value + llh) / 2) * (date - _last_llh_date); - PPK_ASSERT_ERROR(trapezoid_area >= 0, - "Invalid area computed (%g): Cannot be negative.", - (double) trapezoid_area); - - // In order to be visualized properly, this value is stored as date-epsilon - Rational time_diff_from_last_llh_computation = date - _last_llh_date; - - if (time_diff_from_last_llh_computation > 0) - { - Rational epsilon = min(Rational(1e-6), time_diff_from_last_llh_computation / 2); - if (_write_output_file) - { - int first_job_size = -1; - const Job * first_job = _queue->first_job_or_nullptr(); - if (first_job != nullptr) - first_job_size = first_job->nb_requested_resources; - - write_output_file((double)(Rational(date) - epsilon), _queue->nb_jobs(), - first_job_size, - (double) _queue->compute_load_estimation(), - (double) llh); - } - } - - _llh_integral_since_last_monitoring_stage += trapezoid_area; - _last_llh_value = llh; - _last_llh_date = date; - - _inertial_schedule = _schedule; - const Job * priority_job = nullptr; - IntervalSet priority_job_reserved_machines; - bool priority_job_can_be_started_soon = true; - (void) priority_job_can_be_started_soon; - - Rational time_to_wake_up = (*_variant_options)["time_switch_on"].GetDouble(); - Rational soon_horizon = time_to_wake_up * 2 + date; - - if (_inertial_shutdown_debug) - LOG_F(1, "Date=%g. Begin of on_monitoring_stage.", date); - - if (_machines_to_sedate.size() > 0) - { - // The machines sedated for being idle should never be done in the future, so - // cancelled switches OFF are inertial ones. However, for coherency's sake, - // the number of idle-sedated machines might also be decreased if there is no - // inertially-sedated machines left. - int nb_cancelled_switches_off = (int) _machines_to_sedate.size(); - int nb_cancelled_inertially_sedated_machines = min(nb_cancelled_switches_off, - _nb_machines_sedated_by_inertia); - - int nb_cancelled_idle_sedated_machines = 0; - if (nb_cancelled_inertially_sedated_machines < nb_cancelled_switches_off) - nb_cancelled_idle_sedated_machines = nb_cancelled_switches_off - - nb_cancelled_inertially_sedated_machines; - - PPK_ASSERT_ERROR(nb_cancelled_inertially_sedated_machines + - nb_cancelled_idle_sedated_machines == nb_cancelled_switches_off); - - _nb_machines_sedated_by_inertia -= nb_cancelled_inertially_sedated_machines; - _nb_machines_sedated_for_being_idle -= nb_cancelled_idle_sedated_machines; - - PPK_ASSERT_ERROR(_nb_machines_sedated_by_inertia >= 0); - PPK_ASSERT_ERROR(_nb_machines_sedated_for_being_idle >= 0); - - _machines_to_sedate.clear(); - } - - //_machines_to_awaken.clear(); - - if (_inertial_shutdown_debug) - { - LOG_F(1, "LLH computed. %s", _inertial_schedule.to_string().c_str()); - write_schedule_debug("_on_monitoring_after_llh"); - } - - IntervalSet machines_that_can_be_used_by_the_priority_job; - Schedule::JobAlloc priority_job_alloc; - bool priority_job_needs_awakenings = false; - - if (_inertial_shutdown_debug) - LOG_F(1, "Schedule without priority_job.%s", _inertial_schedule.to_string().c_str()); - - compute_priority_job_and_related_stuff(_inertial_schedule, _queue, priority_job, - _selector, - priority_job_needs_awakenings, - priority_job_alloc, - priority_job_reserved_machines, - machines_that_can_be_used_by_the_priority_job); - - if (priority_job != nullptr) - { - Rational priority_job_starting_time_expectancy = _priority_job_starting_time_expectancy; - priority_job_can_be_started_soon = priority_job_starting_time_expectancy <= soon_horizon; - priority_job_can_be_started_soon = true; // REMOVEME. Forces the previous behaviour. - } - else - { - _priority_job_starting_time_expectancy = date; - } - - if (_first_monitoring_stage) - { - _first_monitoring_stage = false; - _last_decision = AWAKEN_MACHINES; - _inertial_number = 0; - } - else if (!priority_job_needs_awakenings) - { - Rational mean_llh_over_last_period = _llh_integral_since_last_monitoring_stage / period(); - - if (mean_llh_over_last_period >= _upper_llh_threshold) - { - // If the threshold is met, the next decision is forced by hacking some variables. - if (_last_decision == SEDATE_MACHINES) - { - _last_decision = AWAKEN_MACHINES; - _inertial_number = 0; - } - - _llh_integral_of_preceding_monitoring_stage_slice = 0; - } - - if (_last_decision == AWAKEN_MACHINES) - { - if (_llh_integral_since_last_monitoring_stage > _llh_integral_of_preceding_monitoring_stage_slice) - { - // The LLH's integral is still increasing! We may want to awaken more machines! - IntervalSet awakable_machines = _asleep_machines - _machines_to_awaken; - - if (!_allow_future_switches) - awakable_machines -= _non_wakable_asleep_machines; - - int nb_machines_to_awaken_by_inertia = 0; - if (_alteration_type == PRODUCT) - nb_machines_to_awaken_by_inertia = (int)((int)_machines_awakened_since_last_monitoring_stage_inertia.size() * - _inertial_alteration_number); - else if (_alteration_type == SUM) - nb_machines_to_awaken_by_inertia = (int)((int)_machines_awakened_since_last_monitoring_stage_inertia.size() + - _inertial_alteration_number); - else - PPK_ASSERT_ERROR(false); - - _inertial_number = min(max(nb_machines_to_awaken_by_inertia,1), - (int)awakable_machines.size()); - - if (_inertial_number > 0) - { - LOG_F(INFO, "Date=%g. on_monitoring_stage. Would like to awaken %d machines.", - date, _inertial_number); - - IntervalSet machines_to_awaken; - select_machines_to_awaken(_inertial_number, awakable_machines, machines_to_awaken); - - LOG_F(INFO, "Date=%g. Decided to awaken machines %s", date, machines_to_awaken.to_string_brackets().c_str()); - - _machines_to_awaken += machines_to_awaken; - - // The awakened machines are the inertially-sedated ones. - // If there is no inertially-sedated machines left, the idle-sedated ones are impacted - int nb_awakened_inertially_sedated_machines = min((int)machines_to_awaken.size(), - _nb_machines_sedated_by_inertia); - int nb_awakened_idle_sedated_machines = 0; - if (nb_awakened_inertially_sedated_machines < (int)machines_to_awaken.size()) - nb_awakened_idle_sedated_machines = (int)machines_to_awaken.size() - - nb_awakened_inertially_sedated_machines; - - _nb_machines_sedated_by_inertia -= nb_awakened_inertially_sedated_machines; - _nb_machines_sedated_for_being_idle -= nb_awakened_idle_sedated_machines; - - PPK_ASSERT_ERROR(_nb_machines_sedated_by_inertia >= 0); - PPK_ASSERT_ERROR(_nb_machines_sedated_for_being_idle >= 0); - } - } - else - { - // The LLH has decreased, let's switch to sedate mode :) - _last_decision = SEDATE_MACHINES; - _inertial_number = 0; - - LOG_F(INFO, "Date=%g. Decided to do nothing now, but switching to sedate mode!", date); - } - } // end if (_last_decision == AWAKEN_MACHINES) - else // if (_last_decision == SEDATE_MACHINES) - { - if (_llh_integral_since_last_monitoring_stage <= _llh_integral_of_preceding_monitoring_stage_slice) - { - // The LLH's integral is still decreasing, let's sedate more machines! - - IntervalSet sedatable_machines = _awake_machines - _machines_to_sedate; - - if (!_allow_future_switches) - { - PPK_ASSERT_ERROR(_inertial_schedule.nb_slices() > 0); - auto slice_it = _inertial_schedule.begin(); - const Schedule::TimeSlice & slice = *slice_it; - - sedatable_machines -= (_all_machines - slice.available_machines); - } - - int nb_sedatable_machines = sedatable_machines.size(); - - if (priority_job != nullptr) - nb_sedatable_machines = max(0, (int)sedatable_machines.size() - (int)priority_job_reserved_machines.size()); - - int nb_machines_to_sedate_by_inertia = 0; - if (_alteration_type == PRODUCT) - nb_machines_to_sedate_by_inertia = (int)((int)_machines_sedated_since_last_monitoring_stage_inertia.size() * - _inertial_alteration_number); - else if (_alteration_type == SUM) - nb_machines_to_sedate_by_inertia = (int)((int)_machines_sedated_since_last_monitoring_stage_inertia.size() + - _inertial_alteration_number); - else - PPK_ASSERT_ERROR(false); - - _inertial_number = min(max(nb_machines_to_sedate_by_inertia,1), - nb_sedatable_machines); - - if (_inertial_number > 0) - { - IntervalSet machines_to_sedate; - int nb_idle_machines_to_steal = 0; - int nb_new_machines_to_sedate = _inertial_number; - - // Let's just use already sedated machines (for being idle) as if they were inertially sedated - if (_nb_machines_sedated_for_being_idle > 0) - { - nb_idle_machines_to_steal = min(_nb_machines_sedated_for_being_idle, - (int) _inertial_number); - nb_new_machines_to_sedate = _inertial_number - nb_idle_machines_to_steal; - - PPK_ASSERT_ERROR(nb_idle_machines_to_steal >= 0 && - nb_idle_machines_to_steal <= _nb_machines_sedated_for_being_idle); - PPK_ASSERT_ERROR(nb_new_machines_to_sedate >= 0); - } - - if (nb_new_machines_to_sedate > 0) - select_machines_to_sedate(_inertial_number, sedatable_machines, - machines_that_can_be_used_by_the_priority_job, - machines_to_sedate, priority_job); - - LOG_F(INFO, "Date=%g. Decided to sedate machines %s", - date, machines_to_sedate.to_string_brackets().c_str()); - if (nb_idle_machines_to_steal > 0) - LOG_F(INFO, "... and to steal %d idle-sedated machines", - nb_idle_machines_to_steal); - - _nb_machines_sedated_by_inertia += machines_to_sedate.size() + nb_idle_machines_to_steal; - _nb_machines_sedated_for_being_idle -= nb_idle_machines_to_steal; - _machines_to_sedate += machines_to_sedate; - - PPK_ASSERT_ERROR(_nb_machines_sedated_by_inertia + _nb_machines_sedated_for_being_idle <= _nb_machines); - PPK_ASSERT_ERROR(_nb_machines_sedated_for_being_idle >= 0); - } - } - else - { - // The LLH has increased, let's switch to awakening mode :) - _last_decision = AWAKEN_MACHINES; - _inertial_number = 0; - - LOG_F(INFO, "Date=%g. Decided to do nothing now, but switching to awakening mode!", date); - } - } - } - - if (priority_job != nullptr && !priority_job_needs_awakenings) - { - // Let's make sure the priority job has not been delayed by the choices we made. - Schedule::JobAlloc priority_job_alloc2 = _inertial_schedule.add_job_first_fit(priority_job, _selector); - - if (_inertial_shutdown_debug) - { - LOG_F(1, "After decisions.%s", _inertial_schedule.to_string().c_str()); - } - - PPK_ASSERT_ERROR(priority_job_alloc2.has_been_inserted && - priority_job_alloc2.begin <= priority_job_alloc.begin, - "Invalid energy-related decisions have been made: " - "These decisions delayed the priority job. " - "Starting time before:%g. Starting time after:%g\n", - (double)priority_job_alloc.begin, (double)priority_job_alloc2.begin); - } - - _last_llh_value = llh; - _last_llh_date = date; - _llh_integral_of_preceding_monitoring_stage_slice = _llh_integral_since_last_monitoring_stage; - _llh_integral_since_last_monitoring_stage = 0; - - // Let's handle queue switches (first run, to make the first decisions) - IntervalSet machines_sedated_this_turn, machines_awakened_this_turn; - handle_queued_switches(_inertial_schedule, _machines_to_sedate, _machines_to_awaken, - machines_sedated_this_turn, machines_awakened_this_turn); - - if (machines_sedated_this_turn.size() > 0) - LOG_F(INFO, "Date=%g. Those machines should be put to sleep now: %s", - date, machines_sedated_this_turn.to_string_brackets().c_str()); - if (machines_awakened_this_turn.size() > 0) - LOG_F(INFO, "Date=%g. Those machines should be awakened now: %s", - date, machines_awakened_this_turn.to_string_brackets().c_str()); - - _machines_to_sedate -= machines_sedated_this_turn; - _machines_to_awaken -= machines_awakened_this_turn; - _machines_sedated_since_last_monitoring_stage_inertia = machines_sedated_this_turn; - _machines_awakened_since_last_monitoring_stage_inertia = machines_awakened_this_turn; - - // Let's make the inertial decisions - make_decisions_of_schedule(_inertial_schedule, false); - - IntervalSet machines_asleep_soon = (_asleep_machines + _switching_off_machines - _switching_on_machines) - + _machines_to_sedate - _machines_to_awaken; - PPK_ASSERT_ERROR((int)machines_asleep_soon.size() == _nb_machines_sedated_by_inertia + - _nb_machines_sedated_for_being_idle, - "Asleep machines inconsistency. nb_asleep_soon=%d (%s). nb_sedated_inertia=%d. " - "nb_sedated_idle=%d\n", - (int)machines_asleep_soon.size(), machines_asleep_soon.to_string_brackets().c_str(), - _nb_machines_sedated_by_inertia, _nb_machines_sedated_for_being_idle); - - if (priority_job_needs_awakenings) - { - // In this case, we can only try to add the priority job after doing the previously requested awakenings. - PPK_ASSERT_ERROR(priority_job != nullptr); - - _inertial_schedule.add_job_first_fit(priority_job, _selector, false); - } - - // Let's now try to sedate the machines which have been idle for too long - EnergyBackfillingIdleSleeper::update_idle_states(date, _inertial_schedule, _all_machines, - _idle_machines, _machines_idle_start_date); - IntervalSet machines_awake_soon = (_awake_machines + _switching_on_machines - _switching_off_machines) - + _machines_to_awaken - _machines_to_sedate; - - // Guard to prevent switch ON/OFF cycles - if (_switching_on_machines.size() == 0 && _machines_to_awaken.size() == 0) - { - IntervalSet machines_to_sedate_for_being_idle; - EnergyBackfillingIdleSleeper::select_idle_machines_to_sedate(date, - _idle_machines, machines_awake_soon, - priority_job, _machines_idle_start_date, - _needed_amount_of_idle_time_to_be_sedated, - machines_to_sedate_for_being_idle, - true); // <------------------------------------------------- Choice to make. - - if (machines_to_sedate_for_being_idle.size() > 0) - { - // Let's handle queue switches - machines_sedated_this_turn.clear(); - machines_awakened_this_turn.clear(); - IntervalSet empty_range; - - handle_queued_switches(_inertial_schedule, machines_to_sedate_for_being_idle, empty_range, - machines_sedated_this_turn, machines_awakened_this_turn); - - // A subset of those machines can be sedated (not all of them because it might be jobs - // in the future on some resources) - PPK_ASSERT_ERROR((machines_sedated_this_turn & machines_to_sedate_for_being_idle) == - machines_sedated_this_turn, - "The sedated machines are not the expected ones.Sedated=%s.\nExpected=subset of %s", - machines_sedated_this_turn.to_string_brackets().c_str(), - machines_to_sedate_for_being_idle.to_string_brackets().c_str()); - - PPK_ASSERT_ERROR(machines_awakened_this_turn == IntervalSet::empty_interval_set(), - "The awakened machines are not the expected ones.Awakened=%s.\nExpected=%s", - machines_awakened_this_turn.to_string_brackets().c_str(), - IntervalSet::empty_interval_set().to_string_brackets().c_str()); - - LOG_F(INFO, "Date=%g. Those machines should be put to sleep now for being idle: %s", - date, machines_sedated_this_turn.to_string_brackets().c_str()); - - - PPK_ASSERT_ERROR((_machines_to_awaken & _machines_to_sedate) == IntervalSet::empty_interval_set()); - - if (_inertial_shutdown_debug) - { - LOG_F(1, "Date=%g. Before make_decisions_of_schedule. %s", - date, _inertial_schedule.to_string().c_str()); - write_schedule_debug("_on_monitoring_before_make_decisions_of_schedule"); - } - - // Let's finally make the idle decisions! - make_decisions_of_schedule(_inertial_schedule, false); - - _nb_machines_sedated_for_being_idle += machines_sedated_this_turn.size(); - PPK_ASSERT_ERROR(_nb_machines_sedated_for_being_idle <= _nb_machines); - } - } - - if (_inertial_shutdown_debug) - LOG_F(1, "Date=%g. End of on_monitoring_stage", date); - - // Now that the decisions have been made, let's compute the LLH again. - llh = EasyBackfillingPlotLiquidLoadHorizon::compute_liquid_load_horizon(_inertial_schedule, - _queue, date); - if (_write_output_file) - { - int first_job_size = -1; - const Job * first_job = _queue->first_job_or_nullptr(); - if (first_job != nullptr) - first_job_size = first_job->nb_requested_resources; - - write_output_file(date, _queue->nb_jobs(), - first_job_size, - (double) _queue->compute_load_estimation(), - (double) llh); - } - - machines_asleep_soon = (_asleep_machines + _switching_off_machines - _switching_on_machines) - + _machines_to_sedate - _machines_to_awaken; - PPK_ASSERT_ERROR((int)machines_asleep_soon.size() == _nb_machines_sedated_by_inertia + - _nb_machines_sedated_for_being_idle, - "Asleep machines inconsistency. nb_asleep_soon=%d (%s). nb_sedated_inertia=%d. " - "nb_sedated_idle=%d\n", - (int)machines_asleep_soon.size(), machines_asleep_soon.to_string_brackets().c_str(), - _nb_machines_sedated_by_inertia, _nb_machines_sedated_for_being_idle); - - _last_llh_value = llh; - _last_llh_date = date; -} - -void EnergyBackfillingMonitoringInertialShutdown::select_machines_to_sedate(int nb_machines, - const IntervalSet &sedatable_machines, - const IntervalSet &machines_that_can_be_used_by_the_priority_job, - IntervalSet &machines_to_sedate, - const Job *priority_job) const -{ - PPK_ASSERT_ERROR(nb_machines <= (int)sedatable_machines.size()); - PPK_ASSERT_ERROR((sedatable_machines & (_awake_machines + _switching_on_machines)) == sedatable_machines); - - if (_sedating_policy == SEDATE_FIRST_MACHINES) - { - select_first_machines_to_sedate(nb_machines, _inertial_schedule, sedatable_machines, - machines_that_can_be_used_by_the_priority_job, machines_to_sedate, - priority_job); - } - else - PPK_ASSERT_ERROR(false, "Unknown sedating policy"); - - PPK_ASSERT_ERROR(machines_to_sedate.size() == (unsigned int)nb_machines); - PPK_ASSERT_ERROR((machines_to_sedate & sedatable_machines) == machines_to_sedate); -} - -void EnergyBackfillingMonitoringInertialShutdown::select_machines_to_awaken(int nb_machines, - const IntervalSet &awakable_machines, - IntervalSet &machines_to_awaken) const -{ - PPK_ASSERT_ERROR(nb_machines <= (int)awakable_machines.size()); - PPK_ASSERT_ERROR((awakable_machines & (_asleep_machines + _switching_off_machines)) == awakable_machines, - "awakable_machines should be a subset of union(_asleep_machines,_switching_off_machines).\n" - "awakable_machines=%s\n_asleep_machines=%s\n_switching_off_machines=%s\n" - "union(_asleep_machines,_switching_off_machines)=%s\n", - awakable_machines.to_string_brackets().c_str(), - _asleep_machines.to_string_brackets().c_str(), - _switching_off_machines.to_string_brackets().c_str(), - (_asleep_machines + _switching_off_machines).to_string_brackets().c_str()); - - if (_awakening_policy == AWAKEN_FIRST_MACHINES) - select_first_machines_to_awaken(nb_machines, _inertial_schedule, awakable_machines, machines_to_awaken); - else - PPK_ASSERT_ERROR(false, "Unknown awakening policy"); - - PPK_ASSERT_ERROR(machines_to_awaken.size() == (unsigned int) nb_machines); - PPK_ASSERT_ERROR((machines_to_awaken & awakable_machines) == machines_to_awaken); -} - -void EnergyBackfillingMonitoringInertialShutdown::select_first_machines_to_sedate(int nb_machines, const Schedule & schedule, - const IntervalSet & sedatable_machines, - const IntervalSet & machines_that_can_be_used_by_the_priority_job, - IntervalSet &machines_to_sedate, - const Job * priority_job) -{ - machines_to_sedate.clear(); - - // If there is a priority job, in order to make sure we don't delay it, we can compute how many machines we can "steal" - // from the "hole" in which it is supposed to be executed - IntervalSet stolen_machines; - IntervalSet really_stolable_machines = sedatable_machines & machines_that_can_be_used_by_the_priority_job; - int priority_job_nb_requested_resources = 0; - - if (priority_job != nullptr) - priority_job_nb_requested_resources = priority_job->nb_requested_resources; - - int nb_machines_that_can_be_stolen_from_priority_job = max(0, (int)(really_stolable_machines.size() - priority_job_nb_requested_resources)); - - auto slice_it = schedule.begin(); - while (slice_it != schedule.end()) - { - const Schedule::TimeSlice & slice = *slice_it; - IntervalSet sedatable_machines_now = (slice.available_machines & sedatable_machines) - machines_to_sedate; - - // The sedatable machines might annoy the priority job... - IntervalSet annoying_sedatable_machines = (sedatable_machines_now & (really_stolable_machines - stolen_machines)); - if (annoying_sedatable_machines != IntervalSet::empty_interval_set()) - { - // Let's see if we can "steal" some machines from the priority job. - int nb_machines_stolable_now = nb_machines_that_can_be_stolen_from_priority_job - stolen_machines.size(); - if (nb_machines_stolable_now > 0) - { - IntervalSet stolable_machines = annoying_sedatable_machines.left(min(nb_machines_stolable_now, (int)annoying_sedatable_machines.size())); - - int nb_machines_i_want_to_steal = min(stolable_machines.size(), nb_machines - machines_to_sedate.size()); - IntervalSet stolen_machines_now = stolable_machines.left(nb_machines_i_want_to_steal); - - stolen_machines += stolen_machines_now; - machines_to_sedate += stolen_machines_now; - } - } - - sedatable_machines_now -= annoying_sedatable_machines; - - if (sedatable_machines_now.size() + machines_to_sedate.size() >= (unsigned int)nb_machines) - { - machines_to_sedate += sedatable_machines_now.left(nb_machines - machines_to_sedate.size()); - return; - } - else - machines_to_sedate += sedatable_machines_now; - - slice_it++; - } - - PPK_ASSERT_ERROR(false, "Couldn't select %d machines to sedate :(.\n" - "sedatable_machines=%s.\n" - "priority_job=%p.\n" - "priority_job_nb_requested_resources=%d.\n" - "machines_that_can_be_used_by_the_priority_job=%s.\n%s\n", - nb_machines, sedatable_machines.to_string_brackets().c_str(), - priority_job, priority_job_nb_requested_resources, - machines_that_can_be_used_by_the_priority_job.to_string_brackets().c_str(), - schedule.to_string().c_str()); -} - -void EnergyBackfillingMonitoringInertialShutdown::select_first_machines_to_awaken(int nb_machines, const Schedule & schedule, - const IntervalSet &awakable_machines, - IntervalSet &machines_to_awaken) -{ - machines_to_awaken.clear(); - - auto slice_it = schedule.begin(); - while (slice_it != schedule.end()) - { - const Schedule::TimeSlice & slice = *slice_it; - IntervalSet awakable_machines_now = (compute_potentially_awaken_machines(slice) & awakable_machines) - machines_to_awaken; - - if (awakable_machines_now.size() + machines_to_awaken.size() >= (unsigned int)nb_machines) - { - machines_to_awaken += awakable_machines_now.left(nb_machines - machines_to_awaken.size()); - return; - } - else - machines_to_awaken += awakable_machines_now; - - slice_it++; - } - - PPK_ASSERT_ERROR(false, "Couldn't select %d machines to awaken :(. %s\n", nb_machines, schedule.to_string().c_str()); -} - -void EnergyBackfillingMonitoringInertialShutdown::handle_queued_switches(Schedule & schedule, - const IntervalSet & machines_to_sedate, - const IntervalSet & machines_to_awaken, - IntervalSet & machines_sedated_now, - IntervalSet & machines_awakened_now) -{ - if (_inertial_shutdown_debug) - { - LOG_F(1, "handle_queued_switches, begin.\n" - "machines_to_awaken: %s\nmachines_to_sedate: %s\n" - "_awake_machines: %s\n_asleep_machines: %s\n" - "_wakable_asleep_machines: %s\n" - "_non_wakable_asleep_machines: %s\n%s", - machines_to_awaken.to_string_brackets().c_str(), - machines_to_sedate.to_string_brackets().c_str(), - _awake_machines.to_string_brackets().c_str(), - _asleep_machines.to_string_brackets().c_str(), - _wakable_asleep_machines.to_string_brackets().c_str(), - _non_wakable_asleep_machines.to_string_brackets().c_str(), - _inertial_schedule.to_string().c_str()); - write_schedule_debug("_handle_queued_switches_begin"); - } - - // Let's sedate and awaken the desired machines into the _inertial_schedule - for (auto machine_it = machines_to_awaken.elements_begin(); machine_it != machines_to_awaken.elements_end(); machine_it++) - { - int machine_id = *machine_it; - Rational awakening_date = awaken_machine_as_soon_as_possible(schedule, machine_id); - - if (awakening_date == schedule.first_slice_begin()) - machines_awakened_now += machine_id; - } - - if (_inertial_shutdown_debug) - { - LOG_F(1, "handle_queued_switches, after awakenings.\n" - "machines_to_sedate: %s\n%s", - machines_to_sedate.to_string_brackets().c_str(), - _inertial_schedule.to_string().c_str()); - write_schedule_debug("_handle_queued_switches_after_awakenings"); - } - - for (auto machine_it = machines_to_sedate.elements_begin(); machine_it != machines_to_sedate.elements_end(); machine_it++) - { - int machine_id = *machine_it; - - Rational sedating_date = sedate_machines_at_the_furthest_moment(schedule, machine_id); - - if (sedating_date == schedule.first_slice_begin()) - machines_sedated_now += machine_id; - } - - if (_inertial_shutdown_debug) - { - LOG_F(1, "handle_queued_switches, end.\n" - "machines_sedated_now = %s\n" - "machines_awakened_now = %s\n%s", - machines_sedated_now.to_string_brackets().c_str(), - machines_awakened_now.to_string_brackets().c_str(), - _inertial_schedule.to_string().c_str()); - write_schedule_debug("_handle_queued_switches_end"); - } - -} - -void EnergyBackfillingMonitoringInertialShutdown::write_output_file(double date, - int nb_jobs_in_queue, - int first_job_size, - double load_in_queue, - double liquid_load_horizon) -{ - PPK_ASSERT_ERROR(_output_file.is_open()); - - const int buf_size = 256; - int nb_printed; - char * buf = (char*) malloc(sizeof(char) * buf_size); - - if (first_job_size != -1) - nb_printed = snprintf(buf, buf_size, "%g,%d,%d,%g,%g,%g\n", - date, nb_jobs_in_queue, first_job_size, - (double)_priority_job_starting_time_expectancy - date, - load_in_queue, liquid_load_horizon); - else - nb_printed = snprintf(buf, buf_size, "%g,%d,NA,NA,%g,%g\n", - date, nb_jobs_in_queue, - load_in_queue, liquid_load_horizon); - PPK_ASSERT_ERROR(nb_printed < buf_size - 1, - "Buffer too small, some information might have been lost"); - - _output_file.write(buf, strlen(buf)); - - free(buf); -} - -void EnergyBackfillingMonitoringInertialShutdown::write_schedule_debug(const string &filename_suffix) -{ - if (_really_write_svg_files) - { - char output_filename[256]; - snprintf(output_filename, 256, "%s/inertial_schedule_%06d%s.svg", - _output_dir.c_str(), _debug_output_id, filename_suffix.c_str()); - - _inertial_schedule.write_svg_to_file(output_filename); - } - - ++_debug_output_id; -} - -void EnergyBackfillingMonitoringInertialShutdown::compute_priority_job_and_related_stuff(Schedule &schedule, - const Queue * queue, - const Job *&priority_job, - ResourceSelector * priority_job_selector, - bool & priority_job_needs_awakenings, - Schedule::JobAlloc &first_insertion_alloc, - IntervalSet &priority_job_reserved_machines, - IntervalSet &machines_that_can_be_used_by_the_priority_job) -{ - priority_job = nullptr; - first_insertion_alloc.has_been_inserted = false; - priority_job_reserved_machines.clear(); - machines_that_can_be_used_by_the_priority_job.clear(); - - // Let's find in which time space the priority job should be executed - if (!queue->is_empty()) - { - priority_job = queue->first_job(); - // To do so, let's insert the priority job into the schedule. - - if (schedule.contains_job(priority_job)) - schedule.remove_job(priority_job); - - first_insertion_alloc = schedule.add_job_first_fit(priority_job, priority_job_selector, false); - - if (!first_insertion_alloc.has_been_inserted) - { - // The priority job has not been inserted. This is probably because some machines must be awakened first. - priority_job_needs_awakenings = true; - } - else - { - priority_job_reserved_machines = first_insertion_alloc.used_machines; - - // Now we want to determine which machines the priority job can use at this period of time. - // To do so, let's remove it from the schedule then compute all available machines during - // this period of time. - PPK_ASSERT_ERROR(schedule.contains_job(priority_job)); - schedule.remove_job(priority_job); - PPK_ASSERT_ERROR(!schedule.contains_job(priority_job)); - - machines_that_can_be_used_by_the_priority_job = schedule.available_machines_during_period(first_insertion_alloc.begin, first_insertion_alloc.end); - // Coherency checks: the previous allocation should be a subset of these machines - PPK_ASSERT_ERROR(((first_insertion_alloc.used_machines & machines_that_can_be_used_by_the_priority_job) == first_insertion_alloc.used_machines) && - ((first_insertion_alloc.used_machines + machines_that_can_be_used_by_the_priority_job) == machines_that_can_be_used_by_the_priority_job), - "The priority job '%s' has been allocated in an invalid place. " - "It should have been among %s, but it is in %s.\n%s", - priority_job->id.c_str(), machines_that_can_be_used_by_the_priority_job.to_string_brackets().c_str(), - first_insertion_alloc.used_machines.to_string_brackets().c_str(), - schedule.to_string().c_str()); - } - } -} - -Rational EnergyBackfillingMonitoringInertialShutdown::compute_priority_job_starting_time_expectancy(const Schedule &schedule, - const Job *priority_job) -{ - if (priority_job == nullptr) - return -1; - - Schedule copy = schedule; - - // Let's remove the job from the schedule if it exists - copy.remove_job_if_exists(priority_job); - - // Let's remove every sleeping machine from it - IntervalSet machines_asleep_soon = (_asleep_machines + _switching_off_machines - _switching_on_machines) - + _machines_to_sedate - _machines_to_awaken; - - for (auto mit = machines_asleep_soon.elements_begin(); mit != machines_asleep_soon.elements_end(); ++mit) - { - int machine_id = *mit; - EnergyBackfilling::awaken_machine_as_soon_as_possible(copy, machine_id); - } - - Schedule::JobAlloc alloc = copy.add_job_first_fit(priority_job, _selector, true); - - PPK_ASSERT_ERROR(alloc.has_been_inserted); - return alloc.begin; -} diff --git a/src/algo/energy_bf_monitoring_inertial_shutdown.hpp b/src/algo/energy_bf_monitoring_inertial_shutdown.hpp deleted file mode 100644 index 4efe12c6c76b772ca8a502c46323130c6c9bf3b1..0000000000000000000000000000000000000000 --- a/src/algo/energy_bf_monitoring_inertial_shutdown.hpp +++ /dev/null @@ -1,158 +0,0 @@ -#pragma once - -#include "energy_bf_monitoring_period.hpp" - -#include <fstream> - -class EnergyBackfillingMonitoringInertialShutdown : public EnergyBackfillingMonitoringPeriod -{ -public: - enum DecisionType - { - SEDATE_MACHINES, - AWAKEN_MACHINES, - }; - - enum MachinesSedatingPolicy - { - SEDATE_FIRST_MACHINES - }; - - enum MachinesAwakeningPolicy - { - AWAKEN_FIRST_MACHINES - }; - - enum InertialAlterationType - { - SUM, - PRODUCT - }; - -public: - EnergyBackfillingMonitoringInertialShutdown(Workload * workload, - SchedulingDecision * decision, - Queue * queue, - ResourceSelector * selector, - double rjms_delay, - rapidjson::Document * variant_options); - - virtual void on_simulation_start(double date, const rapidjson::Value & batsim_config); - - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); - - virtual void on_monitoring_stage(double date); - - - void select_machines_to_sedate(int nb_machines, - const IntervalSet & sedatable_machines, - const IntervalSet & machines_that_can_be_used_by_the_priority_job, - IntervalSet & machines_to_sedate, - const Job * priority_job) const; - - void select_machines_to_awaken(int nb_machines, - const IntervalSet & awakable_machines, - IntervalSet & machines_to_awaken) const; - - static void select_first_machines_to_sedate(int nb_machines, - const Schedule & schedule, - const IntervalSet & sedatable_machines, - const IntervalSet &machines_that_can_be_used_by_the_priority_job, - IntervalSet & machines_to_sedate, - const Job * priority_job = nullptr); - - static void select_first_machines_to_awaken(int nb_machines, - const Schedule & schedule, - const IntervalSet & awakable_machines, - IntervalSet & machines_to_awaken); - - void write_schedule_debug(const std::string & filename_suffix = ""); - - static void compute_priority_job_and_related_stuff(Schedule & schedule, - const Queue * queue, - const Job *& priority_job, - ResourceSelector * priority_job_selector, - bool & priority_job_needs_awakenings, - Schedule::JobAlloc & first_insertion_alloc, - IntervalSet & priority_job_reserved_machines, - IntervalSet & machines_that_can_be_used_by_the_priority_job); - - Rational compute_priority_job_starting_time_expectancy(const Schedule & schedule, - const Job * priority_job); - - -protected: - /** - - * @details This method will add fake jobs into _inertial_schedule and update _machines_to_sedate - * and _machines_to_awaken if the switches have been done in the first slice. - */ - - /** - * @brief Handles the switches ON/OFF that must be done but that couldn't be done earlier. - * @param[in,out] schedule The Schedule on which the modifications should be done - * @param[in] machines_to_sedate The machines that must be sedated as soon as possible - * @param[in] machines_to_awaken The machines that must be awakened as soon as possible - * @param[out] machines_sedated_now The machines that have been sedated in the first time slice - * @param[out] machines_awakened_now The machines that have been awakened in the first time slice - */ - void handle_queued_switches(Schedule & schedule, - const IntervalSet & machines_to_sedate, - const IntervalSet & machines_to_awaken, - IntervalSet & machines_sedated_now, - IntervalSet & machines_awakened_now); - - void write_output_file(double date, - int nb_jobs_in_queue, - int first_job_size, - double load_in_queue, - double liquid_load_horizon); - -protected: - Schedule _inertial_schedule; - -private: - std::ofstream _output_file; - -protected: - bool _inertial_shutdown_debug = false; - bool _really_write_svg_files = false; - bool _write_output_file = true; - int _debug_output_id = 0; - - // Algorithm real (scientific) parameters: - bool _allow_future_switches = true; - Rational _upper_llh_threshold = 1e100; - InertialAlterationType _alteration_type = PRODUCT; - Rational _inertial_alteration_number = 2; - - bool _sedate_idle_on_classical_events = false; - double _needed_amount_of_idle_time_to_be_sedated = 1e18; - int _nb_machines_sedated_for_being_idle = 0; - int _nb_machines_sedated_by_inertia = 0; - std::map<int, Rational> _machines_idle_start_date; - IntervalSet _idle_machines; - - bool _first_monitoring_stage = true; - - Rational _priority_job_starting_time_expectancy = 0; - - DecisionType _last_decision; - int _inertial_number; - Rational _last_llh_value = 0; //!< The value of the latest computed Liquid Load Horizon - Rational _last_llh_date = 0; //!< The date at which the latest Liquid Load Horizon has been computed - Rational _llh_integral_since_last_monitoring_stage = 0; //!< The sum over time of the Liquid Load Horizon. This sum is computed by the sum of the areas of trapezoids. - Rational _llh_integral_of_preceding_monitoring_stage_slice = 0; //!< The precedent value of _llh_integral_since_last_monitoring_stage - - MachinesSedatingPolicy _sedating_policy = SEDATE_FIRST_MACHINES; - MachinesAwakeningPolicy _awakening_policy = AWAKEN_FIRST_MACHINES; - - IntervalSet _machines_to_sedate; - IntervalSet _machines_to_awaken; - - // The pstates changes that were asked since the last monitoring stage - IntervalSet _machines_awakened_since_last_monitoring_stage_inertia; - IntervalSet _machines_sedated_since_last_monitoring_stage_inertia; -}; diff --git a/src/algo/energy_bf_monitoring_period.cpp b/src/algo/energy_bf_monitoring_period.cpp deleted file mode 100644 index 4da14adf48de917afedb271862ec50197b14cdd1..0000000000000000000000000000000000000000 --- a/src/algo/energy_bf_monitoring_period.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "energy_bf_monitoring_period.hpp" - -#include <loguru.hpp> - -#include "../pempek_assert.hpp" - -using namespace std; - -EnergyBackfillingMonitoringPeriod::EnergyBackfillingMonitoringPeriod(Workload * workload, - SchedulingDecision * decision, - Queue * queue, - ResourceSelector * selector, - double rjms_delay, - rapidjson::Document * variant_options) : - EnergyBackfilling(workload, decision, queue, selector, rjms_delay, variant_options) -{ - PPK_ASSERT_ERROR(variant_options->HasMember("output_dir"), "Invalid options JSON object: Member 'output_dir' cannot be found"); - PPK_ASSERT_ERROR((*variant_options)["output_dir"].IsString(), "Invalid options JSON object: Member 'output_dir' must be a string"); - _output_dir = (*variant_options)["output_dir"].GetString(); - - PPK_ASSERT_ERROR(variant_options->HasMember("monitoring_period"), "Invalid options JSON object: Member 'monitoring_period' cannot be found"); - PPK_ASSERT_ERROR((*variant_options)["monitoring_period"].IsNumber(), "Invalid options JSON object: Member 'monitoring_period' must be a number"); - _period_between_monitoring_stages = (*variant_options)["monitoring_period"].GetDouble(); -} - -EnergyBackfillingMonitoringPeriod::~EnergyBackfillingMonitoringPeriod() -{ - -} - -void EnergyBackfillingMonitoringPeriod::on_simulation_end(double date) -{ - EnergyBackfilling::on_simulation_end(date); - - PPK_ASSERT_ERROR(_simulation_finished == false); - _simulation_finished = true; - - LOG_F(INFO, "EnergyBackfillingMonitoringPeriod: 'End of simulation' message received from Batsim. date=%g", date); -} - -void EnergyBackfillingMonitoringPeriod::on_job_release(double date, const std::vector<string> &job_ids) -{ - if (!_monitoring_period_launched) - { - _next_monitoring_period_expected_date = date + _period_between_monitoring_stages; - - LOG_F(INFO, "EnergyBackfillingMonitoringPeriod: First monitoring nop is expected to be at date=%g", - (double) _next_monitoring_period_expected_date); - - _decision->add_call_me_later((double)(_next_monitoring_period_expected_date), date); - _nb_call_me_later_running++; - _monitoring_period_launched = true; - } - - EnergyBackfilling::on_job_release(date, job_ids); -} - -void EnergyBackfillingMonitoringPeriod::on_requested_call(double date) -{ - EnergyBackfilling::on_requested_call(date); - LOG_F(INFO, "on_requested_call, date = %g", date); - - if (!_simulation_finished) - { - // Let's execute on_monitoring_stage - on_monitoring_stage(date); - - if (!_stop_sending_call_me_later) - { - // Let's request a call for the next monitoring stage - _next_monitoring_period_expected_date = date + _period_between_monitoring_stages; - _decision->add_call_me_later((double)(_next_monitoring_period_expected_date), date); - _nb_call_me_later_running++; - - LOG_F(INFO, "EnergyBackfillingMonitoringPeriod: 'Chose to launch a call_me_later at %g", - (double)_next_monitoring_period_expected_date); - } - } -} - -void EnergyBackfillingMonitoringPeriod::on_monitoring_stage(double date) -{ - LOG_F(INFO, "EnergyBackfillingMonitoringPeriod: Monitoring stage at date=%g", date); -} - -Rational EnergyBackfillingMonitoringPeriod::period() const -{ - return _period_between_monitoring_stages; -} - -Rational EnergyBackfillingMonitoringPeriod::next_monitoring_stage_date() const -{ - return _next_monitoring_period_expected_date; -} - -bool EnergyBackfillingMonitoringPeriod::is_simulation_finished() const -{ - return _simulation_finished; -} - - diff --git a/src/algo/energy_bf_monitoring_period.hpp b/src/algo/energy_bf_monitoring_period.hpp deleted file mode 100644 index 525e27fee3745e1b1afe4fd5b459e2fbef1a54e1..0000000000000000000000000000000000000000 --- a/src/algo/energy_bf_monitoring_period.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "energy_bf.hpp" - -class EnergyBackfillingMonitoringPeriod : public EnergyBackfilling -{ -public: - EnergyBackfillingMonitoringPeriod(Workload * workload, - SchedulingDecision * decision, - Queue * queue, - ResourceSelector * selector, - double rjms_delay, - rapidjson::Document * variant_options); - - virtual ~EnergyBackfillingMonitoringPeriod(); - - virtual void on_simulation_end(double date); - - virtual void on_job_release(double date, const std::vector<std::string> & job_ids); - - virtual void on_requested_call(double date); - - virtual void on_monitoring_stage(double date); - -public: - Rational period() const; - Rational next_monitoring_stage_date() const; - bool is_simulation_finished() const; - -protected: - std::string _output_dir; - bool _stop_sending_call_me_later = false; - -private: - bool _monitoring_period_launched = false; - bool _simulation_finished = false; - Rational _period_between_monitoring_stages; - Rational _next_monitoring_period_expected_date; - -}; diff --git a/src/algo/energy_watcher.cpp b/src/algo/energy_watcher.cpp deleted file mode 100644 index 0905f7899fec453db0cda714e7b36702d92385ae..0000000000000000000000000000000000000000 --- a/src/algo/energy_watcher.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "energy_watcher.hpp" - -#include <loguru.hpp> - -#include "../pempek_assert.hpp" - -using namespace std; - -EnergyWatcher::EnergyWatcher(Workload * workload, - SchedulingDecision * decision, - Queue * queue, - ResourceSelector * selector, - double rjms_delay, - rapidjson::Document * variant_options) : - ISchedulingAlgorithm(workload, decision, queue, selector, rjms_delay, variant_options) -{ - -} - -EnergyWatcher::~EnergyWatcher() -{ - -} - -void EnergyWatcher::on_simulation_start(double date, const rapidjson::Value & batsim_config) -{ - (void) date; - - _machines.insert(IntervalSet::ClosedInterval(0, _nb_machines - 1)); - PPK_ASSERT_ERROR(_machines.size() == (unsigned int) _nb_machines); - - // TODO: print warning if time sharing is disabled - (void) batsim_config; -} - -void EnergyWatcher::on_simulation_end(double date) -{ - (void) date; -} - -void EnergyWatcher::make_decisions(double date, - SortableJobOrder::UpdateInformation *update_info, - SortableJobOrder::CompareInformation *compare_info) -{ - (void) update_info; - (void) compare_info; - - /* This algorithm execute the jobs in order in arrival in a sequence. - It queries about the energy consumption at each job arrival and checks - that it is non-decreasing. */ - - if (_consumed_joules_updated_recently) - { - if (_previous_energy < 0) - _previous_energy = _consumed_joules; - - PPK_ASSERT_ERROR(_consumed_joules - _previous_energy >= -1e-6, - "Energy consumption inconsistency: it should be non-decreasing. " - "Received %g but previous value is %g.", - _consumed_joules, _previous_energy); - LOG_F(INFO, "Updating consumed joules. Now=%g. Before=%g.", - _consumed_joules, _previous_energy); - _previous_energy = _consumed_joules; - } - - PPK_ASSERT_ERROR(_jobs_killed_recently.size() == 0, - "Jobs have been killed, which should not happen with this algorithm."); - - if (_jobs_ended_recently.size() > 0) - { - PPK_ASSERT_ERROR(_is_machine_busy == true); - _is_machine_busy = false; - } - - // Let's handle recently released jobs - for (const string & new_job_id : _jobs_released_recently) - { - const Job * new_job = (*_workload)[new_job_id]; - - if (new_job->nb_requested_resources > _nb_machines) - { - // The job is too big for the machine -> reject - _decision->add_reject_job(new_job_id, date); - } - else - { - // THe job fits in the machine -> added in queue - _queue->append_job(new_job, update_info); - } - } - - // Query consumed energy if new jobs have been submitted - if (_jobs_released_recently.size() > 0) - { - _decision->add_query_energy_consumption(date); - } - - // Execute a job if the machine is completely idle - execute_job_if_whole_machine_is_idle(date); -} - -void EnergyWatcher::execute_job_if_whole_machine_is_idle(double date) -{ - const Job * job = _queue->first_job_or_nullptr(); - - // If all the machines are available and that there are jobs to compute - if (!_is_machine_busy && job != nullptr) - { - IntervalSet machines_to_use = _machines.left(job->nb_requested_resources); - _decision->add_execute_job(job->id, machines_to_use, date); - - _is_machine_busy = true; - _queue->remove_job(job); - } -} - diff --git a/src/algo/energy_watcher.hpp b/src/algo/energy_watcher.hpp deleted file mode 100644 index a60fb66028ab6392be221d0dd33b7714a1239b5d..0000000000000000000000000000000000000000 --- a/src/algo/energy_watcher.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "../isalgorithm.hpp" - -class EnergyWatcher : public ISchedulingAlgorithm -{ -public: - EnergyWatcher(Workload * workload, SchedulingDecision * decision, Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options); - - virtual ~EnergyWatcher(); - - virtual void on_simulation_start(double date, const rapidjson::Value & batsim_config); - - virtual void on_simulation_end(double date); - - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); - - void execute_job_if_whole_machine_is_idle(double date); - -private: - IntervalSet _machines; - bool _is_machine_busy = false; - double _previous_energy = -1; -}; diff --git a/src/algo/fcfs_fast.cpp b/src/algo/fcfs_fast.cpp deleted file mode 100644 index edac4dbcc217b404e6ebbb05a954137a8fc3f694..0000000000000000000000000000000000000000 --- a/src/algo/fcfs_fast.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include "fcfs_fast.hpp" - -#include "../pempek_assert.hpp" - -FCFSFast::FCFSFast(Workload *workload, - SchedulingDecision *decision, Queue *queue, ResourceSelector *selector, - double rjms_delay, rapidjson::Document *variant_options) : - ISchedulingAlgorithm(workload, decision, queue, selector, rjms_delay, - variant_options) -{} - -FCFSFast::~FCFSFast() -{} - -void FCFSFast::on_simulation_start(double date, - const rapidjson::Value &batsim_config) -{ - (void) date; - (void) batsim_config; - - _available_machines.insert(IntervalSet::ClosedInterval(0, _nb_machines - 1)); - _nb_available_machines = _nb_machines; - PPK_ASSERT_ERROR(_available_machines.size() == (unsigned int) _nb_machines); -} - -void FCFSFast::on_simulation_end(double date) -{ - (void) date; -} - -void FCFSFast::make_decisions(double date, - SortableJobOrder::UpdateInformation *update_info, - SortableJobOrder::CompareInformation *compare_info) -{ - (void) update_info; - (void) compare_info; - - // This algorithm is a fast version of FCFS without backfilling. - // It is meant to be fast in the usual case, not to handle corner cases. - // It is not meant to be easily readable or hackable ;). - - // This fast FCFS variant in a few words: - // - only handles the FCFS queue order - // - only handles the basic resource selection policy - // - only handles finite jobs (no switchoff) - // - only handles time as floating-point (-> precision errors). - - bool job_ended = false; - - // Handle newly finished jobs - for (const std::string & ended_job_id : _jobs_ended_recently) - { - job_ended = true; - - Job * finished_job = (*_workload)[ended_job_id]; - - // Update data structures - _available_machines.insert(_current_allocations[ended_job_id]); - _nb_available_machines += finished_job->nb_requested_resources; - _current_allocations.erase(ended_job_id); - } - - // If jobs have finished, execute jobs as long as they fit - if (job_ended) - { - for (auto job_it = _pending_jobs.begin(); - job_it != _pending_jobs.end(); ) - { - Job * pending_job = *job_it; - if (pending_job->nb_requested_resources <= _nb_available_machines) - { - IntervalSet machines = _available_machines.left( - pending_job->nb_requested_resources); - _decision->add_execute_job(pending_job->id, - machines, date); - - // Update data structures - _available_machines -= machines; - _nb_available_machines -= pending_job->nb_requested_resources; - _current_allocations[pending_job->id] = machines; - job_it = _pending_jobs.erase(job_it); - } - else - { - // The job becomes priority! - // As there is no backfilling, we can simply leave this loop. - break; - } - } - } - - // Handle newly released jobs - for (const std::string & new_job_id : _jobs_released_recently) - { - Job * new_job = (*_workload)[new_job_id]; - - // Is this job valid? - if (new_job->nb_requested_resources > _nb_machines) - { - // Invalid! - _decision->add_reject_job(new_job_id, date); - continue; - } - - // Is there a waiting job? - if (!_pending_jobs.empty()) - { - // Yes. The new job is queued up. - _pending_jobs.push_back(new_job); - } - else - { - // No, the queue is empty. - // Can the new job be executed now? - if (new_job->nb_requested_resources <= _nb_available_machines) - { - // Yes, the job can be executed right away! - IntervalSet machines = _available_machines.left( - new_job->nb_requested_resources); - _decision->add_execute_job(new_job_id, machines, date); - - // Update data structures - _available_machines -= machines; - _nb_available_machines -= new_job->nb_requested_resources; - _current_allocations[new_job_id] = machines; - } - else - { - // No. The job is queued up. - _pending_jobs.push_back(new_job); - } - } - } -} diff --git a/src/algo/fcfs_fast.hpp b/src/algo/fcfs_fast.hpp deleted file mode 100644 index 9c287ea7d5f49ed3f461570686e75e179a4b32fe..0000000000000000000000000000000000000000 --- a/src/algo/fcfs_fast.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include <unordered_map> -#include <list> - -#include "../isalgorithm.hpp" -#include "../json_workload.hpp" -#include "../locality.hpp" - -class FCFSFast : public ISchedulingAlgorithm -{ -public: - FCFSFast(Workload * workload, SchedulingDecision * decision, - Queue * queue, ResourceSelector * selector, - double rjms_delay, - rapidjson::Document * variant_options); - virtual ~FCFSFast(); - - virtual void on_simulation_start(double date, - const rapidjson::Value & batsim_config); - - virtual void on_simulation_end(double date); - - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); - -private: - // Machines currently available - IntervalSet _available_machines; - int _nb_available_machines = -1; - - // Pending jobs (queue) - std::list<Job *> _pending_jobs; - - // Allocations of running jobs - std::unordered_map<std::string, IntervalSet> _current_allocations; -}; diff --git a/src/algo/filler.cpp b/src/algo/filler.cpp deleted file mode 100644 index ecffb7887d7a907ba211760cdbba4ed497f1424c..0000000000000000000000000000000000000000 --- a/src/algo/filler.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include "filler.hpp" - -#include <loguru.hpp> - -#include "../json_workload.hpp" -#include "../decision.hpp" -#include "../pempek_assert.hpp" - -using namespace std; - -Filler::Filler(Workload *workload, SchedulingDecision * decision, Queue * queue, - ResourceSelector *selector, double rjms_delay, rapidjson::Document *variant_options) : - ISchedulingAlgorithm(workload, decision, queue, selector, rjms_delay, variant_options) -{ - if (variant_options->HasMember("fraction_of_machines_to_use")) - { - PPK_ASSERT_ERROR((*variant_options)["fraction_of_machines_to_use"].IsNumber(), - "Invalid options: 'fraction_of_machines_to_use' should be a number"); - fraction_of_machines_to_use = (*variant_options)["fraction_of_machines_to_use"].GetDouble(); - PPK_ASSERT_ERROR(fraction_of_machines_to_use > 0 && fraction_of_machines_to_use <= 1, - "Invalid options: 'fraction_of_machines_to_use' should be in ]0,1] " - "but got value=%g", fraction_of_machines_to_use); - } - - if (variant_options->HasMember("custom_mapping")) - { - PPK_ASSERT_ERROR((*variant_options)["custom_mapping"].IsBool(), - "Invalid options: 'custom_mapping' should be a boolean"); - custom_mapping = (*variant_options)["custom_mapping"].GetBool(); - } - - if (variant_options->HasMember("set_job_metadata")) - { - PPK_ASSERT_ERROR((*variant_options)["set_job_metadata"].IsBool(), - "Invalid options: 'set_job_metadata' should be a boolean"); - set_job_metadata = (*variant_options)["set_job_metadata"].GetBool(); - } - - LOG_SCOPE_FUNCTION(INFO); - LOG_F(INFO, "custom_mapping: %s", custom_mapping?"true":"false"); - LOG_F(INFO, "fraction_of_machines_to_use: %g", fraction_of_machines_to_use); - LOG_F(INFO, "set_job_metadata: %d", set_job_metadata); -} - -Filler::~Filler() -{ - -} - -void Filler::on_simulation_start(double date, const rapidjson::Value & batsim_config) -{ - (void) date; - (void) batsim_config; - - available_machines.insert(IntervalSet::ClosedInterval(0, _nb_machines - 1)); - PPK_ASSERT_ERROR(available_machines.size() == (unsigned int) _nb_machines); -} - -void Filler::on_simulation_end(double date) -{ - (void) date; -} - -void Filler::make_decisions(double date, - SortableJobOrder::UpdateInformation *update_info, - SortableJobOrder::CompareInformation *compare_info) -{ - // Let's update available machines - for (const string & ended_job_id : _jobs_ended_recently) - { - available_machines.insert(current_allocations[ended_job_id]); - current_allocations.erase(ended_job_id); - } - - // Handle machine (un)availability from user events - unavailable_machines -= _machines_that_became_available_recently; - unavailable_machines += _machines_that_became_unavailable_recently; - - // Let's handle recently released jobs - for (const string & new_job_id : _jobs_released_recently) - { - const Job * new_job = (*_workload)[new_job_id]; - - if (new_job->nb_requested_resources > _nb_machines) - _decision->add_reject_job(new_job_id, date); - else - _queue->append_job(new_job, update_info); - } - - // Queue sorting - _queue->sort_queue(update_info, compare_info); - - fill(date); -} - -void Filler::fill(double date) -{ - IntervalSet usable_machines = available_machines - unavailable_machines; - if (_debug) - LOG_F(1, "fill, usable_machines=%s", usable_machines.to_string_hyphen().c_str()); - - int nb_usable = usable_machines.size(); - for (auto job_it = _queue->begin(); job_it != _queue->end() && nb_usable > 0; ) - { - const Job * job = (*job_it)->job; - - // If it fits I sits (http://knowyourmeme.com/memes/if-it-fits-i-sits) - IntervalSet used_machines; - - if (_selector->fit(job, usable_machines, used_machines)) - { - // Fewer machines might be used that those selected by the fitting algorithm - int nb_machines_to_allocate = ceil(fraction_of_machines_to_use * job->nb_requested_resources); - PPK_ASSERT_ERROR(nb_machines_to_allocate > 0 && nb_machines_to_allocate <= job->nb_requested_resources); - used_machines = used_machines.left(nb_machines_to_allocate); - - if (custom_mapping) - { - vector<int> executor_to_allocated_resource_mapping; - executor_to_allocated_resource_mapping.resize(job->nb_requested_resources); - for (int i = 0; i < job->nb_requested_resources; ++i) - executor_to_allocated_resource_mapping[i] = i % nb_machines_to_allocate; - _decision->add_execute_job(job->id, used_machines, date, executor_to_allocated_resource_mapping); - } - else - { - _decision->add_execute_job(job->id, used_machines, date); - } - - current_allocations[job->id] = used_machines; - - usable_machines.remove(used_machines); - available_machines.remove(used_machines); - nb_usable -= used_machines.size(); - - if (set_job_metadata) - _decision->add_set_job_metadata(job->id, - "just some metadata for job " + job->id, - date); - - job_it = _queue->remove_job(job); - } - else - job_it++; - } -} diff --git a/src/algo/filler.hpp b/src/algo/filler.hpp deleted file mode 100644 index 2aa15d94e50a442f02b220e44a984fda3fb613a4..0000000000000000000000000000000000000000 --- a/src/algo/filler.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "../isalgorithm.hpp" - -#include <vector> -#include <set> -#include <list> -#include <map> - -#include "../locality.hpp" -#include <intervalset.hpp> - -class Workload; -class SchedulingDecision; - -class Filler : public ISchedulingAlgorithm -{ -public: - Filler(Workload * workload, SchedulingDecision * decision, Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options); - - virtual ~Filler(); - - virtual void on_simulation_start(double date, const rapidjson::Value & batsim_config); - - virtual void on_simulation_end(double date); - - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); -private: - void fill(double date); - -private: - double fraction_of_machines_to_use = 1; //! In ]0,1]. If job requests 42 machines, the scheduler will allocate ceil(42*rho) machines. - bool set_job_metadata = false; //! If set to true, metadata will be associated to jobs when they are started. - bool custom_mapping = true; - - IntervalSet available_machines; // Corresponds to classical availability: no job is running on those machines. - IntervalSet unavailable_machines; // This is NOT the complement of available_machines! This correspond to user-supplied events, that may overlap strangely with job executions as I write these lines. - std::map<std::string, IntervalSet> current_allocations; - bool _debug = true; -}; diff --git a/src/algo/killer.cpp b/src/algo/killer.cpp deleted file mode 100644 index e0ce47b7243f7b58100bc8acdf5f075b76740bdb..0000000000000000000000000000000000000000 --- a/src/algo/killer.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "killer.hpp" - -#include <loguru.hpp> - -#include "../pempek_assert.hpp" - -using namespace std; - -Killer::Killer(Workload *workload, - SchedulingDecision *decision, - Queue *queue, - ResourceSelector *selector, - double rjms_delay, - rapidjson::Document *variant_options) : - ISchedulingAlgorithm(workload, decision, queue, selector, rjms_delay, variant_options) -{ - if (variant_options->HasMember("nb_kills_per_job")) - { - PPK_ASSERT_ERROR((*variant_options)["nb_kills_per_job"].IsInt(), - "Bad algo options: nb_kills_per_job should be an integer"); - nb_kills_per_job = (*variant_options)["nb_kills_per_job"].GetInt(); - PPK_ASSERT_ERROR(nb_kills_per_job >= 0, - "Bad algo options: nb_kills_per_job should be positive (got %d)", - nb_kills_per_job); - } - - if (variant_options->HasMember("delay_before_kill")) - { - PPK_ASSERT_ERROR((*variant_options)["delay_before_kill"].IsNumber(), - "Bad algo options: delay_before_kill should be an integer"); - delay_before_kill = (*variant_options)["delay_before_kill"].GetDouble(); - PPK_ASSERT_ERROR(delay_before_kill >= 0, - "Bad algo options: delay_before_kill should be positive (got %g)", - delay_before_kill); - } - - LOG_SCOPE_FUNCTION(INFO); - LOG_F(INFO, "nb_kills_per_job: %d", nb_kills_per_job); - LOG_F(INFO, "delay_before_kill: %g", delay_before_kill); -} - -Killer::~Killer() -{ - -} - -void Killer::on_simulation_start(double date, const rapidjson::Value & batsim_config) -{ - (void) date; - (void) batsim_config; - - available_machines.insert(IntervalSet::ClosedInterval(0, _nb_machines - 1)); - PPK_ASSERT_ERROR(available_machines.size() == (unsigned int) _nb_machines); -} - -void Killer::on_simulation_end(double date) -{ - (void) date; -} - -void Killer::make_decisions(double date, - SortableJobOrder::UpdateInformation *update_info, - SortableJobOrder::CompareInformation *compare_info) -{ - LOG_F(1, "Date: %g. Available machines: %s", date, available_machines.to_string_brackets().c_str()); - - // Let's update available machines - for (const string & ended_job_id : _jobs_ended_recently) - { - int nb_available_before = available_machines.size(); - available_machines.insert(current_allocations[ended_job_id]); - PPK_ASSERT_ERROR(nb_available_before + (*_workload)[ended_job_id]->nb_requested_resources == (int)available_machines.size()); - current_allocations.erase(ended_job_id); - } - - LOG_F(1, "Date: %g. Available machines: %s", date, available_machines.to_string_brackets().c_str()); - - // Let's handle recently released jobs - for (const string & new_job_id : _jobs_released_recently) - { - const Job * new_job = (*_workload)[new_job_id]; - - if (new_job->nb_requested_resources > _nb_machines) - _decision->add_reject_job(new_job_id, date); - else - _queue->append_job(new_job, update_info); - } - - // Queue sorting - _queue->sort_queue(update_info, compare_info); - - // Let's now do a scheduling pass. - // This algorithm can only execute one job by call, the priority one. - // It is executed if it there are enough available resources. - // The job kill is then queued 10 secondes after its starting. - - if (!_queue->is_empty()) - { - const Job * job = _queue->first_job(); - - if (job->nb_requested_resources <= (int)available_machines.size()) - { - IntervalSet used_machines; - if (_selector->fit(job, available_machines, used_machines)) - { - _decision->add_execute_job(job->id, used_machines, date); - current_allocations[job->id] = used_machines; - available_machines.remove(used_machines); - _queue->remove_job(job); - - for (int i = 0; i < nb_kills_per_job; ++i) - { - date += delay_before_kill; - _decision->add_kill_job({job->id}, date); - } - } - } - } - - LOG_F(1, "Date: %g. Available machines: %s", date, available_machines.to_string_brackets().c_str()); -} diff --git a/src/algo/killer.hpp b/src/algo/killer.hpp deleted file mode 100644 index 70c77748d8128c633733f977aca3f2fdbc9ef635..0000000000000000000000000000000000000000 --- a/src/algo/killer.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "../isalgorithm.hpp" - -#include <vector> -#include <set> -#include <list> -#include <map> - -#include "../locality.hpp" -#include <intervalset.hpp> - -class Workload; -class SchedulingDecision; - -class Killer : public ISchedulingAlgorithm -{ -public: - Killer(Workload * workload, SchedulingDecision * decision, Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options); - - virtual ~Killer(); - - virtual void on_simulation_start(double date, const rapidjson::Value & batsim_config); - - virtual void on_simulation_end(double date); - - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); - -private: - IntervalSet available_machines; - std::map<std::string, IntervalSet> current_allocations; - int nb_kills_per_job = 1; - double delay_before_kill = 10; -}; diff --git a/src/algo/killer2.cpp b/src/algo/killer2.cpp deleted file mode 100644 index 66f7bc693e9daccb0079566e4f6b8b5a1998758a..0000000000000000000000000000000000000000 --- a/src/algo/killer2.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include "killer2.hpp" - -#include <loguru.hpp> - -#include "../pempek_assert.hpp" - -using namespace std; - -Killer2::Killer2(Workload *workload, - SchedulingDecision *decision, - Queue *queue, - ResourceSelector *selector, - double rjms_delay, - rapidjson::Document *variant_options) : - ISchedulingAlgorithm(workload, decision, queue, selector, rjms_delay, variant_options) -{ - -} - -Killer2::~Killer2() -{ - -} - -void Killer2::on_simulation_start(double date, const rapidjson::Value & batsim_config) -{ - (void) date; - (void) batsim_config; - - available_machines.insert(IntervalSet::ClosedInterval(0, _nb_machines - 1)); - PPK_ASSERT_ERROR(available_machines.size() == (unsigned int) _nb_machines); -} - -void Killer2::on_simulation_end(double date) -{ - (void) date; -} - -void Killer2::make_decisions(double date, - SortableJobOrder::UpdateInformation *update_info, - SortableJobOrder::CompareInformation *compare_info) -{ - LOG_F(1, "Date: %g. Available machines: %s", date, available_machines.to_string_brackets().c_str()); - - // Let's update available machines - for (const string & ended_job_id : _jobs_ended_recently) - { - int nb_available_before = available_machines.size(); - available_machines.insert(current_allocations[ended_job_id]); - PPK_ASSERT_ERROR(nb_available_before + (int)current_allocations[ended_job_id].size() == (int)available_machines.size()); - current_allocations.erase(ended_job_id); - } - - LOG_F(1, "Date: %g. Available machines: %s", date, available_machines.to_string_brackets().c_str()); - - // Let's handle recently released jobs - for (const string & new_job_id : _jobs_released_recently) - { - const Job * new_job = (*_workload)[new_job_id]; - - if (new_job->nb_requested_resources > _nb_machines) - _decision->add_reject_job(new_job_id, date); - else - _queue->append_job(new_job, update_info); - } - - // Queue sorting - _queue->sort_queue(update_info, compare_info); - - if (!_queue->is_empty()) - { - const Job * job = _queue->first_job(); - - // Kill the job if it is running - if (current_allocations.size() > 0) - { - PPK_ASSERT_ERROR(current_allocations.size() == 1); - string running_job = current_allocations.begin()->first; - - _decision->add_kill_job({running_job}, date); - available_machines.insert(current_allocations[running_job]); - current_allocations.erase(running_job); - date += 5; // The execution will be 5 seconds after the kill - } - - // Run the priority job - IntervalSet used_machines; - bool fitted = _selector->fit(job, available_machines, used_machines); - PPK_ASSERT_ERROR(fitted); - - _decision->add_execute_job(job->id, used_machines, date); - current_allocations[job->id] = used_machines; - - available_machines.remove(used_machines); - _queue->remove_job(job); - } - - LOG_F(1, "Date: %g. Available machines: %s", date, available_machines.to_string_brackets().c_str()); -} diff --git a/src/algo/killer2.hpp b/src/algo/killer2.hpp deleted file mode 100644 index 9242b46b64ce6472660b3b576d54328343eb079e..0000000000000000000000000000000000000000 --- a/src/algo/killer2.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "../isalgorithm.hpp" - -#include <vector> -#include <set> -#include <list> -#include <map> - -#include "../locality.hpp" -#include <intervalset.hpp> - -class Workload; -class SchedulingDecision; - -class Killer2 : public ISchedulingAlgorithm -{ -public: - Killer2(Workload * workload, SchedulingDecision * decision, Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options); - - virtual ~Killer2(); - - virtual void on_simulation_start(double date, const rapidjson::Value & batsim_config); - - virtual void on_simulation_end(double date); - - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); - -private: - IntervalSet available_machines; - std::map<std::string, IntervalSet> current_allocations; -}; diff --git a/src/algo/random.cpp b/src/algo/random.cpp deleted file mode 100644 index 2377e20fffd99dbd7072204ae5658b977f290d5b..0000000000000000000000000000000000000000 --- a/src/algo/random.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "random.hpp" - -#include "../pempek_assert.hpp" - -using namespace std; - -Random::Random(Workload * workload, - SchedulingDecision * decision, - Queue * queue, - ResourceSelector * selector, - double rjms_delay, - rapidjson::Document * variant_options) : - ISchedulingAlgorithm(workload, decision, queue, selector, rjms_delay, variant_options) -{ - -} - -Random::~Random() -{ - -} - -void Random::on_simulation_start(double date, const rapidjson::Value & batsim_config) -{ - (void) date; - - machines.insert(IntervalSet::ClosedInterval(0, _nb_machines - 1)); - PPK_ASSERT_ERROR(machines.size() == (unsigned int) _nb_machines); - - // TODO: print warning if time sharing is disabled - (void) batsim_config; -} - -void Random::on_simulation_end(double date) -{ - (void) date; -} - -void Random::make_decisions(double date, - SortableJobOrder::UpdateInformation *update_info, - SortableJobOrder::CompareInformation *compare_info) -{ - (void) update_info; - (void) compare_info; - - /* This algorithm does not care whether machines are in use, it does not even store it. - * Jobs are allocated to random machines as soon as they are submitted. */ - - PPK_ASSERT_ERROR(_jobs_killed_recently.size() == 0, - "Jobs have been killed, which should not happen with this algorithm."); - - // Let's handle recently released jobs - for (const string & new_job_id : _jobs_released_recently) - { - const Job * new_job = (*_workload)[new_job_id]; - - if (new_job->nb_requested_resources > _nb_machines) - { - // The job is too big for the machine -> reject - _decision->add_reject_job(new_job_id, date); - } - else - { - // THe job fits in the machine -> executed right now - _decision->add_execute_job(new_job->id, - machines.random_pick(new_job->nb_requested_resources), - date); - } - } -} - diff --git a/src/algo/random.hpp b/src/algo/random.hpp deleted file mode 100644 index dda59207fb6407f5ad87732286dffd7df34858bb..0000000000000000000000000000000000000000 --- a/src/algo/random.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "../isalgorithm.hpp" - -class Random : public ISchedulingAlgorithm -{ -public: - Random(Workload * workload, SchedulingDecision * decision, Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options); - - virtual ~Random(); - - virtual void on_simulation_start(double date, const rapidjson::Value & batsim_config); - - virtual void on_simulation_end(double date); - - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); - -private: - IntervalSet machines; -}; diff --git a/src/algo/sequencer_dvfs.cpp b/src/algo/sequencer_dvfs.cpp deleted file mode 100644 index d4a21c721bf7c7bc338c39c6745f471b33a32cbd..0000000000000000000000000000000000000000 --- a/src/algo/sequencer_dvfs.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "sequencer_dvfs.hpp" - -#include <loguru.hpp> - -#include "../pempek_assert.hpp" - -SequencerDVFS::SequencerDVFS(Workload *workload, - SchedulingDecision *decision, - Queue *queue, - ResourceSelector *selector, - double rjms_delay, - rapidjson::Document *variant_options) : - ISchedulingAlgorithm(workload, decision, queue, selector, rjms_delay, variant_options) -{ - PPK_ASSERT_ERROR(variant_options->HasMember("monitoring_period"), "invalid options: 'monitoring_period' is missing"); - PPK_ASSERT_ERROR((*variant_options)["monitoring_period"].IsNumber(), "invalid options: 'monitoring_period' is not a number"); - _monitoring_period = (*variant_options)["monitoring_period"].GetDouble(); - PPK_ASSERT_ERROR(_monitoring_period > 0, "invalid options: 'monitoring period' should be strictly positive but got %g", _monitoring_period); - - PPK_ASSERT_ERROR(variant_options->HasMember("pstate_compute_fast"), "invalid options: 'pstate_compute_fast' is missing"); - PPK_ASSERT_ERROR((*variant_options)["pstate_compute_fast"].IsNumber(), "invalid options: 'pstate_compute_fast' is not a number"); - _pstate_compute_fast = (*variant_options)["pstate_compute_fast"].GetInt(); - PPK_ASSERT_ERROR(_pstate_compute_fast >= 0, "invalid options: 'pstate_compute_fast period' should be positive but got %d", _pstate_compute_fast); - - PPK_ASSERT_ERROR(variant_options->HasMember("pstate_compute_slow"), "invalid options: 'pstate_compute_slow' is missing"); - PPK_ASSERT_ERROR((*variant_options)["pstate_compute_slow"].IsNumber(), "invalid options: 'pstate_compute_slow' is not a number"); - _pstate_compute_slow = (*variant_options)["pstate_compute_slow"].GetInt(); - PPK_ASSERT_ERROR(_pstate_compute_slow >= 0, "invalid options: 'pstate_compute_slow period' should be positive but got %d", _pstate_compute_slow); - - PPK_ASSERT_ERROR(_pstate_compute_fast != _pstate_compute_slow, "invalid options: 'pstate_compute_fast' and 'pstate_compute_slow' should be different but both have value %d", _pstate_compute_fast); - - LOG_SCOPE_FUNCTION(INFO); - LOG_F(INFO, "monitoring_period: %g", _monitoring_period); - LOG_F(INFO, "pstate_compute_fast: %d", _pstate_compute_fast); - LOG_F(INFO, "pstate_compute_slow: %d", _pstate_compute_slow); -} - -SequencerDVFS::~SequencerDVFS() -{ - -} - -void SequencerDVFS::on_simulation_start(double date, const rapidjson::Value &batsim_config) -{ - (void) date; - (void) batsim_config; - - _machines.insert(IntervalSet::ClosedInterval(0, _nb_machines - 1)); - PPK_ASSERT_ERROR(_machines.size() == (unsigned int) _nb_machines); - - // Request the initial future call. Future ones will be requested in the on_requested_call function. - _decision->add_call_me_later(date + _monitoring_period, date); -} - -void SequencerDVFS::on_simulation_end(double date) -{ -} - -void SequencerDVFS::on_requested_call(double date) -{ - LOG_SCOPE_FUNCTION(INFO); - LOG_F(INFO, "requested call received!"); - - // Request a future call at current date + period (unless simulation is about to finish) - if (!_no_more_static_job_to_submit_received || _is_job_running) - { - _decision->add_call_me_later(date + _monitoring_period, date); - } - - // Toggle DVFS state of allocated machines if a job is being run - if (_is_job_running) - { - int new_pstate = _currently_fast ? _pstate_compute_slow : _pstate_compute_fast; - _decision->add_set_resource_state(_allocated_machines, new_pstate, date); - _currently_fast = !_currently_fast; - } -} - -void SequencerDVFS::make_decisions(double date, - SortableJobOrder::UpdateInformation *update_info, - SortableJobOrder::CompareInformation *compare_info) -{ - // This algorithm executes all the jobs, one after the other. - // At any time, either 0 or 1 job is running on the platform. - // The order of the sequence depends on the queue order. - - // Up to one job finished since last call. - PPK_ASSERT_ERROR(_jobs_ended_recently.size() <= 1); - if (!_jobs_ended_recently.empty()) - { - PPK_ASSERT_ERROR(_is_job_running); - _is_job_running = false; - } - - // Add valid jobs into the queue - for (const std::string & job_id : _jobs_released_recently) - { - const Job * job = (*_workload)[job_id]; - - if (job->nb_requested_resources <= _nb_machines) - _queue->append_job(job, update_info); - else - _decision->add_reject_job(job->id, date); - } - - // Sort queue if needed - _queue->sort_queue(update_info, compare_info); - - // Execute the first job if the machine is empty - const Job * job = _queue->first_job_or_nullptr(); - if (job != nullptr && !_is_job_running) - { - _allocated_machines = _machines.left(job->nb_requested_resources); - _decision->add_execute_job(job->id, _allocated_machines, date); - _is_job_running = true; - _queue->remove_job(job); - } -} diff --git a/src/algo/sequencer_dvfs.hpp b/src/algo/sequencer_dvfs.hpp deleted file mode 100644 index 018f2495dcaf22e05774341faa49cb999bd9a2b6..0000000000000000000000000000000000000000 --- a/src/algo/sequencer_dvfs.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include "../isalgorithm.hpp" - -#include "../locality.hpp" -#include <intervalset.hpp> - -class Workload; -class SchedulingDecision; - -class SequencerDVFS : public ISchedulingAlgorithm -{ -public: - SequencerDVFS(Workload * workload, SchedulingDecision * decision, Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options); - - virtual ~SequencerDVFS(); - - virtual void on_simulation_start(double date, const rapidjson::Value & batsim_config); - - virtual void on_simulation_end(double date); - - virtual void on_requested_call(double date); - - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); - -private: - IntervalSet _machines; - IntervalSet _allocated_machines; - bool _is_job_running = false; - double _monitoring_period = -1; - - int _pstate_compute_fast = -1; - int _pstate_compute_slow = -1; - bool _currently_fast = true; -}; diff --git a/src/algo/sleeper.cpp b/src/algo/sleeper.cpp deleted file mode 100644 index 04bd771f8700136827c6b7f57e3705abb09c7a94..0000000000000000000000000000000000000000 --- a/src/algo/sleeper.cpp +++ /dev/null @@ -1,192 +0,0 @@ -#include "sleeper.hpp" - -#include <iostream> - -#include "../pempek_assert.hpp" - -using namespace std; - -Sleeper::Sleeper(Workload * workload, SchedulingDecision * decision, Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options) : - ISchedulingAlgorithm(workload, decision, queue, selector, rjms_delay, variant_options) -{ - PPK_ASSERT_ERROR(variant_options->HasMember("pstate_compute"), "Invalid options JSON object: Member 'pstate_compute' cannot be found"); - PPK_ASSERT_ERROR((*variant_options)["pstate_compute"].IsInt(), "Invalid options JSON object: Member 'pstate_compute' must be integral"); - int pstate_compute = (*variant_options)["pstate_compute"].GetInt(); - PPK_ASSERT_ERROR(pstate_compute >= 0, "Invalid options JSON object: Member 'pstate_compute' value must be positive (got %d)", pstate_compute); - compute_pstate = pstate_compute; - - PPK_ASSERT_ERROR(variant_options->HasMember("pstate_sleep"), "Invalid options JSON object: Member 'pstate_sleep' cannot be found"); - PPK_ASSERT_ERROR((*variant_options)["pstate_sleep"].IsInt(), "Invalid options JSON object: Member 'pstate_sleep' must be integral"); - int pstate_sleep = (*variant_options)["pstate_sleep"].GetInt(); - PPK_ASSERT_ERROR(pstate_sleep >= 0, "Invalid options JSON object: Member 'pstate_sleep' value must be positive (got %d)", pstate_sleep); - sleep_pstate = pstate_sleep; -} - -Sleeper::~Sleeper() -{ - -} - -void Sleeper::on_simulation_start(double date, const rapidjson::Value &batsim_config) -{ - (void) date; - (void) batsim_config; - - all_machines.insert(IntervalSet::ClosedInterval(0, _nb_machines - 1)); - available_machines = all_machines; -} - -void Sleeper::on_simulation_end(double date) -{ - (void) date; - - simulation_finished = true; -} - -void Sleeper::make_decisions(double date, SortableJobOrder::UpdateInformation *update_info, SortableJobOrder::CompareInformation *compare_info) -{ - // Let's handle recently ended jobs - PPK_ASSERT_ERROR(_jobs_ended_recently.size() == 0 || _jobs_ended_recently.size() == 1); - if (_jobs_ended_recently.size() > 0) - { - string ended_job_id = _jobs_ended_recently[0]; - const Job * finished_job = (*_workload)[ended_job_id]; - PPK_ASSERT_ERROR(job_being_computed == ended_job_id); - - job_being_computed = ""; - - PPK_ASSERT_ERROR(finished_job->nb_requested_resources == (int) computing_machines.size()); - available_machines.insert(computing_machines); - computing_machines.clear(); - } - - // Let's handle recently released jobs - for (const string & new_job_id : _jobs_released_recently) - { - const Job * new_job = (*_workload)[new_job_id]; - - if (new_job->nb_requested_resources > _nb_machines) - _decision->add_reject_job(new_job_id, date); - else - _queue->append_job(new_job, update_info); - } - - // Let's handle machine power state alterations - for (auto mit : _machines_whose_pstate_changed_recently) - { - const int & new_pstate = mit.first; - const IntervalSet & machines = mit.second; - - PPK_ASSERT_ERROR(new_pstate == compute_pstate || new_pstate == sleep_pstate); - - if (new_pstate == compute_pstate) - { - PPK_ASSERT_ERROR(machines == (machines & machines_being_switched_on)); - - machines_being_switched_on.remove(machines); - available_machines.insert(machines); - } - else if (new_pstate == sleep_pstate) - { - PPK_ASSERT_ERROR(machines == (machines & machines_being_switched_off)); - - machines_being_switched_off.remove(machines); - sleeping_machines.insert(machines); - } - } - - // Queue sorting - _queue->sort_queue(update_info, compare_info); - - /* This algorithm will ensure that 0 or 1 job is being computed at any given time. - * Only the first job of the queue can be computed. - * - * Unneeded resources will be put to sleep. - * Resources can be awakened in order to run a job. - */ - - if (!_queue->is_empty()) - { - // There are jobs to compute - const Job * job = _queue->first_job(); - - if (job_being_computed == "") - { - PPK_ASSERT_ERROR(computing_machines.size() == 0); - // No job is being computed now. We should then run the job! - if ((int)available_machines.size() >= job->nb_requested_resources) - { - // The job can be executed right now - IntervalSet alloc = available_machines.left(job->nb_requested_resources); - _decision->add_execute_job(job->id, alloc, date); - - // Let's update machine states - available_machines.remove(alloc); - computing_machines.insert(alloc); - - // Let's put unused machines into sleep - if (available_machines.size() > 0) - { - _decision->add_set_resource_state(available_machines, sleep_pstate, date); - machines_being_switched_off.insert(available_machines); - available_machines.clear(); - } - - job_being_computed = job->id; - _queue->remove_job(job); - } - else - { - // There are not enough available machine now to run the job - int future_nb_avail = available_machines.size() + machines_being_switched_on.size(); - - if (future_nb_avail < job->nb_requested_resources) - { - // Some machines must be ordered to wake up so the job can run. - int nb_machines_to_wake_up = job->nb_requested_resources - future_nb_avail; - int nb_machines_to_order_to_wake_up = std::min((int)sleeping_machines.size(), nb_machines_to_wake_up); - - if (nb_machines_to_order_to_wake_up > 0) - { - // Decision - IntervalSet machines_to_order_to_wake_up = sleeping_machines.left(nb_machines_to_order_to_wake_up); - _decision->add_set_resource_state(machines_to_order_to_wake_up, compute_pstate, date); - - // Machines state update - sleeping_machines.remove(machines_to_order_to_wake_up); - machines_being_switched_on.insert(machines_to_order_to_wake_up); - } - } - else - { - // Machines are already being waken up, there is nothing to do. - } - } - } - else - { - // A job is already being computed, there is nothing to do but to put as much machines as possible to sleep - - // Let's put unused machines into sleep - if (available_machines.size() > 0) - { - _decision->add_set_resource_state(available_machines, sleep_pstate, date); - machines_being_switched_off.insert(available_machines); - available_machines.clear(); - } - } - } - else if (!simulation_finished) - { - // There are no jobs to compute at the moment. - - // Let's put unused machines into sleep - if (available_machines.size() > 0) - { - _decision->add_set_resource_state(available_machines, sleep_pstate, date); - machines_being_switched_off.insert(available_machines); - available_machines.clear(); - } - } -} diff --git a/src/algo/sleeper.hpp b/src/algo/sleeper.hpp deleted file mode 100644 index 71346adc497cb56b0ff28f3c49b28ddb59579ea2..0000000000000000000000000000000000000000 --- a/src/algo/sleeper.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include <list> - -#include "../isalgorithm.hpp" -#include "../json_workload.hpp" -#include "../locality.hpp" - -class Sleeper : public ISchedulingAlgorithm -{ -public: - Sleeper(Workload * workload, SchedulingDecision * decision, Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options); - - virtual ~Sleeper(); - - virtual void on_simulation_start(double date, const rapidjson::Value & batsim_config); - - virtual void on_simulation_end(double date); - - void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); - - -private: - IntervalSet all_machines; - - IntervalSet available_machines; - IntervalSet computing_machines; - IntervalSet sleeping_machines; - IntervalSet machines_being_switched_on; - IntervalSet machines_being_switched_off; - - std::string job_being_computed = ""; - - int compute_pstate; - int sleep_pstate; - bool simulation_finished = false; -}; diff --git a/src/algo/submitter.cpp b/src/algo/submitter.cpp deleted file mode 100644 index 3d5735d656d73418593a615c5c20d2f24a9b2fe9..0000000000000000000000000000000000000000 --- a/src/algo/submitter.cpp +++ /dev/null @@ -1,230 +0,0 @@ -#include "submitter.hpp" - -#include <loguru.hpp> - -#include "../pempek_assert.hpp" - -using namespace std; - -Submitter::Submitter(Workload *workload, SchedulingDecision *decision, Queue *queue, - ResourceSelector *selector, double rjms_delay, - rapidjson::Document *variant_options) : - ISchedulingAlgorithm(workload, decision, queue, selector, rjms_delay, variant_options) -{ - - if (variant_options->HasMember("nb_jobs_to_submit")) - { - PPK_ASSERT_ERROR((*variant_options)["nb_jobs_to_submit"].IsInt(), - "Bad algo options: nb_jobs_to_submit is not integral"); - nb_jobs_to_submit = (*variant_options)["nb_jobs_to_submit"].GetInt(); - PPK_ASSERT_ERROR(nb_jobs_to_submit >= 0, - "Bad algo options: nb_jobs_to_submit is negative (%d)", nb_jobs_to_submit); - } - - if (variant_options->HasMember("increase_jobs_duration")) - { - PPK_ASSERT_ERROR((*variant_options)["increase_jobs_duration"].IsBool(), - "Bad algo options: increase_jobs_duration is not a boolean"); - increase_jobs_duration = (*variant_options)["increase_jobs_duration"].GetBool(); - } - - if (variant_options->HasMember("send_profile_if_already_sent")) - { - PPK_ASSERT_ERROR((*variant_options)["send_profile_if_already_sent"].IsBool(), - "Bad algo options: send_profile_if_already_sent is not a boolean"); - send_profile_if_already_sent = (*variant_options)["send_profile_if_already_sent"].GetBool(); - } - - if (variant_options->HasMember("send_profiles_in_separate_event")) - { - PPK_ASSERT_ERROR((*variant_options)["send_profiles_in_separate_event"].IsBool(), - "Bad algo options: send_profiles_in_separate_event is not a boolean"); - send_profiles_in_separate_event = (*variant_options)["send_profiles_in_separate_event"].GetBool(); - } - - if (variant_options->HasMember("set_job_metadata")) - { - PPK_ASSERT_ERROR((*variant_options)["set_job_metadata"].IsBool(), - "Invalid options: 'set_job_metadata' should be a boolean"); - set_job_metadata = (*variant_options)["set_job_metadata"].GetBool(); - } - - LOG_SCOPE_FUNCTION(INFO); - LOG_F(INFO, "nb_jobs_to_submit: %d", nb_jobs_to_submit); - LOG_F(INFO, "increase_jobs_duration: %d", increase_jobs_duration); - LOG_F(INFO, "send_profile_if_already_sent: %d", send_profile_if_already_sent); - LOG_F(INFO, "send_profiles_in_separate_event: %d", send_profiles_in_separate_event); - LOG_F(INFO, "set_job_metadata: %d", set_job_metadata); -} - -Submitter::~Submitter() -{ - -} - -void Submitter::on_simulation_start(double date, const rapidjson::Value & batsim_config) -{ - (void) date; - - available_machines.insert(IntervalSet::ClosedInterval(0, _nb_machines - 1)); - PPK_ASSERT_ERROR(available_machines.size() == (unsigned int) _nb_machines); - - PPK_ASSERT_ERROR(batsim_config["dynamic-jobs-enabled"].GetBool(), - "This algorithm only works if dynamic job are enabled!"); - dyn_submit_ack = batsim_config["dynamic-jobs-acknowledged"].GetBool(); -} - -void Submitter::on_simulation_end(double date) -{ - (void) date; -} - -void Submitter::make_decisions(double date, - SortableJobOrder::UpdateInformation *update_info, - SortableJobOrder::CompareInformation *compare_info) -{ - // Let's update available machines - if (_jobs_ended_recently.size() > 0) - { - for (const string & ended_job_id : _jobs_ended_recently) - { - int nb_available_before = available_machines.size(); - available_machines.insert(current_allocations[ended_job_id]); - PPK_ASSERT_ERROR(nb_available_before + (*_workload)[ended_job_id]->nb_requested_resources == (int)available_machines.size()); - current_allocations.erase(ended_job_id); - } - } - - // Let's handle recently released jobs - for (const string & new_job_id : _jobs_released_recently) - { - const Job * new_job = (*_workload)[new_job_id]; - - if (set_job_metadata) - { - _decision->add_set_job_metadata(new_job_id, - "just some metadata for job " + new_job_id, - date); - } - - if (new_job->nb_requested_resources > _nb_machines) - _decision->add_reject_job(new_job_id, date); - else - _queue->append_job(new_job, update_info); - } - - // Queue sorting - _queue->sort_queue(update_info, compare_info); - - // Trying to execute the priority job if possible - if (!_queue->is_empty()) - { - const Job * job = _queue->first_job(); - - if (job->nb_requested_resources <= (int)available_machines.size()) - { - IntervalSet used_machines; - if (_selector->fit(job, available_machines, used_machines)) - { - _decision->add_execute_job(job->id, used_machines, date); - current_allocations[job->id] = used_machines; - - available_machines.remove(used_machines); - _queue->remove_job(job); - } - } - } - - // If the queue is empty, dynamic jobs are submitted if possible - if (_queue->is_empty() && available_machines.size() >= 1) - { - if (nb_submitted_jobs < nb_jobs_to_submit) - { - double new_job_duration = 1; - if (increase_jobs_duration) - new_job_duration += nb_submitted_jobs*10; - - submit_delay_job(new_job_duration, date); - - // If dynamic submissions acknowledgements are disabled, the job is directly executed - if (!dyn_submit_ack) - { - // The execution is done 10 seconds after submitting the job - date = date + 10; - - IntervalSet used_machines = available_machines.left(1); - - string job_id = "dynamic!" + to_string(nb_submitted_jobs); - _decision->add_execute_job(job_id, used_machines, date); - current_allocations[job_id] = used_machines; - - available_machines.remove(used_machines); - } - - ++nb_submitted_jobs; - } - } - - // Sending the "end of dynamic submissions" to Batsim if needed - if ((nb_submitted_jobs >= nb_jobs_to_submit) && !finished_submitting_sent) - { - _decision->add_scheduler_finished_submitting_jobs(date); - finished_submitting_sent = true; - } -} - -void Submitter::submit_delay_job(double delay, double date) -{ - string workload_name = "dynamic"; - - double submit_time = date; - double walltime = delay + 5; - int res = 1; - string profile = "delay_" + std::to_string(delay); - - int buf_size = 128; - - string job_id = to_string(nb_submitted_jobs); - string unique_job_id = workload_name + "!" + job_id; - - char * buf_job = new char[buf_size]; - int nb_chars = snprintf(buf_job, buf_size, - R"foo({"id":"%s", "subtime":%g, "walltime":%g, "res":%d, "profile":"%s"})foo", - job_id.c_str(), submit_time, walltime, res, profile.c_str()); - PPK_ASSERT_ERROR(nb_chars < buf_size - 1); - - char * buf_profile = new char[buf_size]; - nb_chars = snprintf(buf_profile, buf_size, - R"foo({"type": "delay", "delay": %g})foo", delay); - PPK_ASSERT_ERROR(nb_chars < buf_size - 1); - - bool already_sent_profile = profiles_already_sent.count(profile) == 1; - - bool send_profile = !already_sent_profile || send_profile_if_already_sent; - - if (send_profile && send_profiles_in_separate_event) - _decision->add_submit_profile(workload_name, profile, buf_profile, date); - - _decision->add_submit_job(workload_name, job_id, profile, - buf_job, buf_profile, date, - send_profile && !send_profiles_in_separate_event); - - if (set_job_metadata) - { - string job_id_str = workload_name + "!" + job_id; - _decision->add_set_job_metadata(job_id_str, - "just some metadata for job " + job_id_str, - date); - } - - profiles_already_sent.insert(profile); - - // If dynamic submisions ack is disabled, we must add the job in the workload now - if (!dyn_submit_ack) - { - _workload->add_job_from_json_description_string(buf_job, unique_job_id, date); - } - - delete[] buf_job; - delete[] buf_profile; -} diff --git a/src/algo/submitter.hpp b/src/algo/submitter.hpp deleted file mode 100644 index 89aeef02d87dd70aad0026f03bda81b4f7f88094..0000000000000000000000000000000000000000 --- a/src/algo/submitter.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "../isalgorithm.hpp" - -#include <vector> -#include <set> -#include <list> -#include <map> - -#include "../locality.hpp" -#include <intervalset.hpp> - -class Workload; -class SchedulingDecision; - -class Submitter : public ISchedulingAlgorithm -{ -public: - Submitter(Workload * workload, SchedulingDecision * decision, Queue * queue, - ResourceSelector * selector, double rjms_delay, - rapidjson::Document * variant_options); - - virtual ~Submitter(); - - virtual void on_simulation_start(double date, const rapidjson::Value & batsim_config); - - virtual void on_simulation_end(double date); - - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); - -protected: - void submit_delay_job(double delay, double date); - -private: - IntervalSet available_machines; - std::map<std::string, IntervalSet> current_allocations; - int nb_submitted_jobs = 0; //!< The number of jobs submitted from this algorithm - int nb_jobs_to_submit = 10; //!< The number of jobs to submit - bool increase_jobs_duration = true; //!< Whether the duration of the submitted jobs increases or not. If false, the same profile will be used by all the submitted jobs. - bool send_profile_if_already_sent = false; //!< Whether already transmitted profiles should be sent again to Batsim or not. - bool send_profiles_in_separate_event = true; //!< Whether profiles should be sent in a separate message or not - bool set_job_metadata = false; //! If set to true, metadata will be associated to jobs when they are submitted. - - bool dyn_submit_ack; - bool finished_submitting_sent = false; - - std::set<std::string> profiles_already_sent; -}; diff --git a/src/algo/wt_estimator.cpp b/src/algo/wt_estimator.cpp deleted file mode 100644 index 615d6beaac18ae1222821973df11d479cca98b51..0000000000000000000000000000000000000000 --- a/src/algo/wt_estimator.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "wt_estimator.hpp" - -WaitingTimeEstimator::WaitingTimeEstimator(Workload *workload, - SchedulingDecision *decision, - Queue *queue, - ResourceSelector *selector, - double rjms_delay, - rapidjson::Document *variant_options) : - ISchedulingAlgorithm(workload, decision, queue, selector, rjms_delay, variant_options) -{ - -} - -WaitingTimeEstimator::~WaitingTimeEstimator() -{ - -} - -void WaitingTimeEstimator::on_simulation_start(double date, const rapidjson::Value &batsim_config) -{ - (void) date; - (void) batsim_config; -} - -void WaitingTimeEstimator::on_simulation_end(double date) -{ - (void) date; -} - -void WaitingTimeEstimator::make_decisions(double date, - SortableJobOrder::UpdateInformation *update_info, - SortableJobOrder::CompareInformation *compare_info) -{ - (void) update_info; - (void) compare_info; - - /* Another stupid algorithm. - * This one rejects all jobs. - * It also estimates a negative waiting time for all jobs, as they will never be launched. - */ - - for (const std::string & job_id : _jobs_whose_waiting_time_estimation_has_been_requested_recently) - _decision->add_answer_estimate_waiting_time(job_id, -1, date); - - for (const std::string & job_id : _jobs_released_recently) - _decision->add_reject_job(job_id, date); -} diff --git a/src/algo/wt_estimator.hpp b/src/algo/wt_estimator.hpp deleted file mode 100644 index b10bd7bc21a12eae09f96b3f3eb8009c1351d945..0000000000000000000000000000000000000000 --- a/src/algo/wt_estimator.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "../isalgorithm.hpp" - -#include "../locality.hpp" -#include <intervalset.hpp> - -class Workload; -class SchedulingDecision; - -class WaitingTimeEstimator : public ISchedulingAlgorithm -{ -public: - WaitingTimeEstimator(Workload * workload, SchedulingDecision * decision, Queue * queue, ResourceSelector * selector, - double rjms_delay, rapidjson::Document * variant_options); - - virtual ~WaitingTimeEstimator(); - - void on_simulation_start(double date, const rapidjson::Value & batsim_config); - - void on_simulation_end(double date); - - virtual void make_decisions(double date, - SortableJobOrder::UpdateInformation * update_info, - SortableJobOrder::CompareInformation * compare_info); -}; diff --git a/src/main.cpp b/src/main.cpp index 56b245fc710e419831778b3c785b870bc589924c..76b20efe7c87c9e5547e8121ea12419fbae1af4d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,30 +19,10 @@ #include "json_workload.hpp" #include "pempek_assert.hpp" -#include "algo/conservative_bf.hpp" -#include "algo/crasher.hpp" #include "algo/easy_bf.hpp" -#include "algo/easy_bf_fast.hpp" -#include "algo/easy_bf_plot_liquid_load_horizon.hpp" -#include "algo/energy_bf.hpp" -#include "algo/energy_bf_dicho.hpp" -#include "algo/energy_bf_idle_sleeper.hpp" -#include "algo/energy_bf_monitoring_period.hpp" -#include "algo/energy_bf_monitoring_inertial_shutdown.hpp" -#include "algo/energy_bf_machine_subpart_sleeper.hpp" -#include "algo/energy_watcher.hpp" -#include "algo/filler.hpp" #include "algo/fcfs.hpp" -#include "algo/fcfs_fast.hpp" -#include "algo/killer.hpp" -#include "algo/killer2.hpp" -#include "algo/random.hpp" #include "algo/rejecter.hpp" -#include "algo/sleeper.hpp" #include "algo/sequencer.hpp" -#include "algo/sequencer_dvfs.hpp" -#include "algo/submitter.hpp" -#include "algo/wt_estimator.hpp" using namespace std; using namespace boost; @@ -74,14 +54,7 @@ void run(Network & n, ISchedulingAlgorithm * algo, SchedulingDecision &d, int main(int argc, char ** argv) { - const set<string> variants_set = {"conservative_bf", "crasher", "easy_bf", "easy_bf_fast", - "easy_bf_plot_liquid_load_horizon", - "energy_bf", "energy_bf_dicho", "energy_bf_idle_sleeper", - "energy_bf_monitoring", - "energy_bf_monitoring_inertial", "energy_bf_subpart_sleeper", - "energy_watcher", "fcfs", "fcfs_fast", - "filler", "killer", "killer2", "random", "rejecter", - "sequencer", "sequencer_dvfs", "sleeper", "submitter", "waiting_time_estimator"}; + const set<string> variants_set = {"easy_bf", "fcfs", "rejecter", "sequencer" }; const set<string> policies_set = {"basic", "contiguous"}; const set<string> queue_orders_set = {"fcfs", "lcfs", "desc_bounded_slowdown", "desc_slowdown", "asc_size", "desc_size", "asc_walltime", "desc_walltime"}; @@ -104,7 +77,7 @@ int main(int argc, char ** argv) args::ValueFlag<double> flag_rjms_delay(parser, "delay", "Sets the expected time that the RJMS takes to do some things like killing a job", {'d', "rjms_delay"}, 5.0); args::ValueFlag<string> flag_selection_policy(parser, "policy", "Sets the resource selection policy. Available values are " + policies_string, {'p', "policy"}, "basic"); args::ValueFlag<string> flag_socket_endpoint(parser, "endpoint", "Sets the socket endpoint.", {'s', "socket-endpoint"}, "tcp://*:28000"); - args::ValueFlag<string> flag_scheduling_variant(parser, "variant", "Sets the scheduling variant. Available values are " + variants_string, {'v', "variant"}, "filler"); + args::ValueFlag<string> flag_scheduling_variant(parser, "variant", "Sets the scheduling variant. Available values are " + variants_string, {'v', "variant"}, "rejecter"); args::ValueFlag<string> flag_variant_options(parser, "options", "Sets the scheduling variant options. Must be formatted as a JSON object.", {"variant_options"}, "{}"); args::ValueFlag<string> flag_variant_options_filepath(parser, "options-filepath", "Sets the scheduling variant options as the content of the given filepath. Overrides the variant_options options.", {"variant_options_filepath"}, ""); args::ValueFlag<string> flag_queue_order(parser, "order", "Sets the queue order. Available values are " + queue_orders_string, {'o', "queue_order"}, "fcfs"); @@ -259,54 +232,14 @@ int main(int argc, char ** argv) LOG_F(1, "variant_options = '%s'", variant_options.c_str()); // Scheduling variant - if (scheduling_variant == "filler") - algo = new Filler(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "conservative_bf") - algo = new ConservativeBackfilling(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "crasher") - algo = new Crasher(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "easy_bf") + if (scheduling_variant == "easy_bf") algo = new EasyBackfilling(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "easy_bf_fast") - algo = new EasyBackfillingFast(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "easy_bf_plot_liquid_load_horizon") - algo = new EasyBackfillingPlotLiquidLoadHorizon(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "energy_bf") - algo = new EnergyBackfilling(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "energy_bf_dicho") - algo = new EnergyBackfillingDichotomy(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "energy_bf_idle_sleeper") - algo = new EnergyBackfillingIdleSleeper(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "energy_bf_monitoring") - algo = new EnergyBackfillingMonitoringPeriod(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "energy_bf_monitoring_inertial") - algo = new EnergyBackfillingMonitoringInertialShutdown(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "energy_bf_subpart_sleeper") - algo = new EnergyBackfillingMachineSubpartSleeper(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "energy_watcher") - algo = new EnergyWatcher(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); else if (scheduling_variant == "fcfs") algo = new FCFS(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "fcfs_fast") - algo = new FCFSFast(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "killer") - algo = new Killer(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "killer2") - algo = new Killer2(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "random") - algo = new Random(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); else if (scheduling_variant == "rejecter") algo = new Rejecter(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); else if (scheduling_variant == "sequencer") algo = new Sequencer(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "sequencer_dvfs") - algo = new SequencerDVFS(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "sleeper") - algo = new Sleeper(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "submitter") - algo = new Submitter(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); - else if (scheduling_variant == "waiting_time_estimator") - algo = new WaitingTimeEstimator(&w, &decision, queue, selector, rjms_delay, &json_doc_variant_options); // Network Network n;