Salome HOME
Updated copyright comment
[modules/yacs.git] / src / workloadmanager / DefaultAlgorithm.cxx
1 // Copyright (C) 2020-2024  CEA, EDF
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 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 #include "DefaultAlgorithm.hxx"
20 #include "Task.hxx"
21 #include <stdexcept>
22 #include <limits>
23 #include <algorithm>
24
25 namespace WorkloadManager
26 {
27 void DefaultAlgorithm::addTask(Task* t)
28 {
29   // put the tasks which need more cores in front.
30   float newNeedCores = t->type().neededCores;
31   if(_waitingTasks.empty())
32     _waitingTasks.push_back(t);
33   else if(_waitingTasks.back()->type().neededCores >= newNeedCores)
34     _waitingTasks.push_back(t);
35   else
36   {
37     std::list<Task*>::iterator it = _waitingTasks.begin();
38     while(it != _waitingTasks.end() && (*it)->type().neededCores >= newNeedCores)
39       it++;
40     _waitingTasks.insert(it, t);
41   }
42 }
43
44 bool DefaultAlgorithm::empty()const
45 {
46   return _waitingTasks.empty();
47 }
48
49 void DefaultAlgorithm::addResource(const Resource& r)
50 {
51   _resources.emplace_back(r);
52 }
53
54 WorkloadAlgorithm::LaunchInfo DefaultAlgorithm::chooseTask()
55 {
56   LaunchInfo result;
57   std::list<Task*>::iterator chosenTaskIt;
58   for( std::list<Task*>::iterator itTask = _waitingTasks.begin();
59       !result.taskFound && itTask != _waitingTasks.end();
60       itTask ++)
61   {
62     const ContainerType& ctype = (*itTask)->type();
63     if(ctype.ignoreResources)
64       result.taskFound = true;
65     else
66     {
67       std::list<ResourceLoadInfo>::iterator best_resource;
68       best_resource = _resources.end();
69       float best_cost = std::numeric_limits<float>::max();
70       bool isSupported = false;
71       for(auto itResource = _resources.begin();
72           itResource != _resources.end();
73           itResource++)
74         if(itResource->isSupported(ctype)
75             && (*itTask)->isAccepted(itResource->resource()))
76         {
77           isSupported = true;
78           if(itResource->isAllocPossible(ctype))
79           {
80             float thisCost = itResource->cost(ctype);
81             if( best_cost > thisCost)
82             {
83               best_cost = thisCost;
84               best_resource = itResource;
85             }
86           }
87         }
88       if(best_resource != _resources.end())
89       {
90         result.taskFound = true;
91         result.worker.resource = best_resource->resource();
92         result.worker.index = best_resource->alloc(ctype);
93       }
94       else if(!isSupported && _resourcesFrozen)
95       {
96         // This task can never be run by any available resource.
97         result.taskFound = true;
98         result.worker.isOk = false;
99         result.worker.error_message = "No resource can run this task.";
100       }
101     }
102     if(result.taskFound)
103     {
104       chosenTaskIt = itTask;
105       result.task = (*itTask);
106       result.worker.type = ctype;
107     }
108   }
109   if(result.taskFound)
110     _waitingTasks.erase(chosenTaskIt);
111   return result;
112 }
113
114 void DefaultAlgorithm::liberate(const LaunchInfo& info)
115 {
116   const ContainerType& ctype = info.worker.type;
117   if(!ctype.ignoreResources && info.worker.isOk)
118   {
119     const Resource& r = info.worker.resource;
120     unsigned int index = info.worker.index;
121     std::list<ResourceLoadInfo>::iterator it = std::find(_resources.begin(),
122                                                         _resources.end(),
123                                                         r);
124     it->free(ctype, index); // we are sure to find it
125   }
126 }
127
128 // ResourceInfoForContainer
129
130 DefaultAlgorithm::ResourceInfoForContainer::ResourceInfoForContainer
131                                 (const Resource& r, const ContainerType& ctype)
132 : _ctype(ctype)
133 , _resource(r)
134 , _runningContainers()
135 , _firstFreeContainer(0)
136 {
137 }
138
139 unsigned int DefaultAlgorithm::ResourceInfoForContainer::maxContainers()const
140 {
141   return float(_resource.nbCores) / _ctype.neededCores;
142 }
143
144 unsigned int  DefaultAlgorithm::ResourceInfoForContainer::alloc()
145 {
146   unsigned int result = _firstFreeContainer;
147   _runningContainers.insert(result);
148   _firstFreeContainer++;
149   while(isContainerRunning(_firstFreeContainer))
150     _firstFreeContainer++;
151   return result;
152 }
153
154 void DefaultAlgorithm::ResourceInfoForContainer::free(unsigned int index)
155 {
156   _runningContainers.erase(index);
157   if(index < _firstFreeContainer)
158     _firstFreeContainer = index;
159 }
160
161 unsigned int DefaultAlgorithm::ResourceInfoForContainer::nbRunningContainers()const
162 {
163   return _runningContainers.size();
164 }
165
166 bool DefaultAlgorithm::ResourceInfoForContainer::isContainerRunning
167                                 (unsigned int index)const
168 {
169   return _runningContainers.find(index)!=_runningContainers.end();
170 }
171
172 // ResourceLoadInfo
173
174 DefaultAlgorithm::ResourceLoadInfo::ResourceLoadInfo(const Resource& r)
175 : _resource(r)
176 , _load(0.0)
177 , _loadCost(0.0)
178 , _ctypes()
179 {
180 }
181
182 bool DefaultAlgorithm::ResourceLoadInfo::isSupported
183                                 (const ContainerType& ctype)const
184 {
185   return ctype.neededCores <= _resource.nbCores ;
186 }
187
188 bool DefaultAlgorithm::ResourceLoadInfo::isAllocPossible
189                                 (const ContainerType& ctype)const
190 {
191   return ctype.neededCores + _load <= _resource.nbCores;
192 }
193
194 float DefaultAlgorithm::ResourceLoadInfo::cost
195                                 (const ContainerType& ctype)const
196 {
197   return _loadCost * 100.0 / float(_resource.nbCores);
198 }
199
200 unsigned int DefaultAlgorithm::ResourceLoadInfo::alloc
201                                 (const ContainerType& ctype)
202 {
203   std::list<ResourceInfoForContainer>::iterator it = std::find(_ctypes.begin(),
204                                                                _ctypes.end(),
205                                                                ctype);
206   // add the type if not found
207   if(it == _ctypes.end())
208   {
209     _ctypes.emplace_back(_resource, ctype);
210     it = _ctypes.end();
211     it--;
212   }
213   _load += ctype.neededCores;
214   if(ctype.neededCores == 0)
215     _loadCost += COST_FOR_0_CORE_TASKS;
216   else
217     _loadCost += ctype.neededCores;
218   return it->alloc();
219 }
220
221 void DefaultAlgorithm::ResourceLoadInfo::free
222                                 (const ContainerType& ctype, int index)
223 {
224   _load -= ctype.neededCores;
225   if(ctype.neededCores == 0)
226     _loadCost -= COST_FOR_0_CORE_TASKS;
227   else
228     _loadCost -= ctype.neededCores;
229   std::list<ResourceInfoForContainer>::iterator it = std::find(_ctypes.begin(),
230                                                                _ctypes.end(),
231                                                                ctype);
232   it->free(index);
233 }
234
235 }