51#ifndef DOXYGEN_SHOULD_SKIP_THIS
62 Scheduler(max_nb_threads, max_megabyte_memory) {
64 GUM_CONSTRUCTOR(SchedulerSequential);
68 SchedulerSequential::SchedulerSequential(
const SchedulerSequential& from) : Scheduler(from) {
70 GUM_CONS_CPY(SchedulerSequential);
74 SchedulerSequential::SchedulerSequential(SchedulerSequential&& from) :
75 Scheduler(
std::move(from)) {
77 GUM_CONS_MOV(SchedulerSequential);
81 SchedulerSequential* SchedulerSequential::clone()
const {
return new SchedulerSequential(*
this); }
84 SchedulerSequential::~SchedulerSequential() {
86 GUM_DESTRUCTOR(SchedulerSequential);
90 void SchedulerSequential::execute(Schedule& schedule) {
93 _setSchedule_(schedule);
98 if ((this->_max_memory != 0.0) && _memory_usage_.first > this->_max_memory) {
99 throw std::bad_alloc();
103 for (
const auto node: _operations_) {
106 auto& op =
const_cast< ScheduleOperator&
>(schedule.operation(node));
111 _operations_up_to_date_ =
false;
117 double SchedulerSequential::nbOperations(
const Schedule& schedule) {
120 for (
const auto node: schedule.dag()) {
121 nb_ops += schedule.operation(node).nbOperations();
128 bool SchedulerSequential::_cmp_(
const UnexecutedOperation& a,
const UnexecutedOperation& b) {
129 if (a.max_memory_usage < b.max_memory_usage)
return true;
130 if (b.max_memory_usage < a.max_memory_usage)
return false;
131 return (a.end_memory_usage < b.end_memory_usage);
135 std::pair< double, double > SchedulerSequential::memoryUsage(
const Schedule& schedule) {
138 _setSchedule_(schedule);
140 return _memory_usage_;
144 Size SchedulerSequential::_addExecutableOps_(
145 std::vector< UnexecutedOperation >& unexecuted_deletions,
146 std::vector< UnexecutedOperation >& unexecuted_operations,
147 bool& unexecuted_deletions_sorted,
148 bool& unexecuted_operations_sorted,
151 List< NodeId >& available_nodes)
const {
152 double added_mem = 0.0;
155 if (!unexecuted_deletions_sorted) {
156 std::sort(unexecuted_deletions.begin(), unexecuted_deletions.end(), _cmp_);
157 unexecuted_deletions_sorted =
true;
159 if (!unexecuted_operations_sorted) {
160 std::sort(unexecuted_operations.begin(), unexecuted_operations.end(), _cmp_);
161 unexecuted_operations_sorted =
true;
166 std::size_t i_del = 0;
167 for (std::size_t end = unexecuted_deletions.size(); i_del < end; ++i_del) {
168 if (memory_used + added_mem + unexecuted_deletions[i_del].max_memory_usage > max_memory)
170 added_mem += unexecuted_deletions[i_del].end_memory_usage;
175 std::size_t i_op = 0;
176 for (std::size_t end = unexecuted_operations.size(); i_op < end; ++i_op) {
177 if (memory_used + added_mem + unexecuted_operations[i_op].max_memory_usage > max_memory)
179 added_mem += unexecuted_operations[i_op].end_memory_usage;
185 for (std::size_t j = i_op - 1, i = i_op; i > 0; --j, --i)
186 available_nodes.pushFront(unexecuted_operations[j].node);
188 unexecuted_operations.erase(unexecuted_operations.begin(),
189 unexecuted_operations.begin() + i_op);
194 for (std::size_t j = i_del - 1, i = 0; i > 0; --j, --i)
195 available_nodes.pushFront(unexecuted_deletions[j].node);
197 unexecuted_deletions.erase(unexecuted_deletions.begin(),
198 unexecuted_deletions.begin() + i_del);
206 void SchedulerSequential::_simulateDAGUpdate_(DAG& dag,
208 std::vector< NodeId >& new_available_nodes)
const {
209 new_available_nodes.clear();
211 for (
const auto child_node: dag.children(node)) {
212 if (dag.parents(child_node).size() == 1) {
213 new_available_nodes.push_back(child_node);
222 void SchedulerSequential::_simulateExecuteOneOperation_(
224 ScheduleOperator& op,
226 List< NodeId >& available_nodes,
227 std::vector< NodeId >& new_available_nodes) {
229 _operations_.push_back(node);
232 _simulateDAGUpdate_(dag, node, new_available_nodes);
237 for (
const auto new_node: new_available_nodes) {
238 if (!_schedule_->operation(new_node).implyDeletion()) available_nodes.pushFront(new_node);
240 for (
const auto new_node: new_available_nodes) {
241 if (_schedule_->operation(new_node).implyDeletion()) available_nodes.pushFront(new_node);
246 void SchedulerSequential::_simulateExecution_() {
248 DAG dag = _schedule_->dag();
250 _operations_.clear();
251 _operations_.reserve(dag.sizeNodes());
254 List< NodeId > available_nodes;
255 std::vector< NodeId > new_available_nodes;
259 for (
const auto node: _schedule_->availableOperations()) {
260 if (_schedule_->operation(node).implyDeletion()) available_nodes.pushFront(node);
261 else available_nodes.pushBack(node);
265 if (available_nodes.empty()) {
266 _memory_usage_ = {0.0, 0.0};
267 _operations_up_to_date_ =
true;
275 double memory_used = 0;
276 double max_memory_used = 0;
277 double max_memory = this->_max_memory;
278 std::vector< UnexecutedOperation > unexecuted_deletions;
279 std::vector< UnexecutedOperation > unexecuted_operations;
280 bool unexecuted_deletions_sorted =
true;
281 bool unexecuted_operations_sorted =
true;
284 bool schedule_fully_performed =
false;
286 while (!available_nodes.empty()) {
288 const NodeId node = available_nodes.front();
289 available_nodes.popFront();
290 auto& op =
const_cast< ScheduleOperator&
>(_schedule_->operation(node));
294 std::pair< double, double > op_mem = op.memoryUsage();
295 if (max_memory != 0.0) {
296 if (memory_used + op_mem.first > max_memory) {
300 if (op_mem.second > 0) {
301 unexecuted_operations.push_back({op_mem.first, op_mem.second, node});
302 unexecuted_operations_sorted =
false;
304 unexecuted_deletions.push_back({op_mem.first, op_mem.second, node});
305 unexecuted_deletions_sorted =
false;
314 _simulateExecuteOneOperation_(node, op, dag, available_nodes, new_available_nodes);
315 if (memory_used + op_mem.first > max_memory_used)
316 max_memory_used = memory_used + op_mem.first;
317 memory_used += op_mem.second;
322 if (!unexecuted_operations.empty() || !unexecuted_deletions.empty()) {
323 const Size nb_new_ops =
const_cast< SchedulerSequential*
>(
this)->_addExecutableOps_(
324 unexecuted_deletions,
325 unexecuted_operations,
326 unexecuted_deletions_sorted,
327 unexecuted_operations_sorted,
332 if (nb_new_ops == 0) {
336 if (!unexecuted_deletions.empty())
337 max_memory = memory_used + unexecuted_deletions[0].max_memory_usage;
338 else if (!unexecuted_operations.empty())
339 max_memory = memory_used + unexecuted_operations[0].max_memory_usage;
341 const_cast< SchedulerSequential*
>(
this)->_addExecutableOps_(unexecuted_deletions,
342 unexecuted_operations,
343 unexecuted_deletions_sorted,
344 unexecuted_operations_sorted,
350 schedule_fully_performed =
true;
352 }
while (!schedule_fully_performed);
354 _memory_usage_ = {max_memory_used, memory_used};
355 _operations_up_to_date_ =
true;
SchedulerSequential(Size max_nb_threads=0, double max_megabyte_memory=0.0)
default constructor
The common interface of all the schedulers.
std::size_t Size
In aGrUM, hashed values are unsigned long int.
gum is the global namespace for all aGrUM entities
a scheduler that executes any available operation (chosen arbitrarily)
a scheduler that executes any available operation (chosen aribtrarily)