Salome HOME
b3b53c9d981339ac7aedaf27d64e0d2ff3574934
[modules/yacs.git] / src / workloadmanager / Test / TestMain.cxx
1 // Copyright (C) 2020  EDF R&D
2 //
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.
7 //
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.
12 //
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
16 //
17 //
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>
26 #include <stdexcept>
27
28 #include <iostream>
29 #include <fstream>
30 #include <cstdlib>
31 #include <cppunit/extensions/HelperMacros.h>
32
33 #include <string>
34 #include <sstream>
35
36 #include <chrono>
37 #include <ctime>
38 #include <thread>
39
40 #include "../WorkloadManager.hxx"
41 #include "../DefaultAlgorithm.hxx"
42
43 constexpr bool ACTIVATE_DEBUG_LOG = true;
44 template<typename... Ts>
45 void DEBUG_LOG(Ts... args)
46 {
47   if(! ACTIVATE_DEBUG_LOG)
48     return;
49   if(sizeof...(Ts) == 0)
50     return;
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)...};
56   message << std::endl;
57   std::cerr << message.str();
58 }
59
60 class MyTask;
61 class AbstractChecker
62 {
63 public:
64   virtual void check(const WorkloadManager::Container& c, MyTask* t)=0;
65 };
66
67 template <std::size_t size_R, std::size_t size_T>
68 class Checker : public AbstractChecker
69 {
70 public:
71   Checker();
72   void check(const WorkloadManager::Container& c, MyTask* t)override;
73   void globalCheck();
74   void reset();
75
76   WorkloadManager::Resource resources[size_R];
77   WorkloadManager::ContainerType types[size_T];
78 private:
79   std::mutex _mutex;
80   int _maxContainersForResource[size_R][size_T];
81 };
82
83 class MyTask : public WorkloadManager::Task
84 {
85 public:
86   const WorkloadManager::ContainerType* type()const override {return _type;}
87   void run(const WorkloadManager::Container& c)override
88   {
89     _check->check(c, this);
90
91     DEBUG_LOG("Running task ", _id, " on ", c.resource->name, "-", c.type->name,
92               "-", c.index);
93     std::this_thread::sleep_for(std::chrono::seconds(_sleep));
94     DEBUG_LOG("Finish task ", _id);
95   }
96
97   void reset(int id,
98              const WorkloadManager::ContainerType* type,
99              int sleep,
100              AbstractChecker * check
101             )
102   {
103     _id = id;
104     _type = type;
105     _sleep = sleep;
106     _check = check;
107   }
108 private:
109   int _id = 0;
110   const WorkloadManager::ContainerType* _type = nullptr;
111   int _sleep = 0;
112   AbstractChecker * _check;
113 };
114
115 template <std::size_t size_R, std::size_t size_T>
116 Checker<size_R, size_T>::Checker()
117 {
118   for(std::size_t i=0; i < size_R; i ++)
119   {
120     resources[i].id = i;
121     std::ostringstream name;
122     name << "r" << i;
123     resources[i].name = name.str();
124   }
125
126   for(std::size_t i=0; i < size_T; i ++)
127   {
128     types[i].id = i;
129     std::ostringstream name;
130     name << "t" << i;
131     types[i].name = name.str();
132   }
133
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;
137 }
138
139 template <std::size_t size_R, std::size_t size_T>
140 void Checker<size_R, size_T>::check(const WorkloadManager::Container& c,
141                                     MyTask* t)
142 {
143   std::unique_lock<std::mutex> lock(_mutex);
144   int& max = _maxContainersForResource[c.resource->id][c.type->id];
145   if( max < c.index)
146     max = c.index;
147 }
148
149 template <std::size_t size_R, std::size_t size_T>
150 void Checker<size_R, size_T>::globalCheck()
151 {
152   for(std::size_t i=0; i < size_R; i++)
153   {
154     float global_max = 0;
155     for(std::size_t j=0; j < size_T; j++)
156     {
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);
161     }
162     DEBUG_LOG(resources[i].name, " global: ", global_max);
163     CPPUNIT_ASSERT(global_max >= resources[i].nbCores); // cores fully used
164   }
165 }
166
167 template <std::size_t size_R, std::size_t size_T>
168 void Checker<size_R, size_T>::reset()
169 {
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;
173 }
174
175 class MyTest: public CppUnit::TestFixture
176 {
177   CPPUNIT_TEST_SUITE(MyTest);
178   CPPUNIT_TEST(atest);
179   CPPUNIT_TEST_SUITE_END();
180 public:
181   void atest();
182 };
183
184 void MyTest::atest()
185 {
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;
193
194   for(std::size_t i=0; i < resourcesNumber; i ++)
195     DEBUG_LOG(check.resources[i].name, " has ", check.resources[i].nbCores,
196               " cores.");
197   for(std::size_t i=0; i < typesNumber; i ++)
198     DEBUG_LOG(check.types[i].name, " needs ", check.types[i].neededCores,
199               " cores.");
200
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);
207
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);
212
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]);
217
218   // Add 4 core tasks first
219   check.reset();
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.
225   wlm.stop();
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");
234   check.globalCheck();
235
236   // Add 1 core tasks first
237   check.reset();
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.
243   wlm.stop();
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");
249   check.globalCheck();
250
251   // Add 1 core tasks first & start before addTask
252   check.reset();
253   start_time = std::chrono::steady_clock::now();
254   wlm.start();
255   for(int i = tasksNumber-1; i >= 0; i--)
256     wlm.addTask(&tasks[i]);
257   wlm.stop();
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");
263   check.globalCheck();
264
265 }
266
267 CPPUNIT_TEST_SUITE_REGISTRATION(MyTest);
268
269 #include "BasicMainTest.hxx"