1 // Copyright (C) 2020 EDF R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 #include <cppunit/CompilerOutputter.h>
19 #include <cppunit/TestResult.h>
20 #include <cppunit/TestResultCollector.h>
21 #include <cppunit/TextTestProgressListener.h>
22 #include <cppunit/BriefTestProgressListener.h>
23 #include <cppunit/extensions/TestFactoryRegistry.h>
24 #include <cppunit/TestRunner.h>
25 #include <cppunit/TextTestRunner.h>
31 #include <cppunit/extensions/HelperMacros.h>
40 #include "../WorkloadManager.hxx"
41 #include "../DefaultAlgorithm.hxx"
43 constexpr bool ACTIVATE_DEBUG_LOG = true;
44 template<typename... Ts>
45 void DEBUG_LOG(Ts... args)
47 if(! ACTIVATE_DEBUG_LOG)
49 if(sizeof...(Ts) == 0)
51 std::ostringstream message;
52 // TODO: C++17 solution: ( (message << args), ...);
53 // since initializer lists guarantee sequencing, this can be used to
54 // call a function on each element of a pack, in order:
55 int dummy[] = { (message << args, 0)...};
57 std::cerr << message.str();
64 virtual void check(const WorkloadManager::Container& c, MyTask* t)=0;
67 template <std::size_t size_R, std::size_t size_T>
68 class Checker : public AbstractChecker
72 void check(const WorkloadManager::Container& c, MyTask* t)override;
76 WorkloadManager::Resource resources[size_R];
77 WorkloadManager::ContainerType types[size_T];
80 int _maxContainersForResource[size_R][size_T];
83 class MyTask : public WorkloadManager::Task
86 const WorkloadManager::ContainerType* type()const override {return _type;}
87 void run(const WorkloadManager::Container& c)override
89 _check->check(c, this);
91 DEBUG_LOG("Running task ", _id, " on ", c.resource->name, "-", c.type->name,
93 std::this_thread::sleep_for(std::chrono::seconds(_sleep));
94 DEBUG_LOG("Finish task ", _id);
98 const WorkloadManager::ContainerType* type,
100 AbstractChecker * check
110 const WorkloadManager::ContainerType* _type = nullptr;
112 AbstractChecker * _check;
115 template <std::size_t size_R, std::size_t size_T>
116 Checker<size_R, size_T>::Checker()
118 for(std::size_t i=0; i < size_R; i ++)
121 std::ostringstream name;
123 resources[i].name = name.str();
126 for(std::size_t i=0; i < size_T; i ++)
129 std::ostringstream name;
131 types[i].name = name.str();
134 for(std::size_t i=0; i < size_R; i++)
135 for(std::size_t j=0; j < size_T; j++)
136 _maxContainersForResource[i][j] = 0;
139 template <std::size_t size_R, std::size_t size_T>
140 void Checker<size_R, size_T>::check(const WorkloadManager::Container& c,
143 std::unique_lock<std::mutex> lock(_mutex);
144 int& max = _maxContainersForResource[c.resource->id][c.type->id];
149 template <std::size_t size_R, std::size_t size_T>
150 void Checker<size_R, size_T>::globalCheck()
152 for(std::size_t i=0; i < size_R; i++)
154 float global_max = 0;
155 for(std::size_t j=0; j < size_T; j++)
157 int max = _maxContainersForResource[i][j];
158 DEBUG_LOG(resources[i].name, ", ", types[j].name, ":", max+1);
159 CPPUNIT_ASSERT( (max+1) * types[j].neededCores <= resources[i].nbCores );
160 global_max += types[j].neededCores * float(max+1);
162 DEBUG_LOG(resources[i].name, " global: ", global_max);
163 CPPUNIT_ASSERT(global_max >= resources[i].nbCores); // cores fully used
167 template <std::size_t size_R, std::size_t size_T>
168 void Checker<size_R, size_T>::reset()
170 for(std::size_t i=0; i < size_R; i++)
171 for(std::size_t j=0; j < size_T; j++)
172 _maxContainersForResource[i][j] = 0;
175 class MyTest: public CppUnit::TestFixture
177 CPPUNIT_TEST_SUITE(MyTest);
179 CPPUNIT_TEST_SUITE_END();
186 constexpr std::size_t resourcesNumber = 2;
187 constexpr std::size_t typesNumber = 2;
188 Checker<resourcesNumber, typesNumber> check;
189 check.resources[0].nbCores = 10;
190 check.resources[1].nbCores = 18;
191 check.types[0].neededCores = 4.0;
192 check.types[1].neededCores = 1.0;
194 for(std::size_t i=0; i < resourcesNumber; i ++)
195 DEBUG_LOG(check.resources[i].name, " has ", check.resources[i].nbCores,
197 for(std::size_t i=0; i < typesNumber; i ++)
198 DEBUG_LOG(check.types[i].name, " needs ", check.types[i].neededCores,
201 constexpr std::size_t tasksNumber = 100;
202 MyTask tasks[tasksNumber];
203 for(std::size_t i = 0; i < tasksNumber / 2; i++)
204 tasks[i].reset(i, &check.types[0], 2, &check);
205 for(std::size_t i = tasksNumber / 2; i < tasksNumber; i++)
206 tasks[i].reset(i, &check.types[1], 1, &check);
208 DEBUG_LOG("Number of tasks: ", tasksNumber);
209 DEBUG_LOG("Tasks from 0 to ", tasksNumber/2, " are ", tasks[0].type()->name);
210 DEBUG_LOG("Tasks from ", tasksNumber/2, " to ", tasksNumber, " are ",
211 tasks[tasksNumber / 2].type()->name);
213 WorkloadManager::DefaultAlgorithm algo;
214 WorkloadManager::WorkloadManager wlm(algo);
215 for(std::size_t i=0; i < resourcesNumber; i ++)
216 wlm.addResource(&check.resources[i]);
218 // Add 4 core tasks first
220 for(std::size_t i = 0; i < tasksNumber; i++)
221 wlm.addTask(&tasks[i]);
222 std::chrono::steady_clock::time_point start_time;
223 start_time = std::chrono::steady_clock::now();
224 wlm.start(); // tasks can be added before start.
226 std::chrono::steady_clock::time_point end_time;
227 end_time = std::chrono::steady_clock::now();
228 std::chrono::seconds duration;
229 duration = std::chrono::duration_cast<std::chrono::seconds>
230 (end_time - start_time);
231 std::chrono::seconds maxExpectedDuration(22);
232 CPPUNIT_ASSERT( duration < maxExpectedDuration );
233 DEBUG_LOG("Test step duration : ", duration.count(), "s");
236 // Add 1 core tasks first
238 // WARNING: std::size_t is always >= 0
239 for(int i = tasksNumber-1; i >= 0; i--)
240 wlm.addTask(&tasks[i]);
241 start_time = std::chrono::steady_clock::now();
242 wlm.start(); // tasks can be added before start.
244 end_time = std::chrono::steady_clock::now();
245 duration = std::chrono::duration_cast<std::chrono::seconds>
246 (end_time - start_time);
247 CPPUNIT_ASSERT( duration < maxExpectedDuration );
248 DEBUG_LOG("Test step duration : ", duration.count(), "s");
251 // Add 1 core tasks first & start before addTask
253 start_time = std::chrono::steady_clock::now();
255 for(int i = tasksNumber-1; i >= 0; i--)
256 wlm.addTask(&tasks[i]);
258 end_time = std::chrono::steady_clock::now();
259 duration = std::chrono::duration_cast<std::chrono::seconds>
260 (end_time - start_time);
261 CPPUNIT_ASSERT( duration < maxExpectedDuration );
262 DEBUG_LOG("Test step duration : ", duration.count(), "s");
267 CPPUNIT_TEST_SUITE_REGISTRATION(MyTest);
269 #include "BasicMainTest.hxx"