diff --git a/src/algo/carbone.cpp b/src/algo/carbone.cpp new file mode 100644 index 0000000000000000000000000000000000000000..31d6fd46f541880255d6015a2507c222c2481a31 --- /dev/null +++ b/src/algo/carbone.cpp @@ -0,0 +1,130 @@ +#include "carbone.hpp" +#include <loguru.hpp> + +#include "../pempek_assert.hpp" + +using namespace std; + +CarboneAlgorithm::CarboneAlgorithm(Workload * workload, + SchedulingDecision * decision, + Queue * queue, + ResourceSelector * selector, + double rjms_delay, + rapidjson::Document * variant_options) : + ISchedulingAlgorithm(workload, decision, queue, selector, rjms_delay, variant_options) { + // Initialisation spécifique à CarboneAlgorithm + carbonFootprintDcA = 0; + carbonFootprintDcB = 0; + carbonFootprintDcC = 0; +} + + +void CarboneAlgorithm::updateCarbonFootprint(int dcA, int dcB, int dcC) { + carbonFootprintDcA = dcA; + carbonFootprintDcB = dcB; + carbonFootprintDcC = dcC; +} + +void CarboneAlgorithm::on_simulation_start(double date, const rapidjson::Value & batsim_config) { + _schedule = Schedule(_nb_machines, date); + + (void) batsim_config; +} + +void CarboneAlgorithm::on_simulation_end(double date) { + + (void) date; +} + +void CarboneAlgorithm::make_decisions(double date, SortableJobOrder::UpdateInformation *update_info, SortableJobOrder::CompareInformation *compare_info) { + const Job * priority_job_before = _queue->first_job_or_nullptr(); // Identifier la première tâche prioritaire avant de commencer les décisions + + for (const string & ended_job_id : _jobs_ended_recently) { + _schedule.remove_job((*_workload)[ended_job_id]); // Supprimer les tâches terminées de l'ordonnancement + } + + std::vector<std::string> recently_queued_jobs; // Préparer une liste pour conserver les IDs des tâches récemment ajoutées à la file d'attente + for (const string & new_job_id : _jobs_released_recently) { // Parcourir les tâches récemment libérées et décider de les rejeter ou de les ajouter à la file d'attente + const Job * new_job = (*_workload)[new_job_id]; + + if (new_job->nb_requested_resources > _nb_machines) { + _decision->add_reject_job(new_job_id, date); // Rejeter la tâche si elle demande plus de ressources que disponibles + } else if (!new_job->has_walltime) { + _decision->add_reject_job(new_job_id, date); // Rejeter la tâche si elle n'a pas de walltime défini + } else { + _queue->append_job(new_job, update_info); // Ajouter la tâche éligible à la file d'attente + recently_queued_jobs.push_back(new_job_id); + } + } + + _schedule.update_first_slice(date); // Mettre à jour l'état actuel de l'ordonnancement + + const Job * priority_job_after = nullptr; // Identifier la première tâche prioritaire après le tri et la mise à jour de la file + sort_queue_while_handling_priority_job(priority_job_before, priority_job_after, update_info, compare_info); + + int nb_available_machines = _schedule.begin()->available_machines.size(); // Calculer le nombre de machines disponibles actuellement + + for (const string & new_job_id : recently_queued_jobs) { //exécuter les tâches récemment ajoutées à la file, en tenant compte de la disponibilité des ressources + const Job * new_job = (*_workload)[new_job_id]; + if (_queue->contains_job(new_job) && new_job != priority_job_after && new_job->nb_requested_resources <= nb_available_machines) { + string optimalDC = select_datacenter_with_lowest_carbon_footprint(); // Sélection du data center avec l'empreinte carbone la plus faible + Schedule::JobAlloc alloc = _schedule.add_job_first_fit(new_job, _selector, optimalDC); // Allouer la tâche dans le data center sélectionné + if (alloc.started_in_first_slice) { + _decision->add_execute_job(new_job_id, alloc.used_machines, date, optimalDC); // Exécuter la tâche si elle peut démarrer immédiatement + _queue->remove_job(new_job); + nb_available_machines -= new_job->nb_requested_resources; + } else { + _schedule.remove_job(new_job); // Retirer la tâche de l'ordonnancement si elle ne peut pas démarrer immédiatement + } + } + } + // Parcourir la file d'attente pour essayer d'exécuter les tâches restantes, en tenant compte de la priorité et de la disponibilité des ressources + auto job_it = _queue->begin(); + while (job_it != _queue->end() && nb_available_machines > 0) { + const Job * job = (*job_it)->job; + if (_schedule.contains_job(job)) _schedule.remove_job(job); + if (job == priority_job_after) { + string optimalDC = select_datacenter_with_lowest_carbon_footprint(); + Schedule::JobAlloc alloc = _schedule.add_job_first_fit(job, _selector,optimalDC); + if (alloc.started_in_first_slice) { + _decision->add_execute_job(job->id, alloc.used_machines, date,optimalDC); + job_it = _queue->remove_job(job_it); + priority_job_after = _queue->first_job_or_nullptr(); + } else { + ++job_it; + } + } else { + Schedule::JobAlloc alloc = _schedule.add_job_first_fit(job, _selector); + 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; + } + } + } +} + + +std::string CarboneAlgorithm::select_datacenter_with_lowest_carbon_footprint() { + int lowestCarbonFootprint = std::min({carbonFootprintDcA, carbonFootprintDcB, carbonFootprintDcC}); + + string lowestDcID = "dcA"; + + if (carbonFootprintDcB < lowestCarbonFootprint) { + lowestCarbonFootprint = carbonFootprintDcB; + lowestDcID = "dcB"; + } + + if (carbonFootprintDcC < lowestCarbonFootprint) { + lowestCarbonFootprint = carbonFootprintDcC; + lowestDcID = "dcC"; + } + + std::string result = "La valeur de l’empreinte carbone la plus optimale est : " + std::to_string(lowestCarbonFootprint) + " de datacenter " + lowestDcName; + return result; + +} + + +