diff --git a/CMakeLists.txt b/CMakeLists.txt index 424f66df50..cb0649010a 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -312,6 +312,8 @@ set(SOURCE_FILES include/wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf/BatchJobSet.h src/wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf/ConservativeBackfillingBatchScheduler.cpp include/wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf/ConservativeBackfillingBatchScheduler.h + src/wrench/services/compute/batch/batch_schedulers/homegrown/easy_bf/EasyBackfillingBatchScheduler.cpp + include/wrench/services/compute/batch/batch_schedulers/homegrown/easy_bf/EasyBackfillingBatchScheduler.h src/wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf/NodeAvailabilityTimeLine.cpp include/wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf/NodeAvailabilityTimeLine.h include/wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf_core_level/BatchJobSetCoreLevel.h @@ -488,6 +490,7 @@ set(TEST_FILES test/services/compute_services/batch_standard_and_pilot_jobs/HomeGrownTimeLineTest.cpp test/services/compute_services/batch_standard_and_pilot_jobs/BatchServiceTest.cpp test/services/compute_services/batch_standard_and_pilot_jobs/BatchServiceFCFSTest.cpp + test/services/compute_services/batch_standard_and_pilot_jobs/BatchServiceEASYBFTest.cpp test/services/compute_services/batch_standard_and_pilot_jobs/BatchServiceCONSERVATIVEBFTest.cpp test/services/compute_services/batch_standard_and_pilot_jobs/BatchServiceTraceFileTest.cpp test/services/compute_services/batch_standard_and_pilot_jobs/BatchServiceOutputCSVFileTest.cpp diff --git a/include/wrench/services/compute/batch/BatchComputeService.h b/include/wrench/services/compute/batch/BatchComputeService.h index f4de453056..74585fb700 100755 --- a/include/wrench/services/compute/batch/BatchComputeService.h +++ b/include/wrench/services/compute/batch/BatchComputeService.h @@ -143,6 +143,7 @@ namespace wrench { friend class WorkloadTraceFileReplayer; friend class HomegrownBatchScheduler; friend class FCFSBatchScheduler; + friend class EasyBackfillingBatchScheduler; friend class ConservativeBackfillingBatchScheduler; friend class ConservativeBackfillingBatchSchedulerCoreLevel; @@ -216,7 +217,7 @@ namespace wrench { std::set queue_ordering_options = {"fcfs", "lcfs", "desc_bounded_slowdown", "desc_slowdown", "asc_size", "desc_size", "asc_walltime", "desc_walltime"}; #else - std::set scheduling_algorithms = {"fcfs", "conservative_bf", "conservative_bf_core_level"}; + std::set scheduling_algorithms = {"fcfs", "conservative_bf", "conservative_bf_core_level", "easy_bf_depth0", "easy_bf_depth1"}; //Batch queue ordering options std::set queue_ordering_options = {}; diff --git a/include/wrench/services/compute/batch/BatchComputeServiceProperty.h b/include/wrench/services/compute/batch/BatchComputeServiceProperty.h index 70ff8511ef..ebb6eee5b9 100755 --- a/include/wrench/services/compute/batch/BatchComputeServiceProperty.h +++ b/include/wrench/services/compute/batch/BatchComputeServiceProperty.h @@ -30,6 +30,8 @@ namespace wrench { * @brief The batch scheduling algorithm. Can be: * - If ENABLE_BATSCHED is set to off / not set: * - "fcfs": First Come First Serve, which allocates resources at the core level (i.e., two jobs may run on the same node if that node has enough cores to support both jobs) (default) + * - "easy_bf_depth0": a home-grown implementation of EASY (FCFS with backfilling), which only allocates resources at the node level (i.e., two jobs can never run on the same node even if that node has enough cores to support both jobs), and which may postpone the first (oldest) job in the queue via backfilling actions + * - "easy_bf_depth1": a home-grown implementation of EASY (FCFS with backfilling), which only allocates resources at the node level (i.e., two jobs can never run on the same node even if that node has enough cores to support both jobs), and which will never postpone the first (oldest) job in the queue via backfilling actions * - "conservative_bf": a home-grown implementation of FCFS with conservative backfilling, which only allocates resources at the node level (i.e., two jobs can never run on the same node even if that node has enough cores to support both jobs) * - "conservative_bf_core_level": a home-grown implementation of FCFS with conservative backfilling, which allocates resources at the core level (i.e., two jobs may run on the same node if that node has enough cores to support both jobs) * @@ -111,7 +113,7 @@ namespace wrench { DECLARE_PROPERTY_NAME(SUBMIT_TIME_OF_FIRST_JOB_IN_WORKLOAD_TRACE_FILE); /** - * @brief Path to a to-be-generated Batsim-style CSV trace file (e.g. for b3atch schedule visualization purposes). + * @brief Path to a to-be-generated Batsim-style CSV trace file (e.g. for batch schedule visualization purposes). * - If ENABLE_BATSCHED is set to off or not set: ignored * - If ENABLE_BATSCHED is set to on: The trace file is generated in CSV format as follows: * allocated_processors,consumed_energy,execution_time,finish_time,job_id,metadata, diff --git a/include/wrench/services/compute/batch/BatchJob.h b/include/wrench/services/compute/batch/BatchJob.h index 743c055034..fbf4c822ec 100755 --- a/include/wrench/services/compute/batch/BatchJob.h +++ b/include/wrench/services/compute/batch/BatchJob.h @@ -53,9 +53,12 @@ namespace wrench { } private: + friend class EasyBackfillingBatchScheduler; friend class ConservativeBackfillingBatchScheduler; friend class ConservativeBackfillingBatchSchedulerCoreLevel; + u_int32_t easy_bf_start_date; // Field used by EASY_BF + u_int32_t easy_bf_expected_end_date; // Field used by EASY_BF u_int32_t conservative_bf_start_date; // Field used by CONSERVATIVE_BF u_int32_t conservative_bf_expected_end_date;// Field used by CONSERVATIVE_BF diff --git a/include/wrench/services/compute/batch/batch_schedulers/homegrown/easy_bf/EasyBackfillingBatchScheduler.h b/include/wrench/services/compute/batch/batch_schedulers/homegrown/easy_bf/EasyBackfillingBatchScheduler.h new file mode 100755 index 0000000000..83b057c454 --- /dev/null +++ b/include/wrench/services/compute/batch/batch_schedulers/homegrown/easy_bf/EasyBackfillingBatchScheduler.h @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2017-2019. The WRENCH Team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#ifndef WRENCH_EASYBACKFILLINGBATCHSCHEDULER_H +#define WRENCH_EASYBACKFILLINGBATCHSCHEDULER_H + +#include "wrench/services/compute/batch/BatchComputeService.h" +#include "wrench/services/compute/batch/batch_schedulers/homegrown/HomegrownBatchScheduler.h" +#include + +namespace wrench { + + /***********************/ + /** \cond INTERNAL */ + /***********************/ + + /** + * @brief A class that defines a easy backfilling batch scheduler + */ + class EasyBackfillingBatchScheduler : public HomegrownBatchScheduler { + + public: + explicit EasyBackfillingBatchScheduler(BatchComputeService *cs, int depth); + + void processQueuedJobs() override; + + void processJobSubmission(std::shared_ptr batch_job) override; + void processJobFailure(std::shared_ptr batch_job) override; + void processJobCompletion(std::shared_ptr batch_job) override; + void processJobTermination(std::shared_ptr batch_job) override; + + void compactSchedule(); + + std::map> scheduleOnHosts(unsigned long, unsigned long, sg_size_t) override; + + std::map + getStartTimeEstimates(std::set> set_of_jobs) override; + + private: + std::unique_ptr schedule; + int _depth; + }; + + + /***********************/ + /** \endcond */ + /***********************/ +}// namespace wrench + + +#endif//WRENCH_EASYBACKFILLINGBATCHSCHEDULER_H diff --git a/src/wrench/services/compute/batch/BatchComputeService.cpp b/src/wrench/services/compute/batch/BatchComputeService.cpp index dc508ddb8d..52a4809f66 100644 --- a/src/wrench/services/compute/batch/BatchComputeService.cpp +++ b/src/wrench/services/compute/batch/BatchComputeService.cpp @@ -27,6 +27,7 @@ #include "wrench/services/compute/batch/batch_schedulers/homegrown/fcfs/FCFSBatchScheduler.h" #include "wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf/ConservativeBackfillingBatchScheduler.h" #include "wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf_core_level/ConservativeBackfillingBatchSchedulerCoreLevel.h" +#include "wrench/services/compute/batch/batch_schedulers/homegrown/easy_bf/EasyBackfillingBatchScheduler.h" #include "wrench/services/compute/batch/batch_schedulers/batsched/BatschedBatchScheduler.h" #include #include @@ -201,6 +202,10 @@ namespace wrench { this->scheduler = std::unique_ptr(new FCFSBatchScheduler(this)); } else if (batch_scheduling_alg == "conservative_bf") { this->scheduler = std::unique_ptr(new ConservativeBackfillingBatchScheduler(this)); + } else if (batch_scheduling_alg == "easy_bf_depth0") { + this->scheduler = std::unique_ptr(new EasyBackfillingBatchScheduler(this, 0)); + } else if (batch_scheduling_alg == "easy_bf_depth1") { + this->scheduler = std::unique_ptr(new EasyBackfillingBatchScheduler(this, 1)); } else if (batch_scheduling_alg == "conservative_bf_core_level") { this->scheduler = std::unique_ptr(new ConservativeBackfillingBatchSchedulerCoreLevel(this)); } diff --git a/src/wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf/ConservativeBackfillingBatchScheduler.cpp b/src/wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf/ConservativeBackfillingBatchScheduler.cpp index 712fe8c1dc..6340edb6d8 100644 --- a/src/wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf/ConservativeBackfillingBatchScheduler.cpp +++ b/src/wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf/ConservativeBackfillingBatchScheduler.cpp @@ -67,7 +67,6 @@ namespace wrench { this->schedule->setTimeOrigin((u_int32_t) Simulation::getCurrentSimulatedDate()); // Start all non-started the jobs in the next slot! - std::set> next_jobs = this->schedule->getJobsInFirstSlot(); if (next_jobs.empty()) { this->compactSchedule(); diff --git a/src/wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf/NodeAvailabilityTimeLine.cpp b/src/wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf/NodeAvailabilityTimeLine.cpp index f6b157b276..db9eda4437 100755 --- a/src/wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf/NodeAvailabilityTimeLine.cpp +++ b/src/wrench/services/compute/batch/batch_schedulers/homegrown/conservative_bf/NodeAvailabilityTimeLine.cpp @@ -78,7 +78,7 @@ namespace wrench { for (auto &availability_timeslot: this->availability_timeslots) { std::cerr << availability_timeslot.first << "(" << availability_timeslot.second.num_nodes_utilized << ") | "; for (auto const &j: availability_timeslot.second.jobs) { - std::cerr << j->getJobID() << "(" << j->getRequestedNumNodes() << ") "; + std::cerr << j->getCompoundJob()->getName() << "(" << j->getRequestedNumNodes() << ") "; } std::cerr << "\n"; } diff --git a/src/wrench/services/compute/batch/batch_schedulers/homegrown/easy_bf/EasyBackfillingBatchScheduler.cpp b/src/wrench/services/compute/batch/batch_schedulers/homegrown/easy_bf/EasyBackfillingBatchScheduler.cpp new file mode 100644 index 0000000000..827c613b9f --- /dev/null +++ b/src/wrench/services/compute/batch/batch_schedulers/homegrown/easy_bf/EasyBackfillingBatchScheduler.cpp @@ -0,0 +1,312 @@ +/** + * Copyright (c) 2017-2021. The WRENCH Team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include + +#include "wrench/services/compute/batch/batch_schedulers/homegrown/easy_bf/EasyBackfillingBatchScheduler.h" + +//#define PRINT_SCHEDULE 1 + +WRENCH_LOG_CATEGORY(wrench_core_easy_bf_batch_scheduler, "Log category for EasyBackfillingBatchScheduler"); + +namespace wrench { + + /** + * @brief Constructor + * @param cs: The BatchComputeService for which this scheduler is working + */ + EasyBackfillingBatchScheduler::EasyBackfillingBatchScheduler(BatchComputeService *cs, int depth) : HomegrownBatchScheduler(cs) { + this->schedule = std::make_unique(cs->total_num_of_nodes); + this->_depth = depth; + } + + /** + * @brief Method to process a job submission + * @param batch_job: the newly submitted BatchComputeService job + */ + void EasyBackfillingBatchScheduler::processJobSubmission(std::shared_ptr batch_job) { + WRENCH_INFO("Scheduling a new BatchComputeService job, %lu, that needs %lu nodes", + batch_job->getJobID(), batch_job->getRequestedNumNodes()); + +// std::cerr << "NEW JOB: " << batch_job->getCompoundJob()->getName() << " (DOING NOTHING)\n"; + } + + /** + * @brief Method to schedule (possibly) the next jobs to be scheduled + */ + void EasyBackfillingBatchScheduler::processQueuedJobs() { + if (this->cs->batch_queue.empty()) { + return; + } + + // Update the time origin + double now = Simulation::getCurrentSimulatedDate(); +// std::cerr << "* [" << now << "] IN PROCESSING QUEUE JOB (" << this->cs->batch_queue.size() << " JOBS IN THE QUEUE)" << std::endl; + this->schedule->setTimeOrigin((u_int32_t) now); + + // While the first job can be scheduled now, schedule it + unsigned int i; + for (i = 0; i < this->cs->batch_queue.size(); i++) { + auto first_job = this->cs->batch_queue.at(i); + // If the job has already been allocated resources, it's already running anyway + if (not first_job->resources_allocated.empty()) { + continue; + } + auto earliest_start_time = this->schedule->findEarliestStartTime(first_job->requested_time, first_job->getRequestedNumNodes()); +// std::cerr << " LOOKING AT JOB " << first_job->getCompoundJob()->getName() << ": IT HAS EARLIEST START TIME " << earliest_start_time << "\n"; + // If the earliest start time is more than a second from now, never mind + if (std::abs(earliest_start_time - now) > 1) { +// std::cerr << " CAN'T SCHEDULE IT NOW, OH WELL\n"; + break; + } +// std::cerr << " SCHEDULING IT\n"; + // Insert the job into the schedule + this->schedule->add(earliest_start_time, earliest_start_time + first_job->getRequestedTime(), first_job); + first_job->easy_bf_start_date = earliest_start_time; + first_job->easy_bf_expected_end_date = earliest_start_time + first_job->getRequestedTime(); + WRENCH_INFO("Scheduled BatchComputeService job %lu on %lu nodes from time %u to %u", + first_job->getJobID(), first_job->getRequestedNumNodes(), + first_job->easy_bf_start_date, first_job->easy_bf_expected_end_date); + } + unsigned int first_job_not_started = i; + + if (first_job_not_started < this->cs->batch_queue.size()) { + + // At this point, the first job in the queue cannot start now, so determine when it could start + auto first_job_start_time = this->schedule->findEarliestStartTime( + this->cs->batch_queue.at(first_job_not_started)->requested_time, + this->cs->batch_queue.at(first_job_not_started)->getRequestedNumNodes()); + +// std::cerr << "THE FIRST JOB'S GUARANTEED START TIME IS: " << first_job_start_time << "\n"; + + // Go through all the other jobs, and start each one that can start + // (without hurting the first job in the queue if the depth is 1) + for (unsigned int i = first_job_not_started + 1; i < this->cs->batch_queue.size(); i++) { + auto candidate_job = this->cs->batch_queue.at(i); + if (not candidate_job->resources_allocated.empty()) { + continue; + } + auto earliest_start_time = this->schedule->findEarliestStartTime( + candidate_job->requested_time, + candidate_job->getRequestedNumNodes()); + + // If the job cannot start now, nevermind + if (std::abs(earliest_start_time - now) > 1) { + continue; + } + + // Tentatively add the job to the schedule + this->schedule->add(earliest_start_time, earliest_start_time + candidate_job->getRequestedTime(), candidate_job); + + // If the reservation depth is 1, then make sure the first job in the queue isn't impacted + if (this->_depth == 1) { + // Check whether starting the job now would postpone the first job in the queue + auto new_first_job_start_time = this->schedule->findEarliestStartTime( + this->cs->batch_queue.at(first_job_not_started)->requested_time, + this->cs->batch_queue.at(first_job_not_started)->getRequestedNumNodes()); + // If the first job would be harmed, remove the tentative job from the schedule + if (new_first_job_start_time > first_job_start_time) { + this->schedule->remove(earliest_start_time, + earliest_start_time + candidate_job->getRequestedTime(), + candidate_job); + } + } + } + } + +// std::cerr << "STARTING ALL THE JOBS THAT WERE SCHEDULED, GIVEN THIS SCHEDULE\n"; +// this->schedule->print(); + + // Start all non-started the jobs in the next slot! + std::set> next_jobs = this->schedule->getJobsInFirstSlot(); + if (next_jobs.empty()) { + this->compactSchedule(); + next_jobs = this->schedule->getJobsInFirstSlot(); + } + + for (auto const &batch_job: next_jobs) { + // If the job has already been allocated resources, it's already running anyway + if (not batch_job->resources_allocated.empty()) { + continue; + } + + // Get the workflow job associated to the picked BatchComputeService job + std::shared_ptr compound_job = batch_job->getCompoundJob(); + + // Find on which resources to actually run the job + unsigned long cores_per_node_asked_for = batch_job->getRequestedCoresPerNode(); + unsigned long num_nodes_asked_for = batch_job->getRequestedNumNodes(); + unsigned long requested_time = batch_job->getRequestedTime(); + + auto resources = this->scheduleOnHosts(num_nodes_asked_for, cores_per_node_asked_for, ComputeService::ALL_RAM); + if (resources.empty()) { + // Hmmm... we don't have the resources right now... we should get an update soon.... + return; + // throw std::runtime_error("Can't run BatchComputeService job " + std::to_string(batch_job->getJobID()) + " right now, this shouldn't happen!"); + } + + WRENCH_INFO("Starting BatchComputeService job %lu ", batch_job->getJobID()); +// std::cerr << "STARTING JOB " << batch_job->getCompoundJob()->getName() << "\n"; + + // Remove the job from the BatchComputeService queue + this->cs->removeJobFromBatchQueue(batch_job); + + // Add it to the running list + this->cs->running_jobs[batch_job->getCompoundJob()] = batch_job; + + // Start it! + this->cs->startJob(resources, compound_job, batch_job, num_nodes_asked_for, requested_time, + cores_per_node_asked_for); + } + } + + /** + * @brief Method to compact the schedule + */ + // THIS METHOD IS LIKELY USELESS FOR EASY...... + void EasyBackfillingBatchScheduler::compactSchedule() { + return; + WRENCH_INFO("Compacting schedule..."); + + std::cerr << "IN COMPACT SCHEDULE\n"; + +#ifdef PRINT_SCHEDULE + // WRENCH_INFO("BEFORE COMPACTING"); + this->schedule->print(); +#endif + + // For each job in the order of the BatchComputeService queue: + // - remove the job from the schedule + // - re-insert it as early as possible + + // Reset the time origin + auto now = (u_int32_t) Simulation::getCurrentSimulatedDate(); + this->schedule->setTimeOrigin(now); + + // Go through the BatchComputeService queue + for (auto const &batch_job: this->cs->batch_queue) { + // WRENCH_INFO("DEALING WITH JOB %lu", batch_job->getJobID()); + + // Remove the job from the schedule + // WRENCH_INFO("REMOVING IT FROM SCHEDULE"); + this->schedule->remove(batch_job->easy_bf_start_date, batch_job->easy_bf_expected_end_date + 100, batch_job); + // this->schedule->print(); + + // Find the earliest start time + // WRENCH_INFO("FINDING THE EARLIEST START TIME"); + auto est = this->schedule->findEarliestStartTime(batch_job->getRequestedTime(), batch_job->getRequestedNumNodes()); + // WRENCH_INFO("EARLIEST START TIME FOR IT: %u", est); + // Insert it in the schedule + this->schedule->add(est, est + batch_job->getRequestedTime(), batch_job); + // WRENCH_INFO("RE-INSERTED THERE!"); + // this->schedule->print(); + + batch_job->easy_bf_start_date = est; + batch_job->easy_bf_expected_end_date = est + batch_job->getRequestedTime(); + } + + + + +#ifdef PRINT_SCHEDULE + WRENCH_INFO("AFTER COMPACTING"); + this->schedule->print(); +#endif + } + + /** + * @brief Method to process a job completion + * @param batch_job: the job that completed + */ + void EasyBackfillingBatchScheduler::processJobCompletion(std::shared_ptr batch_job) { + WRENCH_INFO("Notified of completion of BatchComputeService job, %lu", batch_job->getJobID()); + + auto now = (u_int32_t) Simulation::getCurrentSimulatedDate(); + this->schedule->setTimeOrigin(now); + this->schedule->remove(now, batch_job->easy_bf_expected_end_date + 100, batch_job); + +#ifdef PRINT_SCHEDULE + this->schedule->print(); +#endif + + // TODO: REMOVE THIS, AS FOR EASY IT LIKELY DOESN'T DO ANYTHING + if (now < batch_job->easy_bf_expected_end_date) { + compactSchedule(); + } + } + + /** + * @brief Method to process a job termination + * @param batch_job: the job that was terminated + */ + void EasyBackfillingBatchScheduler::processJobTermination(std::shared_ptr batch_job) { + // Just like a job Completion to me! + this->processJobCompletion(batch_job); + } + + /** + * @brief Method to process a job failure + * @param batch_job: the job that failed + */ + void EasyBackfillingBatchScheduler::processJobFailure(std::shared_ptr batch_job) { + // Just like a job Completion to me! + this->processJobCompletion(batch_job); + } + + /** + * @brief Method to figure out on which actual resources a job could be scheduled right now + * @param num_nodes: number of nodes + * @param cores_per_node: number of cores per node + * @param ram_per_node: amount of RAM + * @return a host: map + * + */ + std::map> + EasyBackfillingBatchScheduler::scheduleOnHosts(unsigned long num_nodes, unsigned long cores_per_node, sg_size_t ram_per_node) { + if (ram_per_node == ComputeService::ALL_RAM) { + ram_per_node = S4U_Simulation::getHostMemoryCapacity(cs->available_nodes_to_cores.begin()->first); + } + if (cores_per_node == ComputeService::ALL_CORES) { + cores_per_node = cs->available_nodes_to_cores.begin()->first->get_core_count(); + } + + if (ram_per_node > S4U_Simulation::getHostMemoryCapacity(cs->available_nodes_to_cores.begin()->first)) { + throw std::runtime_error("CONSERVATIVE_BFBatchScheduler::scheduleOnHosts(): Asking for too much RAM per host"); + } + if (num_nodes > cs->available_nodes_to_cores.size()) { + throw std::runtime_error("CONSERVATIVE_BFBatchScheduler::scheduleOnHosts(): Asking for too many hosts"); + } + if (cores_per_node > (unsigned long) cs->available_nodes_to_cores.begin()->first->get_core_count()) { + throw std::runtime_error("CONSERVATIVE_BFBatchScheduler::scheduleOnHosts(): Asking for too many cores per host (asking for " + + std::to_string(cores_per_node) + " but hosts have " + + std::to_string(cs->available_nodes_to_cores.begin()->first->get_core_count()) + "cores)"); + } + + // IMPORTANT: We always give all cores to a job on a node! + cores_per_node = cs->available_nodes_to_cores.begin()->first->get_core_count(); + + return HomegrownBatchScheduler::selectHostsFirstFit(cs, num_nodes, cores_per_node, ram_per_node); + } + + /** + * @brief Method to obtain start time estimates + * @param set_of_jobs: a set of job specs + * @return map of estimates + */ + std::map EasyBackfillingBatchScheduler::getStartTimeEstimates( + std::set> set_of_jobs) { + std::map to_return; + + throw std::runtime_error("EasyBackfillingBatchScheduler::getStartTimeEstimates(): Method not implemented (ever?) for EASY backfilling"); + } + +}// namespace wrench diff --git a/test/services/compute_services/batch_standard_and_pilot_jobs/BatchServiceEASYBFTest.cpp b/test/services/compute_services/batch_standard_and_pilot_jobs/BatchServiceEASYBFTest.cpp new file mode 100644 index 0000000000..8533e5eb3a --- /dev/null +++ b/test/services/compute_services/batch_standard_and_pilot_jobs/BatchServiceEASYBFTest.cpp @@ -0,0 +1,747 @@ +/** + * Copyright (c) 2017. The WRENCH Team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include "../../../include/TestWithFork.h" +#include "../../../include/UniqueTmpPathPrefix.h" + +#define EPSILON 0.05 + +WRENCH_LOG_CATEGORY(batch_service_easy_bf_test, "Log category for BatchServiceEASYBFTest"); + +class BatchServiceEASY_BFTest : public ::testing::Test { + +public: + std::shared_ptr workflow; + + std::shared_ptr compute_service = nullptr; + + void do_EASY_BF_test(int num_compute_nodes, std::vector> spec); +// void do_SimpleEASY_BF_test_1(); +// void do_SimpleEASY_BF_test_2(); +// void do_SimpleEASY_BF_test_3(int seed); + + int _seed{}; + + +protected: + ~BatchServiceEASY_BFTest() override { + workflow->clear(); + wrench::Simulation::removeAllFiles(); + } + + BatchServiceEASY_BFTest() { + + // Create the simplest workflow + workflow = wrench::Workflow::createWorkflow(); + + // Create a four-host 10-core platform file + std::string xml = "" + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + FILE *platform_file = fopen(platform_file_path.c_str(), "w"); + fprintf(platform_file, "%s", xml.c_str()); + fclose(platform_file); + } + + std::string platform_file_path = UNIQUE_TMP_PATH_PREFIX + "platform.xml"; +}; + + +/**********************************************************************/ +/** SIMPLE EASY_BF GENERIC (DEPTH=0) **/ +/**********************************************************************/ + +class EASY_BFTest_WMS : public wrench::ExecutionController { + +public: + EASY_BFTest_WMS(BatchServiceEASY_BFTest *test, + const std::string& hostname, + std::vector> spec) : wrench::ExecutionController(hostname, "test") { + this->test = test; + this->spec = spec; + } + +private: + BatchServiceEASY_BFTest *test; + std::vector> spec; + + int main() override { + // Create a job manager + auto job_manager = this->createJobManager(); + + std::vector> jobs; + std::map completion_times; + std::map expected_completion_times; + + bool print_completion_times = false; + // Create and submit all jobs + for (auto const &job_spec : spec) { + std::string job_name =std::get<0>(job_spec); + int num_nodes = std::get<1>(job_spec); + int duration = std::get<2>(job_spec); + int sleep_after = std::get<3>(job_spec); + expected_completion_times[job_name] = std::get<4>(job_spec); + if (expected_completion_times[job_name] < 0) { + print_completion_times = true; + } + + auto job = job_manager->createCompoundJob(job_name); + job->addSleepAction("sleep" + std::to_string(duration), duration); + std::map args = {{"-N", std::to_string(num_nodes)}, {"-t", std::to_string(duration)}, {"-c", "10"}}; + job_manager->submitJob(job, this->test->compute_service, args); + jobs.push_back(job); + wrench::Simulation::sleep(sleep_after); + } + + for (unsigned int i=0; i < jobs.size(); i++) { + auto event = this->waitForNextEvent(); + if (auto real_event = std::dynamic_pointer_cast(event)) { + completion_times[real_event->job->getName()] = wrench::Simulation::getCurrentSimulatedDate(); + } else { + throw std::runtime_error("Unexpected workflow execution event: " + event->toString()); + } + } + + if (print_completion_times) { + for (auto const &item: completion_times) { + std::cerr << " " << item.first << ": " << item.second << std::endl; + } + } else { + for (auto const &item: completion_times) { + if (std::abs(item.second - expected_completion_times[item.first]) > 0.001) { + throw std::runtime_error("Invalid job completion time for " + item.first + ": " + + std::to_string(item.second) + "(should be " + + std::to_string(expected_completion_times[item.first]) + ")"); + } + } + } + + return 0; + } +}; + +void BatchServiceEASY_BFTest::do_EASY_BF_test(int num_compute_nodes, + std::vector> spec) { + + // Create and initialize a simulation + auto simulation = wrench::Simulation::createSimulation(); + int argc = 1; + auto argv = (char **) calloc(argc, sizeof(char *)); + argv[0] = strdup("unit_test"); +// argv[1] = strdup("--wrench-full-log"); + + ASSERT_NO_THROW(simulation->init(&argc, argv)); + + // Setting up the platform + ASSERT_NO_THROW(simulation->instantiatePlatform(platform_file_path)); + + // Get a hostname + std::string hostname = "Host1"; + +#ifdef ENABLE_BATSCHED + std::string scheduling_algorithm = "easy_bf"; +#else + std::string scheduling_algorithm = "easy_bf_depth0"; +#endif + + std::vector compute_hosts; + for (int i=1; i <= num_compute_nodes; i++) { + compute_hosts.push_back("Host" + std::to_string(i)); + } + + // Create a Batch Service with a fcfs scheduling algorithm + ASSERT_NO_THROW(compute_service = simulation->add( + new wrench::BatchComputeService(hostname, compute_hosts, "", + {{wrench::BatchComputeServiceProperty::BATCH_SCHEDULING_ALGORITHM, scheduling_algorithm}, + {wrench::BatchComputeServiceProperty::BATCH_RJMS_PADDING_DELAY, "0"}}))); + + simulation->add(new wrench::FileRegistryService(hostname)); + + // Create a WMS + ASSERT_NO_THROW(simulation->add(new EASY_BFTest_WMS(this, hostname, spec))); + + ASSERT_NO_THROW(simulation->launch()); + + for (int i = 0; i < argc; i++) + free(argv[i]); + free(argv); +} + + + +#ifdef ENABLE_BATSCHED +TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_1) +#else +TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_1) +#endif +{ + // job_name, num_nodes, duration, sleep_after, expected CT + std::vector> spec = { + {"job1", 2, 60, 0, 60}, + {"job2", 4, 30, 0, 110}, + {"job3", 2, 30, 0, 30}, + {"job4", 2, 50, 0, 80} + }; + + DO_TEST_WITH_FORK_TWO_ARGS(do_EASY_BF_test, 4, spec); +} + + +#ifdef ENABLE_BATSCHED +TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_2) +#else +TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_2) +#endif +{ + // job_name, num_nodes, duration, sleep_after, expected CT + std::vector> spec = { + {"job1", 1, 6000, 0, 6000}, + {"job2", 2, 70, 0, 70}, + {"job3", 4, 20, 0, 90}, + {"job4", 5, 20, 0, 6020}, + {"job5", 1, 6000, 0, 6000}, + }; + + DO_TEST_WITH_FORK_TWO_ARGS(do_EASY_BF_test, 6, spec); +} + +#ifdef ENABLE_BATSCHED +TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_3) +#else +TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_3) +#endif +{ + // job_name, num_nodes, duration, sleep_after, expected CT + std::vector> spec = { + {"job1", 3, 660, 1, 660}, + {"job2", 1, 120, 1, 121}, + {"job3", 3, 1740, 1, 660 + 1740}, + {"job4", 1, 1080, 1, 1083}, + }; + + DO_TEST_WITH_FORK_TWO_ARGS(do_EASY_BF_test, 6, spec); +} + +#ifdef ENABLE_BATSCHED +TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_RANDOM) +#else +TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_RANDOM) +#endif +{ + int num_jobs = 5; + for (int seed = 0; seed < 10; seed++) { + std::vector> spec; + std::cerr << "SEED: " << seed << "\n"; + unsigned int random = seed; + for (int i = 1; i <= num_jobs; i++) { + std::string job_name = "job" + std::to_string(i); + random = random * 17 + 4123451; + unsigned int num_nodes = 1 + random % 4; + random = random * 17 + 4123451; + unsigned int duration = 60 + 60 * (random % 30); + int expected_ct = -1; + spec.push_back(std::make_tuple(job_name, num_nodes, duration, 0, expected_ct)); + } + DO_TEST_WITH_FORK_TWO_ARGS(do_EASY_BF_test, 6, spec); + } +} + + + + +#if 0 + +/**********************************************************************/ +/** SIMPLE EASY_BF TEST #1 (DEPTH=0) **/ +/**********************************************************************/ + +class SimpleEASY_BFTest_1_WMS : public wrench::ExecutionController { + +public: + SimpleEASY_BFTest_1_WMS(BatchServiceEASY_BFTest *test, + const std::string& hostname) : wrench::ExecutionController(hostname, "test") { + this->test = test; + } + +private: + BatchServiceEASY_BFTest *test; + + int main() override { + // Create a job manager + auto job_manager = this->createJobManager(); + + // |333 222 + // |333 222 + // |111111 222 444 + // |111111 222 444 + + auto job1 = job_manager->createCompoundJob("job1"); + job1->addSleepAction("sleep60", 60); + std::map job1_args = {{"-N", "2"}, {"-t", "60"}, {"-c", "10"}}; + job_manager->submitJob(job1, this->test->compute_service, job1_args); + + auto job2 = job_manager->createCompoundJob("job2"); + job2->addSleepAction("sleep60", 30); + std::map job2_args = {{"-N", "4"}, {"-t", "30"}, {"-c", "10"}}; + job_manager->submitJob(job2, this->test->compute_service, job2_args); + + auto job3 = job_manager->createCompoundJob("job3"); + job3->addSleepAction("sleep60", 30); + std::map job3_args = {{"-N", "2"}, {"-t", "30"}, {"-c", "10"}}; + job_manager->submitJob(job3, this->test->compute_service, job3_args); + + auto job4 = job_manager->createCompoundJob("job4"); + job4->addSleepAction("sleep50", 50); + std::map job4_args = {{"-N", "2"}, {"-t", "50"}, {"-c", "10"}}; + job_manager->submitJob(job4, this->test->compute_service, job4_args); + + std::map completion_times; + for (int i=0; i < 4; i++) { + auto event = this->waitForNextEvent(); + if (auto real_event = std::dynamic_pointer_cast(event)) { + completion_times[real_event->job->getName()] = wrench::Simulation::getCurrentSimulatedDate(); + } else { + throw std::runtime_error("Unexpected workflow execution event: " + event->toString()); + } + } + + std::map expected_completion_times = { + {"job1", 60}, + {"job2", 110}, + {"job3", 30}, + {"job4", 80} + }; + + for (auto const &item : completion_times) { + std::cerr << item.first << ": " << item.second << std::endl; + } + + for (auto const &item : completion_times) { + if (std::abs(item.second - expected_completion_times[item.first]) > 0.001) { + throw std::runtime_error("Invalid job completion time for " + item.first + ": " + + std::to_string(item.second) + "(should be " + std::to_string(expected_completion_times[item.first]) + ")"); + } + } + + return 0; + } +}; + +#ifdef ENABLE_BATSCHED +TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_1) +#else +TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_1) +#endif +{ + DO_TEST_WITH_FORK(do_SimpleEASY_BF_test_1); +} + +void BatchServiceEASY_BFTest::do_SimpleEASY_BF_test_1() { + + // Create and initialize a simulation + auto simulation = wrench::Simulation::createSimulation(); + int argc = 1; + auto argv = (char **) calloc(argc, sizeof(char *)); + argv[0] = strdup("unit_test"); +// argv[1] = strdup("--wrench-full-log"); + + ASSERT_NO_THROW(simulation->init(&argc, argv)); + + // Setting up the platform + ASSERT_NO_THROW(simulation->instantiatePlatform(platform_file_path)); + + // Get a hostname + std::string hostname = "Host1"; + +#ifdef ENABLE_BATSCHED + std::string scheduling_algorithm = "easy_bf"; +#else + std::string scheduling_algorithm = "easy_bf_depth0"; +#endif + + // Create a Batch Service with a fcfs scheduling algorithm + ASSERT_NO_THROW(compute_service = simulation->add( + new wrench::BatchComputeService(hostname, {"Host1", "Host2", "Host3", "Host4"}, "", + {{wrench::BatchComputeServiceProperty::BATCH_SCHEDULING_ALGORITHM, scheduling_algorithm}, + {wrench::BatchComputeServiceProperty::BATCH_RJMS_PADDING_DELAY, "0"}}))); + + simulation->add(new wrench::FileRegistryService(hostname)); + + // Create a WMS + ASSERT_NO_THROW(simulation->add(new SimpleEASY_BFTest_1_WMS(this, hostname))); + + ASSERT_NO_THROW(simulation->launch()); + + for (int i = 0; i < argc; i++) + free(argv[i]); + free(argv); +} + +/**********************************************************************/ +/** SIMPLE EASY_BF TEST #2 (DEPTH=0) **/ +/**********************************************************************/ + +class SimpleEASY_BFTest_2_WMS : public wrench::ExecutionController { + +public: + SimpleEASY_BFTest_2_WMS(BatchServiceEASY_BFTest *test, + const std::string& hostname) : wrench::ExecutionController(hostname, "test") { + this->test = test; + } + +private: + BatchServiceEASY_BFTest *test; + + int main() override { + // Create a job manager + auto job_manager = this->createJobManager(); + + // | 44 + // | 3344 + // | 3344 + // |22222223344 + // |22222223344 + // |1111111111111111111111111111111111111111111 + // + // And then see if job 4 gets postponed by a long 1-node job (job 5) + std::set> jobs; + + { + auto job = job_manager->createCompoundJob("job1"); + int num_nodes = 1; + int time = 6000; + job->addSleepAction("sleep" + std::to_string(time), time); + std::map job_args = {{"-N", std::to_string(num_nodes)}, + {"-t", std::to_string(time)}, + {"-c", "10"}}; + job_manager->submitJob(job, this->test->compute_service, job_args); + jobs.insert(job); + } + + { + auto job = job_manager->createCompoundJob("job2"); + int num_nodes = 2; + int time = 70; + job->addSleepAction("sleep" + std::to_string(time), time); + std::map job_args = {{"-N", std::to_string(num_nodes)}, + {"-t", std::to_string(time)}, + {"-c", "10"}}; + job_manager->submitJob(job, this->test->compute_service, job_args); + jobs.insert(job); + } + + { + auto job = job_manager->createCompoundJob("job3"); + int num_nodes = 4; + int time = 20; + job->addSleepAction("sleep" + std::to_string(time), time); + std::map job_args = {{"-N", std::to_string(num_nodes)}, + {"-t", std::to_string(time)}, + {"-c", "10"}}; + job_manager->submitJob(job, this->test->compute_service, job_args); + jobs.insert(job); + } + + { + auto job = job_manager->createCompoundJob("job4"); + int num_nodes = 5; + int time = 20; + job->addSleepAction("sleep" + std::to_string(time), time); + std::map job_args = {{"-N", std::to_string(num_nodes)}, + {"-t", std::to_string(time)}, + {"-c", "10"}}; + job_manager->submitJob(job, this->test->compute_service, job_args); + jobs.insert(job); + } + + { + auto job = job_manager->createCompoundJob("job5"); + int num_nodes = 1; + int time = 6000; + job->addSleepAction("sleep" + std::to_string(time), time); + std::map job_args = {{"-N", std::to_string(num_nodes)}, + {"-t", std::to_string(time)}, + {"-c", "10"}}; + job_manager->submitJob(job, this->test->compute_service, job_args); + jobs.insert(job); + } + + + std::map completion_times; + for (unsigned int i=0; i < jobs.size(); i++) { + auto event = this->waitForNextEvent(); + if (auto real_event = std::dynamic_pointer_cast(event)) { + completion_times[real_event->job->getName()] = wrench::Simulation::getCurrentSimulatedDate(); + } else { + throw std::runtime_error("Unexpected workflow execution event: " + event->toString()); + } + } + + std::map expected_completion_times = { + {"job1", 6000}, + {"job2", 70}, + {"job3", 90}, + {"job4", 6020}, + {"job5", 6000}, + }; + + for (auto const &item : completion_times) { + std::cerr << item.first << ": " << item.second << std::endl; + if (std::abs(item.second - expected_completion_times[item.first]) > 0.001) { + throw std::runtime_error("Invalid job completion time for " + item.first + ": " + + std::to_string(item.second) + "(should be " + std::to_string(expected_completion_times[item.first]) + ")"); + } + } + + return 0; + } +}; + +#ifdef ENABLE_BATSCHED +//TEST_F(BatchServiceEASY_BFTest, DISABLED_SimpleEASY_BFTest_2) +TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_2) +#else +TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_2) +#endif +{ + DO_TEST_WITH_FORK(do_SimpleEASY_BF_test_2); +} + +void BatchServiceEASY_BFTest::do_SimpleEASY_BF_test_2() { + + // Create and initialize a simulation + auto simulation = wrench::Simulation::createSimulation(); + int argc = 1; + auto argv = (char **) calloc(argc, sizeof(char *)); + argv[0] = strdup("unit_test"); +// argv[1] = strdup("--wrench-full-log"); + + ASSERT_NO_THROW(simulation->init(&argc, argv)); + + // Setting up the platform + ASSERT_NO_THROW(simulation->instantiatePlatform(platform_file_path)); + + // Get a hostname + std::string hostname = "Host1"; + +#ifdef ENABLE_BATSCHED + std::string scheduling_algorithm = "easy_bf"; +#else + std::string scheduling_algorithm = "easy_bf_depth0"; +#endif + + + // Create a Batch Service with a fcfs scheduling algorithm + ASSERT_NO_THROW(compute_service = simulation->add( + new wrench::BatchComputeService(hostname, {"Host1", "Host2", "Host3", "Host4", "Host5", "Host6"}, "", + {{wrench::BatchComputeServiceProperty::BATCH_SCHEDULING_ALGORITHM, scheduling_algorithm}, + {wrench::BatchComputeServiceProperty::BATCH_RJMS_PADDING_DELAY, "0"}}))); + + simulation->add(new wrench::FileRegistryService(hostname)); + + // Create a WMS + ASSERT_NO_THROW(simulation->add(new SimpleEASY_BFTest_2_WMS(this, hostname))); + + ASSERT_NO_THROW(simulation->launch()); + + for (int i = 0; i < argc; i++) + free(argv[i]); + free(argv); +} + +/**********************************************************************/ +/** SIMPLE EASY_BF TEST #3 (DEPTH=0) **/ +/**********************************************************************/ + +#define NUM_JOBS 4 + +class SimpleEASY_BFTest_LARGE_WMS : public wrench::ExecutionController { + +public: + SimpleEASY_BFTest_LARGE_WMS(BatchServiceEASY_BFTest *test, + const std::string& hostname) : wrench::ExecutionController(hostname, "test") { + this->test = test; + } + +private: + BatchServiceEASY_BFTest *test; + + int main() override { + // Create a job manager + auto job_manager = this->createJobManager(); + + unsigned int random = this->test->_seed; + + std::shared_ptr jobs[NUM_JOBS]; + // Create 4 1-min tasks and submit them as various shaped jobs + for (int i = 0; i < NUM_JOBS; i++) { + random = random * 17 + 4123451; + jobs[i] = job_manager->createCompoundJob("job" + std::to_string(i)); + int sleep_time = 60 + 60 * (random % 30); + jobs[i]->addSleepAction("sleep" + std::to_string(sleep_time), sleep_time); + } + + // Submit jobs + try { + for (auto & job : jobs) { + std::map job_specific_args; + random = random * 17 + 4123451; + job_specific_args["-N"] = std::to_string(1 + random % 4); + random = random * 17 + 4123451; + job_specific_args["-t"] = std::to_string(60 * (1 + random % 100)); + auto sleep_action = std::dynamic_pointer_cast(*job->getActions().begin()); + job_specific_args["-t"] = std::to_string(sleep_action->getSleepTime()); + job_specific_args["-c"] = "10"; + job_manager->submitJob(job, this->test->compute_service, job_specific_args); + wrench::Simulation::sleep(1); + } + } catch (wrench::ExecutionException &e) { + throw std::runtime_error( + "Unexpected exception while submitting job"); + } + + std::map, double>> actual_completion_times; + for (int i = 0; i < NUM_JOBS; i++) { + // Wait for a workflow execution event + std::shared_ptr event; + try { + event = this->waitForNextEvent(); + } catch (wrench::ExecutionException &e) { + throw std::runtime_error("Error while getting and execution event: " + e.getCause()->toString()); + } + if (auto real_event = std::dynamic_pointer_cast(event)) { + actual_completion_times[real_event->job->getName()] = std::make_pair(real_event->job, wrench::Simulation::getCurrentSimulatedDate()); + } else if (auto real_event = std::dynamic_pointer_cast(event)) { + actual_completion_times[real_event->job->getName()] = std::make_pair(real_event->job, + wrench::Simulation::getCurrentSimulatedDate()); + } else { + throw std::runtime_error("Unexpected workflow execution event: " + event->toString()); + } + } + + // Print Completion times: +#if 1 + std::cerr << "--------------\n"; + for (auto const &i : actual_completion_times) { + auto job = i.second.first; + double completion_time = i.second.second; + std::shared_ptr sleep_action = std::dynamic_pointer_cast(*job->getActions().begin()); + std::cerr << "- " << i.first.c_str() << ":" << "\t-N:" << + job->getServiceSpecificArguments()["-N"].c_str() << " -t:"<< + job->getServiceSpecificArguments()["-t"].c_str() << " (real=" << + sleep_action->getSleepTime() << ") \tCT="<< + completion_time << "\n"; +// WRENCH_INFO("COMPLETION TIME %s (%s nodes, %s seconds): %lf", +// i.first.c_str(), +// job->getServiceSpecificArguments()["-N"].c_str(), +// job->getServiceSpecificArguments()["-t"].c_str(), +// completion_time); + } +#endif + + + return 0; + } +}; + + + +#ifdef ENABLE_BATSCHED +//TEST_F(BatchServiceEASY_BFTest, DISABLED_SimpleEASY_BFTest_3) +TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_3) +#else +TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_3) +#endif +{ + for (int i=7; i < 8; i++) { + std::cerr << "SEED = " << i << "\n"; + DO_TEST_WITH_FORK_ONE_ARG(do_SimpleEASY_BF_test_3, i); + } +} + +void BatchServiceEASY_BFTest::do_SimpleEASY_BF_test_3(int seed) { + + // Create and initialize a simulation + auto simulation = wrench::Simulation::createSimulation(); + int argc = 1; + auto argv = (char **) calloc(argc, sizeof(char *)); + argv[0] = strdup("unit_test"); +// argv[1] = strdup("--wrench-full-log"); + + this->_seed = seed; + + ASSERT_NO_THROW(simulation->init(&argc, argv)); + + // Setting up the platform + ASSERT_NO_THROW(simulation->instantiatePlatform(platform_file_path)); + + // Get a hostname + std::string hostname = "Host1"; + +#ifdef ENABLE_BATSCHED + std::string scheduling_algorithm = "easy_bf"; +#else + std::string scheduling_algorithm = "easy_bf_depth0"; +#endif + + + // Create a Batch Service with a fcfs scheduling algorithm + ASSERT_NO_THROW(compute_service = simulation->add( + new wrench::BatchComputeService(hostname, {"Host1", "Host2", "Host3", "Host4", "Host5", "Host6"}, "", + {{wrench::BatchComputeServiceProperty::BATCH_SCHEDULING_ALGORITHM, scheduling_algorithm}, + {wrench::BatchComputeServiceProperty::BATCH_RJMS_PADDING_DELAY, "0"}, + {wrench::BatchComputeServiceProperty::BATSCHED_LOGGING_MUTED, "true"}}))); + + simulation->add(new wrench::FileRegistryService(hostname)); + + // Create a WMS + ASSERT_NO_THROW(simulation->add(new SimpleEASY_BFTest_LARGE_WMS(this, hostname))); + + ASSERT_NO_THROW(simulation->launch()); + + for (int i = 0; i < argc; i++) + free(argv[i]); + free(argv); +} + +#endif \ No newline at end of file