diff --git a/include/wrench/services/compute/batch/BatchComputeServiceProperty.h b/include/wrench/services/compute/batch/BatchComputeServiceProperty.h index ebb6eee5b..e9f8ec86e 100755 --- a/include/wrench/services/compute/batch/BatchComputeServiceProperty.h +++ b/include/wrench/services/compute/batch/BatchComputeServiceProperty.h @@ -29,11 +29,22 @@ 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) + * - "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. + * This is typically considered the standard EASY algorithm. + * + * - "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) * * - If ENABLE_BATSCHED is set to on: * - whatever scheduling algorithm is supported by Batsched 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 index 827c613b9..69dc8af0b 100644 --- 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 @@ -50,7 +50,7 @@ namespace wrench { // 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; +// 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 @@ -68,7 +68,7 @@ namespace wrench { // std::cerr << " CAN'T SCHEDULE IT NOW, OH WELL\n"; break; } -// std::cerr << " SCHEDULING IT\n"; +// std::cerr << " SCHEDULING JOB FOR START: " << first_job->getCompoundJob()->getName() << "\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; @@ -86,12 +86,13 @@ namespace wrench { 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"; +// std::cerr << "THE FIRST JOB'S (" << this->cs->batch_queue.at(first_job_not_started)->getCompoundJob()->getName() << ") 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; } @@ -105,19 +106,25 @@ namespace wrench { } // 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) { + if (this->_depth == 0) { + this->schedule->add(earliest_start_time, earliest_start_time + candidate_job->getRequestedTime(), + candidate_job); +// std::cerr << "BACKFILL_D0: SCHEDULING JOB FOR START: " << candidate_job->getCompoundJob()->getName() << "\n"; + } else if (this->_depth == 1) { + this->schedule->add(earliest_start_time, earliest_start_time + candidate_job->getRequestedTime(), + candidate_job); // 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 +// std::cerr << "BACKFILL? OLD=" << first_job_start_time << " NEW=" << new_first_job_start_time << "\n"; if (new_first_job_start_time > first_job_start_time) { this->schedule->remove(earliest_start_time, earliest_start_time + candidate_job->getRequestedTime(), candidate_job); + } else { +// std::cerr << "BACKFILL_D1: SCHEDULING JOB FOR START: " << candidate_job->getCompoundJob()->getName() << " AT TIME " << earliest_start_time << "\n"; } } } @@ -126,14 +133,16 @@ namespace wrench { // 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! + // Start all non-started the jobs in the next slot! +// std::cerr << "GETTING THE JOBS IN THE NEXT SLOT\n"; std::set> next_jobs = this->schedule->getJobsInFirstSlot(); - if (next_jobs.empty()) { - this->compactSchedule(); - next_jobs = this->schedule->getJobsInFirstSlot(); - } +// if (next_jobs.empty()) { +// this->compactSchedule(); +// next_jobs = this->schedule->getJobsInFirstSlot(); +// } for (auto const &batch_job: next_jobs) { +// std::cerr << " --> " << batch_job->getCompoundJob()->getName() << "\n"; // If the job has already been allocated resources, it's already running anyway if (not batch_job->resources_allocated.empty()) { continue; 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 index 8533e5eb3..a328cacf6 100644 --- a/test/services/compute_services/batch_standard_and_pilot_jobs/BatchServiceEASYBFTest.cpp +++ b/test/services/compute_services/batch_standard_and_pilot_jobs/BatchServiceEASYBFTest.cpp @@ -28,13 +28,9 @@ class BatchServiceEASY_BFTest : public ::testing::Test { 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{}; - + void do_EASY_BF_test(int num_compute_nodes, + std::vector> spec, + bool print_completion_times); protected: ~BatchServiceEASY_BFTest() override { @@ -96,34 +92,33 @@ 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") { + std::vector> &spec, + bool print_completion_times) : wrench::ExecutionController(hostname, "test") { this->test = test; this->spec = spec; + this->print_completion_times = print_completion_times; } private: BatchServiceEASY_BFTest *test; std::vector> spec; + bool print_completion_times; int main() override { // Create a job manager auto job_manager = this->createJobManager(); std::vector> jobs; - std::map completion_times; + 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); + auto num_nodes = std::get<1>(job_spec); + auto duration = std::get<2>(job_spec); + auto 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); @@ -136,7 +131,14 @@ class EASY_BFTest_WMS : public wrench::ExecutionController { 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(); + auto job = real_event->job; + auto sleep_action = *(job->getActions().begin()); + completion_times[real_event->job->getName()] = + std::make_tuple(job->getSubmitDate(), + job->getServiceSpecificArguments()["-N"], + job->getServiceSpecificArguments()["-t"], + sleep_action->getStartDate(), + wrench::Simulation::getCurrentSimulatedDate()); } else { throw std::runtime_error("Unexpected workflow execution event: " + event->toString()); } @@ -144,15 +146,19 @@ class EASY_BFTest_WMS : public wrench::ExecutionController { if (print_completion_times) { for (auto const &item: completion_times) { - std::cerr << " " << item.first << ": " << item.second << std::endl; + std::cerr << " " << item.first << + " (arr=" << std::get<0>(item.second) << + ", N=" << std::get<1>(item.second) << + ", t=" << std::get<2>(item.second) << "): " << + std::get<3>(item.second) << " -> " << + std::get<4>(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]) + ")"); - } + } + for (auto const &item: completion_times) { + if ((expected_completion_times[item.first] > 0) and (std::abs(std::get<3>(item.second) - expected_completion_times[item.first]) > 0.001)) { + throw std::runtime_error("Invalid job completion time for " + item.first + ": " + + std::to_string(std::get<3>(item.second)) + " (expected: " + + std::to_string(expected_completion_times[item.first]) + ")"); } } @@ -161,14 +167,16 @@ class EASY_BFTest_WMS : public wrench::ExecutionController { }; void BatchServiceEASY_BFTest::do_EASY_BF_test(int num_compute_nodes, - std::vector> spec) { + std::vector> spec, + bool print_completion_times) { // Create and initialize a simulation auto simulation = wrench::Simulation::createSimulation(); - int argc = 1; + int argc = 2; auto argv = (char **) calloc(argc, sizeof(char *)); argv[0] = strdup("unit_test"); -// argv[1] = strdup("--wrench-full-log"); + argv[1] = strdup("--wrench-commport-pool-size=50000"); +// argv[3] = strdup("--wrench-full-log"); ASSERT_NO_THROW(simulation->init(&argc, argv)); @@ -181,7 +189,7 @@ void BatchServiceEASY_BFTest::do_EASY_BF_test(int num_compute_nodes, #ifdef ENABLE_BATSCHED std::string scheduling_algorithm = "easy_bf"; #else - std::string scheduling_algorithm = "easy_bf_depth0"; + std::string scheduling_algorithm = "easy_bf_depth1"; #endif std::vector compute_hosts; @@ -198,7 +206,7 @@ void BatchServiceEASY_BFTest::do_EASY_BF_test(int num_compute_nodes, 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->add(new EASY_BFTest_WMS(this, hostname, spec, print_completion_times))); ASSERT_NO_THROW(simulation->launch()); @@ -215,6 +223,8 @@ TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_1) TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_1) #endif { + // BATSCHED FAILS: DOES NOT BACKFILL job4, SO DEPTH=1? + // job_name, num_nodes, duration, sleep_after, expected CT std::vector> spec = { {"job1", 2, 60, 0, 60}, @@ -223,7 +233,7 @@ TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_1) {"job4", 2, 50, 0, 80} }; - DO_TEST_WITH_FORK_TWO_ARGS(do_EASY_BF_test, 4, spec); + DO_TEST_WITH_FORK_THREE_ARGS(do_EASY_BF_test, 4, spec, false); } @@ -242,7 +252,7 @@ TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_2) {"job5", 1, 6000, 0, 6000}, }; - DO_TEST_WITH_FORK_TWO_ARGS(do_EASY_BF_test, 6, spec); + DO_TEST_WITH_FORK_THREE_ARGS(do_EASY_BF_test, 6, spec, false); } #ifdef ENABLE_BATSCHED @@ -251,6 +261,8 @@ TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_3) TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_3) #endif { + // BATSCHED FAIL: DOES NOT BACKFILL JOB4, SO DEPTH=1????? + // job_name, num_nodes, duration, sleep_after, expected CT std::vector> spec = { {"job1", 3, 660, 1, 660}, @@ -259,7 +271,7 @@ TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_3) {"job4", 1, 1080, 1, 1083}, }; - DO_TEST_WITH_FORK_TWO_ARGS(do_EASY_BF_test, 6, spec); + DO_TEST_WITH_FORK_THREE_ARGS(do_EASY_BF_test, 6, spec, false); } #ifdef ENABLE_BATSCHED @@ -268,10 +280,10 @@ TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_RANDOM) TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_RANDOM) #endif { - int num_jobs = 5; + int num_jobs = 100; for (int seed = 0; seed < 10; seed++) { std::vector> spec; - std::cerr << "SEED: " << seed << "\n"; +// 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); @@ -280,468 +292,10 @@ TEST_F(BatchServiceEASY_BFTest, SimpleEASY_BFTest_RANDOM) 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()); - } + spec.emplace_back(job_name, num_nodes, duration, 0, expected_ct); } - - // 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); + DO_TEST_WITH_FORK_THREE_ARGS(do_EASY_BF_test, 6, spec, false); } } -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