Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
d36dff633044bab6fb69730d358bd0bd062ecb93
[simgrid.git] / src / surf / cpu_ti.cpp
1 /* Copyright (c) 2013-2017. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #include "cpu_ti.hpp"
8 #include "src/surf/trace_mgr.hpp"
9 #include "xbt/utility.hpp"
10 #include <algorithm>
11
12 #ifndef SURF_MODEL_CPUTI_H_
13 #define SURF_MODEL_CPUTI_H_
14
15 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(surf_cpu_ti, surf_cpu, "Logging specific to the SURF CPU TRACE INTEGRATION module");
16
17 namespace simgrid {
18 namespace surf {
19
20 /*********
21  * Trace *
22  *********/
23
24 CpuTiTrace::CpuTiTrace(tmgr_trace_t speedTrace)
25 {
26   double integral = 0;
27   double time = 0;
28   int i = 0;
29   nbPoints_ = speedTrace->event_list.size() + 1;
30   timePoints_ = new double[nbPoints_];
31   integral_ =  new double[nbPoints_];
32   for (auto const& val : speedTrace->event_list) {
33     timePoints_[i] = time;
34     integral_[i] = integral;
35     integral += val.date_ * val.value_;
36     time += val.date_;
37     i++;
38   }
39   timePoints_[i] = time;
40   integral_[i] = integral;
41 }
42
43 CpuTiTrace::~CpuTiTrace()
44 {
45   delete [] timePoints_;
46   delete [] integral_;
47 }
48
49 CpuTiTgmr::~CpuTiTgmr()
50 {
51   if (trace_)
52     delete trace_;
53 }
54
55 /**
56 * \brief Integrate trace
57 *
58 * Wrapper around surf_cpu_integrate_trace_simple() to get
59 * the cyclic effect.
60 *
61 * \param a      Begin of interval
62 * \param b      End of interval
63 * \return the integrate value. -1 if an error occurs.
64 */
65 double CpuTiTgmr::integrate(double a, double b)
66 {
67   int a_index;
68
69   if ((a < 0.0) || (a > b)) {
70     xbt_die("Error, invalid integration interval [%.2f,%.2f]. "
71         "You probably have a task executing with negative computation amount. Check your code.", a, b);
72   }
73   if (fabs(a -b) < EPSILON)
74     return 0.0;
75
76   if (type_ == TRACE_FIXED) {
77     return ((b - a) * value_);
78   }
79
80   if (fabs(ceil(a / lastTime_) - a / lastTime_) < EPSILON)
81     a_index = 1 + static_cast<int>(ceil(a / lastTime_));
82   else
83     a_index = static_cast<int> (ceil(a / lastTime_));
84
85   int b_index = static_cast<int> (floor(b / lastTime_));
86
87   if (a_index > b_index) {      /* Same chunk */
88     return trace_->integrateSimple(a - (a_index - 1) * lastTime_, b - (b_index) * lastTime_);
89   }
90
91   double first_chunk = trace_->integrateSimple(a - (a_index - 1) * lastTime_, lastTime_);
92   double middle_chunk = (b_index - a_index) * total_;
93   double last_chunk = trace_->integrateSimple(0.0, b - (b_index) * lastTime_);
94
95   XBT_DEBUG("first_chunk=%.2f  middle_chunk=%.2f  last_chunk=%.2f\n", first_chunk, middle_chunk, last_chunk);
96
97   return (first_chunk + middle_chunk + last_chunk);
98 }
99
100 /**
101  * \brief Auxiliary function to compute the integral between a and b.
102  *     It simply computes the integrals at point a and b and returns the difference between them.
103  * \param a  Initial point
104  * \param b  Final point
105 */
106 double CpuTiTrace::integrateSimple(double a, double b)
107 {
108   return integrateSimplePoint(b) - integrateSimplePoint(a);
109 }
110
111 /**
112  * \brief Auxiliary function to compute the integral at point a.
113  * \param a        point
114  */
115 double CpuTiTrace::integrateSimplePoint(double a)
116 {
117   double integral = 0;
118   double a_aux = a;
119   int ind = binarySearch(timePoints_, a, 0, nbPoints_ - 1);
120   integral += integral_[ind];
121
122   XBT_DEBUG("a %f ind %d integral %f ind + 1 %f ind %f time +1 %f time %f",
123        a, ind, integral, integral_[ind + 1], integral_[ind], timePoints_[ind + 1], timePoints_[ind]);
124   double_update(&a_aux, timePoints_[ind], sg_maxmin_precision*sg_surf_precision);
125   if (a_aux > 0)
126     integral += ((integral_[ind + 1] - integral_[ind]) / (timePoints_[ind + 1] - timePoints_[ind])) *
127                 (a - timePoints_[ind]);
128   XBT_DEBUG("Integral a %f = %f", a, integral);
129
130   return integral;
131 }
132
133 /**
134 * \brief Computes the time needed to execute "amount" on cpu.
135 *
136 * Here, amount can span multiple trace periods
137 *
138 * \param a        Initial time
139 * \param amount  Amount to be executed
140 * \return  End time
141 */
142 double CpuTiTgmr::solve(double a, double amount)
143 {
144   /* Fix very small negative numbers */
145   if ((a < 0.0) && (a > -EPSILON)) {
146     a = 0.0;
147   }
148   if ((amount < 0.0) && (amount > -EPSILON)) {
149     amount = 0.0;
150   }
151
152   /* Sanity checks */
153   if ((a < 0.0) || (amount < 0.0)) {
154     XBT_CRITICAL ("Error, invalid parameters [a = %.2f, amount = %.2f]. "
155         "You probably have a task executing with negative computation amount. Check your code.", a, amount);
156     xbt_abort();
157   }
158
159   /* At this point, a and amount are positive */
160   if (amount < EPSILON)
161     return a;
162
163   /* Is the trace fixed ? */
164   if (type_ == TRACE_FIXED) {
165     return (a + (amount / value_));
166   }
167
168   XBT_DEBUG("amount %f total %f", amount, total_);
169   /* Reduce the problem to one where amount <= trace_total */
170   int quotient = static_cast<int>(floor(amount / total_));
171   double reduced_amount = (total_) * ((amount / total_) - floor(amount / total_));
172   double reduced_a = a - (lastTime_) * static_cast<int>(floor(a / lastTime_));
173
174   XBT_DEBUG("Quotient: %d reduced_amount: %f reduced_a: %f", quotient, reduced_amount, reduced_a);
175
176   /* Now solve for new_amount which is <= trace_total */
177   double reduced_b = solveSomewhatSimple(reduced_a, reduced_amount);
178
179 /* Re-map to the original b and amount */
180   double b = (lastTime_) * static_cast<int>(floor(a / lastTime_)) + (quotient * lastTime_) + reduced_b;
181   return b;
182 }
183
184 /**
185 * \brief Auxiliary function to solve integral
186 *
187 * Here, amount is <= trace->total
188 * and a <=trace->last_time
189 *
190 */
191 double CpuTiTgmr::solveSomewhatSimple(double a, double amount)
192 {
193   double b;
194
195   XBT_DEBUG("Solve integral: [%.2f, amount=%.2f]", a, amount);
196   double amount_till_end = integrate(a, lastTime_);
197
198   if (amount_till_end > amount) {
199     b = trace_->solveSimple(a, amount);
200   } else {
201     b = lastTime_ + trace_->solveSimple(0.0, amount - amount_till_end);
202   }
203   return b;
204 }
205
206 /**
207  * \brief Auxiliary function to solve integral.
208  *  It returns the date when the requested amount of flops is available
209  * \param a        Initial point
210  * \param amount  Amount of flops
211  * \return The date when amount is available.
212 */
213 double CpuTiTrace::solveSimple(double a, double amount)
214 {
215   double integral_a = integrateSimplePoint(a);
216   int ind = binarySearch(integral_, integral_a + amount, 0, nbPoints_ - 1);
217   double time = timePoints_[ind];
218   time += (integral_a + amount - integral_[ind]) /
219            ((integral_[ind + 1] - integral_[ind]) / (timePoints_[ind + 1] - timePoints_[ind]));
220
221   return time;
222 }
223
224 /**
225 * \brief Auxiliary function to update the CPU speed scale.
226 *
227 *  This function uses the trace structure to return the speed scale at the determined time a.
228 * \param a        Time
229 * \return CPU speed scale
230 */
231 double CpuTiTgmr::getPowerScale(double a)
232 {
233   double reduced_a = a - floor(a / lastTime_) * lastTime_;
234   int point = trace_->binarySearch(trace_->timePoints_, reduced_a, 0, trace_->nbPoints_ - 1);
235   trace_mgr::DatedValue val = speedTrace_->event_list.at(point);
236   return val.value_;
237 }
238
239 /**
240 * \brief Creates a new integration trace from a tmgr_trace_t
241 *
242 * \param  speedTrace    CPU availability trace
243 * \param  value          Percentage of CPU speed available (useful to fixed tracing)
244 * \return  Integration trace structure
245 */
246 CpuTiTgmr::CpuTiTgmr(tmgr_trace_t speedTrace, double value) :
247     speedTrace_(speedTrace)
248 {
249   double total_time = 0.0;
250   trace_ = 0;
251
252 /* no availability file, fixed trace */
253   if (not speedTrace) {
254     type_ = TRACE_FIXED;
255     value_ = value;
256     XBT_DEBUG("No availability trace. Constant value = %f", value);
257     return;
258   }
259
260   /* only one point available, fixed trace */
261   if (speedTrace->event_list.size() == 1) {
262     trace_mgr::DatedValue val = speedTrace->event_list.front();
263     type_ = TRACE_FIXED;
264     value_                    = val.value_;
265     return;
266   }
267
268   type_ = TRACE_DYNAMIC;
269
270   /* count the total time of trace file */
271   for (auto const& val : speedTrace->event_list)
272     total_time += val.date_;
273
274   trace_ = new CpuTiTrace(speedTrace);
275   lastTime_ = total_time;
276   total_ = trace_->integrateSimple(0, total_time);
277
278   XBT_DEBUG("Total integral %f, last_time %f ", total_, lastTime_);
279 }
280
281 /**
282  * \brief Binary search in array.
283  *  It returns the first point of the interval in which "a" is.
284  * \param array    Array
285  * \param a        Value to search
286  * \param low     Low bound to search in array
287  * \param high    Upper bound to search in array
288  * \return Index of point
289 */
290 int CpuTiTrace::binarySearch(double *array, double a, int low, int high)
291 {
292   xbt_assert(low < high, "Wrong parameters: low (%d) should be smaller than high (%d)", low, high);
293
294   do {
295     int mid = low + (high - low) / 2;
296     XBT_DEBUG("a %f low %d high %d mid %d value %f", a, low, high, mid, array[mid]);
297
298     if (array[mid] > a)
299       high = mid;
300     else
301       low = mid;
302   }
303   while (low < high - 1);
304
305   return low;
306 }
307
308 }
309 }
310
311 /*********
312  * Model *
313  *********/
314
315 void surf_cpu_model_init_ti()
316 {
317   xbt_assert(not surf_cpu_model_pm, "CPU model already initialized. This should not happen.");
318   xbt_assert(not surf_cpu_model_vm, "CPU model already initialized. This should not happen.");
319
320   surf_cpu_model_pm = new simgrid::surf::CpuTiModel();
321   all_existing_models->push_back(surf_cpu_model_pm);
322
323   surf_cpu_model_vm = new simgrid::surf::CpuTiModel();
324   all_existing_models->push_back(surf_cpu_model_vm);
325 }
326
327 namespace simgrid {
328 namespace surf {
329
330 CpuTiModel::CpuTiModel() : CpuModel()
331 {
332   runningActionSetThatDoesNotNeedBeingChecked_ = new ActionList();
333
334   modifiedCpu_ = new CpuTiList();
335 }
336
337 CpuTiModel::~CpuTiModel()
338 {
339   surf_cpu_model_pm = nullptr;
340   delete runningActionSetThatDoesNotNeedBeingChecked_;
341   delete modifiedCpu_;
342 }
343
344 Cpu *CpuTiModel::createCpu(simgrid::s4u::Host *host, std::vector<double>* speedPerPstate, int core)
345 {
346   return new CpuTi(this, host, speedPerPstate, core);
347 }
348
349 double CpuTiModel::nextOccuringEvent(double now)
350 {
351   double min_action_duration = -1;
352
353   /* iterates over modified cpus to update share resources */
354   for (auto it = std::begin(*modifiedCpu_); it != std::end(*modifiedCpu_);) {
355     CpuTi& ti = *it;
356     ++it; // increment iterator here since the following call to ti.updateActionsFinishTime() may invalidate it
357     ti.updateActionsFinishTime(now);
358   }
359
360   /* get the min next event if heap not empty */
361   if (not actionHeapIsEmpty())
362     min_action_duration = actionHeapTopDate() - now;
363
364   XBT_DEBUG("Share resources, min next event date: %f", min_action_duration);
365
366   return min_action_duration;
367 }
368
369 void CpuTiModel::updateActionsState(double now, double /*delta*/)
370 {
371   while (not actionHeapIsEmpty() && actionHeapTopDate() <= now) {
372     CpuTiAction* action = static_cast<CpuTiAction*>(actionHeapPop());
373     XBT_DEBUG("Action %p: finish", action);
374     action->finish(Action::State::done);
375     /* set the remains to 0 due to precision problems when updating the remaining amount */
376     action->setRemains(0);
377     /* update remaining amount of all actions */
378     action->cpu_->updateRemainingAmount(surf_get_clock());
379   }
380 }
381
382 /************
383  * Resource *
384  ************/
385 CpuTi::CpuTi(CpuTiModel *model, simgrid::s4u::Host *host, std::vector<double> *speedPerPstate, int core)
386   : Cpu(model, host, speedPerPstate, core)
387 {
388   xbt_assert(core==1,"Multi-core not handled by this model yet");
389   coresAmount_ = core;
390
391   actionSet_ = new ActionTiList();
392
393   speed_.peak = speedPerPstate->front();
394   XBT_DEBUG("CPU create: peak=%f", speed_.peak);
395
396   speedIntegratedTrace_ = new CpuTiTgmr(nullptr, 1/*scale*/);
397 }
398
399 CpuTi::~CpuTi()
400 {
401   modified(false);
402   delete speedIntegratedTrace_;
403   delete actionSet_;
404 }
405 void CpuTi::setSpeedTrace(tmgr_trace_t trace)
406 {
407   if (speedIntegratedTrace_)
408     delete speedIntegratedTrace_;
409
410   speedIntegratedTrace_ = new CpuTiTgmr(trace, speed_.scale);
411
412   /* add a fake trace event if periodicity == 0 */
413   if (trace && trace->event_list.size() > 1) {
414     trace_mgr::DatedValue val = trace->event_list.back();
415     if (val.date_ < 1e-12)
416       speed_.event = future_evt_set->add_trace(new simgrid::trace_mgr::trace(), this);
417   }
418 }
419
420 void CpuTi::apply_event(tmgr_trace_event_t event, double value)
421 {
422   if (event == speed_.event) {
423     tmgr_trace_t speedTrace;
424     CpuTiTgmr *trace;
425
426     XBT_DEBUG("Finish trace date: value %f", value);
427     /* update remaining of actions and put in modified cpu list */
428     updateRemainingAmount(surf_get_clock());
429
430     modified(true);
431
432     speedTrace = speedIntegratedTrace_->speedTrace_;
433     trace_mgr::DatedValue val = speedTrace->event_list.back();
434     delete speedIntegratedTrace_;
435     speed_.scale = val.value_;
436
437     trace = new CpuTiTgmr(TRACE_FIXED, val.value_);
438     XBT_DEBUG("value %f", val.value_);
439
440     speedIntegratedTrace_ = trace;
441
442     tmgr_trace_event_unref(&speed_.event);
443
444   } else if (event == stateEvent_) {
445     if (value > 0) {
446       if(isOff())
447         host_that_restart.push_back(getHost());
448       turnOn();
449     } else {
450       turnOff();
451       double date = surf_get_clock();
452
453       /* put all action running on cpu to failed */
454       for (CpuTiAction& action : *actionSet_) {
455         if (action.getState() == Action::State::running || action.getState() == Action::State::ready ||
456             action.getState() == Action::State::not_in_the_system) {
457           action.setFinishTime(date);
458           action.setState(Action::State::failed);
459           action.heapRemove(model()->getActionHeap());
460         }
461       }
462     }
463     tmgr_trace_event_unref(&stateEvent_);
464
465   } else {
466     xbt_die("Unknown event!\n");
467   }
468 }
469
470 void CpuTi::updateActionsFinishTime(double now)
471 {
472   double sum_priority = 0.0;
473   double total_area;
474
475   /* update remaining amount of actions */
476   updateRemainingAmount(now);
477
478   for (CpuTiAction const& action : *actionSet_) {
479     /* action not running, skip it */
480     if (action.getStateSet() != surf_cpu_model_pm->getRunningActionSet())
481       continue;
482
483     /* bogus priority, skip it */
484     if (action.getPriority() <= 0)
485       continue;
486
487     /* action suspended, skip it */
488     if (action.suspended_ != 0)
489       continue;
490
491     sum_priority += 1.0 / action.getPriority();
492   }
493   sumPriority_ = sum_priority;
494
495   for (CpuTiAction& action : *actionSet_) {
496     double min_finish = -1;
497     /* action not running, skip it */
498     if (action.getStateSet() != surf_cpu_model_pm->getRunningActionSet())
499       continue;
500
501     /* verify if the action is really running on cpu */
502     if (action.suspended_ == 0 && action.getPriority() > 0) {
503       /* total area needed to finish the action. Used in trace integration */
504       total_area = (action.getRemains()) * sum_priority * action.getPriority();
505
506       total_area /= speed_.peak;
507
508       action.setFinishTime(speedIntegratedTrace_->solve(now, total_area));
509       /* verify which event will happen before (max_duration or finish time) */
510       if (action.getMaxDuration() > NO_MAX_DURATION &&
511           action.getStartTime() + action.getMaxDuration() < action.getFinishTime())
512         min_finish = action.getStartTime() + action.getMaxDuration();
513       else
514         min_finish = action.getFinishTime();
515     } else {
516       /* put the max duration time on heap */
517       if (action.getMaxDuration() > NO_MAX_DURATION)
518         min_finish = action.getStartTime() + action.getMaxDuration();
519     }
520     /* add in action heap */
521     if (min_finish > NO_MAX_DURATION)
522       action.heapUpdate(model()->getActionHeap(), min_finish, NOTSET);
523     else
524       action.heapRemove(model()->getActionHeap());
525
526     XBT_DEBUG("Update finish time: Cpu(%s) Action: %p, Start Time: %f Finish Time: %f Max duration %f", getCname(),
527               &action, action.getStartTime(), action.getFinishTime(), action.getMaxDuration());
528   }
529   /* remove from modified cpu */
530   modified(false);
531 }
532
533 bool CpuTi::isUsed()
534 {
535   return not actionSet_->empty();
536 }
537
538 double CpuTi::getAvailableSpeed()
539 {
540   speed_.scale = speedIntegratedTrace_->getPowerScale(surf_get_clock());
541   return Cpu::getAvailableSpeed();
542 }
543
544 /** @brief Update the remaining amount of actions */
545 void CpuTi::updateRemainingAmount(double now)
546 {
547
548   /* already updated */
549   if (lastUpdate_ >= now)
550     return;
551
552   /* compute the integration area */
553   double area_total = speedIntegratedTrace_->integrate(lastUpdate_, now) * speed_.peak;
554   XBT_DEBUG("Flops total: %f, Last update %f", area_total, lastUpdate_);
555   for (CpuTiAction& action : *actionSet_) {
556     /* action not running, skip it */
557     if (action.getStateSet() != model()->getRunningActionSet())
558       continue;
559
560     /* bogus priority, skip it */
561     if (action.getPriority() <= 0)
562       continue;
563
564     /* action suspended, skip it */
565     if (action.suspended_ != 0)
566       continue;
567
568     /* action don't need update */
569     if (action.getStartTime() >= now)
570       continue;
571
572     /* skip action that are finishing now */
573     if (action.getFinishTime() >= 0 && action.getFinishTime() <= now)
574       continue;
575
576     /* update remaining */
577     action.updateRemains(area_total / (sumPriority_ * action.getPriority()));
578     XBT_DEBUG("Update remaining action(%p) remaining %f", &action, action.getRemainsNoUpdate());
579   }
580   lastUpdate_ = now;
581 }
582
583 CpuAction *CpuTi::execution_start(double size)
584 {
585   XBT_IN("(%s,%g)", getCname(), size);
586   CpuTiAction* action = new CpuTiAction(static_cast<CpuTiModel*>(model()), size, isOff(), this);
587
588   actionSet_->push_back(*action);
589
590   XBT_OUT();
591   return action;
592 }
593
594
595 CpuAction *CpuTi::sleep(double duration)
596 {
597   if (duration > 0)
598     duration = std::max(duration, sg_surf_precision);
599
600   XBT_IN("(%s,%g)", getCname(), duration);
601   CpuTiAction* action = new CpuTiAction(static_cast<CpuTiModel*>(model()), 1.0, isOff(), this);
602
603   action->setMaxDuration(duration);
604   action->suspended_ = 2;
605   if (duration == NO_MAX_DURATION) {
606     /* Move to the *end* of the corresponding action set. This convention is used to speed up update_resource_state */
607     simgrid::xbt::intrusive_erase(*action->getStateSet(), *action);
608     action->stateSet_ = static_cast<CpuTiModel*>(model())->runningActionSetThatDoesNotNeedBeingChecked_;
609     action->getStateSet()->push_back(*action);
610   }
611
612   actionSet_->push_back(*action);
613
614   XBT_OUT();
615   return action;
616 }
617
618 void CpuTi::modified(bool modified){
619   CpuTiList* modifiedCpu = static_cast<CpuTiModel*>(model())->modifiedCpu_;
620   if (modified) {
621     if (not cpu_ti_hook.is_linked()) {
622       modifiedCpu->push_back(*this);
623     }
624   } else {
625     if (cpu_ti_hook.is_linked())
626       simgrid::xbt::intrusive_erase(*modifiedCpu, *this);
627   }
628 }
629
630 /**********
631  * Action *
632  **********/
633
634 CpuTiAction::CpuTiAction(CpuTiModel *model_, double cost, bool failed, CpuTi *cpu)
635  : CpuAction(model_, cost, failed)
636  , cpu_(cpu)
637 {
638   cpu_->modified(true);
639 }
640
641 void CpuTiAction::setState(Action::State state)
642 {
643   CpuAction::setState(state);
644   cpu_->modified(true);
645 }
646
647 int CpuTiAction::unref()
648 {
649   refcount_--;
650   if (not refcount_) {
651     if (action_hook.is_linked())
652       simgrid::xbt::intrusive_erase(*getStateSet(), *this);
653     /* remove from action_set */
654     if (action_ti_hook.is_linked())
655       simgrid::xbt::intrusive_erase(*cpu_->actionSet_, *this);
656     /* remove from heap */
657     heapRemove(getModel()->getActionHeap());
658     cpu_->modified(true);
659     delete this;
660     return 1;
661   }
662   return 0;
663 }
664
665 void CpuTiAction::cancel()
666 {
667   this->setState(Action::State::failed);
668   heapRemove(getModel()->getActionHeap());
669   cpu_->modified(true);
670 }
671
672 void CpuTiAction::suspend()
673 {
674   XBT_IN("(%p)", this);
675   if (suspended_ != 2) {
676     suspended_ = 1;
677     heapRemove(getModel()->getActionHeap());
678     cpu_->modified(true);
679   }
680   XBT_OUT();
681 }
682
683 void CpuTiAction::resume()
684 {
685   XBT_IN("(%p)", this);
686   if (suspended_ != 2) {
687     suspended_ = 0;
688     cpu_->modified(true);
689   }
690   XBT_OUT();
691 }
692
693 void CpuTiAction::setMaxDuration(double duration)
694 {
695   double min_finish;
696
697   XBT_IN("(%p,%g)", this, duration);
698
699   Action::setMaxDuration(duration);
700
701   if (duration >= 0)
702     min_finish = (getStartTime() + getMaxDuration()) < getFinishTime() ?
703                  (getStartTime() + getMaxDuration()) : getFinishTime();
704   else
705     min_finish = getFinishTime();
706
707   /* add in action heap */
708   heapUpdate(getModel()->getActionHeap(), min_finish, NOTSET);
709
710   XBT_OUT();
711 }
712
713 void CpuTiAction::setSharingWeight(double priority)
714 {
715   XBT_IN("(%p,%g)", this, priority);
716   setSharingWeightNoUpdate(priority);
717   cpu_->modified(true);
718   XBT_OUT();
719 }
720
721 double CpuTiAction::getRemains()
722 {
723   XBT_IN("(%p)", this);
724   cpu_->updateRemainingAmount(surf_get_clock());
725   XBT_OUT();
726   return getRemainsNoUpdate();
727 }
728
729 }
730 }
731
732 #endif /* SURF_MODEL_CPUTI_H_ */