1 /* Copyright (c) 2006-2016. The SimGrid Team.
2 * All rights reserved. */
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. */
7 #include "src/surf/surf_interface.hpp"
8 #include "src/simdag/simdag_private.h"
9 #include "simgrid/simdag.h"
10 #include "xbt/sysdep.h"
11 #include "xbt/dynar.h"
12 #include "src/instr/instr_private.h"
14 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(sd_task, sd,
15 "Logging specific to SimDag (task)");
17 static void __SD_task_remove_dependencies(SD_task_t task);
18 static void __SD_task_destroy_scheduling_data(SD_task_t task);
20 void* SD_task_new_f(void)
22 SD_task_t task = xbt_new0(s_SD_task_t, 1);
23 task->tasks_before = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
24 task->tasks_after = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
29 void SD_task_recycle_f(void *t)
31 SD_task_t task = (SD_task_t) t;
33 /* Reset the content */
34 task->kind = SD_TASK_NOT_TYPED;
35 task->state_hookup.prev = NULL;
36 task->state_hookup.next = NULL;
37 task->state_set = sd_global->not_scheduled_task_set;
38 xbt_swag_insert(task, task->state_set);
39 task->state = SD_NOT_SCHEDULED;
40 task->return_hookup.prev = NULL;
41 task->return_hookup.next = NULL;
45 task->start_time = -1.0;
46 task->finish_time = -1.0;
47 task->surf_action = NULL;
48 task->watch_points = 0;
51 xbt_dynar_reset(task->tasks_before);
52 xbt_dynar_reset(task->tasks_after);
53 task->unsatisfied_dependencies = 0;
54 task->is_not_ready = 0;
56 /* scheduling parameters */
57 task->workstation_nb = 0;
58 task->workstation_list = NULL;
59 task->flops_amount = NULL;
60 task->bytes_amount = NULL;
64 void SD_task_free_f(void *t)
66 SD_task_t task = (SD_task_t)t;
68 xbt_dynar_free(&task->tasks_before);
69 xbt_dynar_free(&task->tasks_after);
74 * \brief Creates a new task.
76 * \param name the name of the task (can be \c NULL)
77 * \param data the user data you want to associate with the task (can be \c NULL)
78 * \param amount amount of the task
79 * \return the new task
80 * \see SD_task_destroy()
82 SD_task_t SD_task_create(const char *name, void *data, double amount)
84 SD_task_t task = (SD_task_t)xbt_mallocator_get(sd_global->task_mallocator);
86 /* general information */
87 task->data = data; /* user data */
88 task->name = xbt_strdup(name);
89 task->amount = amount;
90 task->remains = amount;
92 sd_global->task_number++;
94 TRACE_sd_task_create(task);
99 static XBT_INLINE SD_task_t SD_task_create_sized(const char *name,
100 void *data, double amount,
103 SD_task_t task = SD_task_create(name, data, amount);
104 task->bytes_amount = xbt_new0(double, ws_count * ws_count);
105 task->flops_amount = xbt_new0(double, ws_count);
106 task->workstation_nb = ws_count;
107 task->workstation_list = xbt_new0(SD_workstation_t, ws_count);
111 /** @brief create a end-to-end communication task that can then be auto-scheduled
113 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
114 * allows to specify the task costs at creation, and decouple them from the
115 * scheduling process where you just specify which resource should deliver the
118 * A end-to-end communication must be scheduled on 2 hosts, and the amount
119 * specified at creation is sent from hosts[0] to hosts[1].
121 SD_task_t SD_task_create_comm_e2e(const char *name, void *data,
124 SD_task_t res = SD_task_create_sized(name, data, amount, 2);
125 res->bytes_amount[2] = amount;
126 res->kind = SD_TASK_COMM_E2E;
128 TRACE_category("COMM_E2E");
129 TRACE_sd_set_task_category(res, "COMM_E2E");
134 /** @brief create a sequential computation task that can then be auto-scheduled
136 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
137 * allows to specify the task costs at creation, and decouple them from the
138 * scheduling process where you just specify which resource should deliver the
141 * A sequential computation must be scheduled on 1 host, and the amount
142 * specified at creation to be run on hosts[0].
144 * \param name the name of the task (can be \c NULL)
145 * \param data the user data you want to associate with the task (can be \c NULL)
146 * \param flops_amount amount of compute work to be done by the task
147 * \return the new SD_TASK_COMP_SEQ typed task
149 SD_task_t SD_task_create_comp_seq(const char *name, void *data,
152 SD_task_t res = SD_task_create_sized(name, data, flops_amount, 1);
153 res->flops_amount[0] = flops_amount;
154 res->kind = SD_TASK_COMP_SEQ;
156 TRACE_category("COMP_SEQ");
157 TRACE_sd_set_task_category(res, "COMP_SEQ");
162 /** @brief create a parallel computation task that can then be auto-scheduled
164 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
165 * allows to specify the task costs at creation, and decouple them from the
166 * scheduling process where you just specify which resource should deliver the
169 * A parallel computation can be scheduled on any number of host.
170 * The underlying speedup model is Amdahl's law.
171 * To be auto-scheduled, \see SD_task_distribute_comp_amdahl has to be called
173 * \param name the name of the task (can be \c NULL)
174 * \param data the user data you want to associate with the task (can be \c NULL)
175 * \param flops_amount amount of compute work to be done by the task
176 * \param alpha purely serial fraction of the work to be done (in [0.;1.[)
177 * \return the new task
179 SD_task_t SD_task_create_comp_par_amdahl(const char *name, void *data,
180 double flops_amount, double alpha)
182 xbt_assert(alpha < 1. && alpha >= 0.,
183 "Invalid parameter: alpha must be in [0.;1.[");
185 SD_task_t res = SD_task_create(name, data, flops_amount);
187 res->kind = SD_TASK_COMP_PAR_AMDAHL;
189 TRACE_category("COMP_PAR_AMDAHL");
190 TRACE_sd_set_task_category(res, "COMP_PAR_AMDAHL");
195 /** @brief create a complex data redistribution task that can then be
198 * Auto-scheduling mean that the task can be used with SD_task_schedulev().
199 * This allows to specify the task costs at creation, and decouple them from
200 * the scheduling process where you just specify which resource should
203 * A data redistribution can be scheduled on any number of host.
204 * The assumed distribution is a 1D block distribution. Each host owns the same
205 * share of the \see amount.
206 * To be auto-scheduled, \see SD_task_distribute_comm_mxn_1d_block has to be
208 * \param name the name of the task (can be \c NULL)
209 * \param data the user data you want to associate with the task (can be
211 * \param amount amount of data to redistribute by the task
212 * \return the new task
214 SD_task_t SD_task_create_comm_par_mxn_1d_block(const char *name, void *data,
217 SD_task_t res = SD_task_create(name, data, amount);
218 res->workstation_list=NULL;
219 res->kind = SD_TASK_COMM_PAR_MXN_1D_BLOCK;
221 TRACE_category("COMM_PAR_MXN_1D_BLOCK");
222 TRACE_sd_set_task_category(res, "COMM_PAR_MXN_1D_BLOCK");
228 * \brief Destroys a task.
230 * The user data (if any) should have been destroyed first.
232 * \param task the task you want to destroy
233 * \see SD_task_create()
235 void SD_task_destroy(SD_task_t task)
237 XBT_DEBUG("Destroying task %s...", SD_task_get_name(task));
239 __SD_task_remove_dependencies(task);
240 /* if the task was scheduled or runnable we have to free the scheduling parameters */
241 if (__SD_task_is_scheduled_or_runnable(task))
242 __SD_task_destroy_scheduling_data(task);
243 if (task->state_set != NULL) /* would be null if just created */
244 xbt_swag_remove(task, task->state_set);
246 xbt_swag_remove(task, sd_global->return_set);
248 xbt_free(task->name);
250 if (task->surf_action != NULL)
251 task->surf_action->unref();
253 xbt_free(task->workstation_list);
254 xbt_free(task->bytes_amount);
255 xbt_free(task->flops_amount);
257 TRACE_sd_task_destroy(task);
259 xbt_mallocator_release(sd_global->task_mallocator,task);
260 sd_global->task_number--;
262 XBT_DEBUG("Task destroyed.");
266 * \brief Returns the user data of a task
269 * \return the user data associated with this task (can be \c NULL)
270 * \see SD_task_set_data()
272 void *SD_task_get_data(SD_task_t task)
278 * \brief Sets the user data of a task
280 * The new data can be \c NULL. The old data should have been freed first
281 * if it was not \c NULL.
284 * \param data the new data you want to associate with this task
285 * \see SD_task_get_data()
287 void SD_task_set_data(SD_task_t task, void *data)
293 * \brief Sets the rate of a task
295 * This will change the network bandwidth a task can use. This rate
296 * cannot be dynamically changed. Once the task has started, this call
297 * is ineffective. This rate depends on both the nominal bandwidth on
298 * the route onto which the task is scheduled (\see
299 * SD_task_get_current_bandwidth) and the amount of data to transfer.
301 * To divide the nominal bandwidth by 2, the rate then has to be :
302 * rate = bandwidth/(2*amount)
304 * \param task a \see SD_TASK_COMM_E2E task (end-to-end communication)
305 * \param rate the new rate you want to associate with this task.
307 void SD_task_set_rate(SD_task_t task, double rate)
309 xbt_assert(task->kind == SD_TASK_COMM_E2E,
310 "The rate can be modified for end-to-end communications only.");
311 if(task->start_time<0) {
314 XBT_WARN("Task %p has started. Changing rate is ineffective.", task);
319 * \brief Returns the state of a task
322 * \return the current \ref e_SD_task_state_t "state" of this task:
323 * #SD_NOT_SCHEDULED, #SD_SCHEDULED, #SD_RUNNABLE, #SD_RUNNING, #SD_DONE or #SD_FAILED
324 * \see e_SD_task_state_t
326 e_SD_task_state_t SD_task_get_state(SD_task_t task)
331 /* Changes the state of a task. Updates the swags and the flag sd_global->watch_point_reached.
333 void __SD_task_set_state(SD_task_t task, e_SD_task_state_t new_state)
335 xbt_swag_remove(task, task->state_set);
337 case SD_NOT_SCHEDULED:
338 task->state_set = sd_global->not_scheduled_task_set;
341 task->state_set = sd_global->schedulable_task_set;
344 task->state_set = sd_global->scheduled_task_set;
347 task->state_set = sd_global->runnable_task_set;
350 task->state_set = sd_global->in_fifo_task_set;
353 task->state_set = sd_global->running_task_set;
354 task->start_time = task->surf_action->getStartTime();
357 task->state_set = sd_global->done_task_set;
358 task->finish_time = task->surf_action->getFinishTime();
361 jedule_log_sd_event(task);
365 task->state_set = sd_global->failed_task_set;
368 xbt_die( "Invalid state");
370 xbt_swag_insert(task, task->state_set);
371 task->state = new_state;
373 if (task->watch_points & new_state) {
374 XBT_VERB("Watch point reached with task '%s'!", SD_task_get_name(task));
375 sd_global->watch_point_reached = 1;
376 SD_task_unwatch(task, new_state); /* remove the watch point */
381 * \brief Returns the name of a task
384 * \return the name of this task (can be \c NULL)
386 const char *SD_task_get_name(SD_task_t task)
391 /** @brief Allows to change the name of a task */
392 void SD_task_set_name(SD_task_t task, const char *name)
394 xbt_free(task->name);
395 task->name = xbt_strdup(name);
398 /** @brief Returns the dynar of the parents of a task
401 * \return a newly allocated dynar comprising the parents of this task
404 xbt_dynar_t SD_task_get_parents(SD_task_t task)
410 parents = xbt_dynar_new(sizeof(SD_task_t), NULL);
411 xbt_dynar_foreach(task->tasks_before, i, dep) {
412 xbt_dynar_push(parents, &(dep->src));
417 /** @brief Returns the dynar of the parents of a task
420 * \return a newly allocated dynar comprising the parents of this task
422 xbt_dynar_t SD_task_get_children(SD_task_t task)
425 xbt_dynar_t children;
428 children = xbt_dynar_new(sizeof(SD_task_t), NULL);
429 xbt_dynar_foreach(task->tasks_after, i, dep) {
430 xbt_dynar_push(children, &(dep->dst));
436 * \brief Returns the amount of workstations involved in a task
438 * Only call this on already scheduled tasks!
441 int SD_task_get_workstation_count(SD_task_t task)
443 return task->workstation_nb;
447 * \brief Returns the list of workstations involved in a task
449 * Only call this on already scheduled tasks!
452 SD_workstation_t *SD_task_get_workstation_list(SD_task_t task)
454 return task->workstation_list;
458 * \brief Returns the total amount of work contained in a task
461 * \return the total amount of work (computation or data transfer) for this task
462 * \see SD_task_get_remaining_amount()
464 double SD_task_get_amount(SD_task_t task)
470 * \brief Sets the total amount of work of a task
471 * For sequential typed tasks (COMP_SEQ and COMM_E2E), it also sets the
472 * appropriate values in the flops_amount and bytes_amount arrays
473 * respectively. Nothing more than modifying task->amount is done for paralle
474 * typed tasks (COMP_PAR_AMDAHL and COMM_PAR_MXN_1D_BLOCK) as the distribution
475 * of the amount of work is done at scheduling time.
478 * \param amount the new amount of work to execute
480 void SD_task_set_amount(SD_task_t task, double amount)
482 task->amount = amount;
483 if (task->kind == SD_TASK_COMP_SEQ)
484 task->flops_amount[0] = amount;
485 if (task->kind == SD_TASK_COMM_E2E)
486 task->bytes_amount[2] = amount;
490 * \brief Returns the alpha parameter of a SD_TASK_COMP_PAR_AMDAHL task
492 * \param task a parallel task assuming Amdahl's law as speedup model
493 * \return the alpha parameter (serial part of a task in percent) for this task
495 double SD_task_get_alpha(SD_task_t task)
497 xbt_assert(SD_task_get_kind(task) == SD_TASK_COMP_PAR_AMDAHL,
498 "Alpha parameter is not defined for this kink of task");
504 * \brief Returns the remaining amount work to do till the completion of a task
507 * \return the remaining amount of work (computation or data transfer) of this task
508 * \see SD_task_get_amount()
510 double SD_task_get_remaining_amount(SD_task_t task)
512 if (task->surf_action)
513 return surf_action_get_remains(task->surf_action);
515 return task->remains;
518 int SD_task_get_kind(SD_task_t task)
523 /** @brief Displays debugging informations about a task */
524 void SD_task_dump(SD_task_t task)
526 unsigned int counter;
527 SD_dependency_t dependency;
530 XBT_INFO("Displaying task %s", SD_task_get_name(task));
531 statename = bprintf("%s %s %s %s %s %s %s %s",
532 (task->state == SD_NOT_SCHEDULED ? "not scheduled" :
534 (task->state == SD_SCHEDULABLE ? "schedulable" : ""),
535 (task->state == SD_SCHEDULED ? "scheduled" : ""),
536 (task->state == SD_RUNNABLE ? "runnable" :
538 (task->state == SD_IN_FIFO ? "in fifo" : ""),
539 (task->state == SD_RUNNING ? "running" : ""),
540 (task->state == SD_DONE ? "done" : ""),
541 (task->state == SD_FAILED ? "failed" : ""));
542 XBT_INFO(" - state: %s", statename);
545 if (task->kind != 0) {
546 switch (task->kind) {
547 case SD_TASK_COMM_E2E:
548 XBT_INFO(" - kind: end-to-end communication");
550 case SD_TASK_COMP_SEQ:
551 XBT_INFO(" - kind: sequential computation");
553 case SD_TASK_COMP_PAR_AMDAHL:
554 XBT_INFO(" - kind: parallel computation following Amdahl's law");
556 case SD_TASK_COMM_PAR_MXN_1D_BLOCK:
557 XBT_INFO(" - kind: MxN data redistribution assuming 1D block distribution");
560 XBT_INFO(" - (unknown kind %d)", task->kind);
565 XBT_INFO(" - tracing category: %s", task->category);
567 XBT_INFO(" - amount: %.0f", SD_task_get_amount(task));
568 if (task->kind == SD_TASK_COMP_PAR_AMDAHL)
569 XBT_INFO(" - alpha: %.2f", task->alpha);
570 XBT_INFO(" - Dependencies to satisfy: %d", task->unsatisfied_dependencies);
571 if (!xbt_dynar_is_empty(task->tasks_before)) {
572 XBT_INFO(" - pre-dependencies:");
573 xbt_dynar_foreach(task->tasks_before, counter, dependency) {
574 XBT_INFO(" %s", SD_task_get_name(dependency->src));
577 if (!xbt_dynar_is_empty(task->tasks_after)) {
578 XBT_INFO(" - post-dependencies:");
579 xbt_dynar_foreach(task->tasks_after, counter, dependency) {
580 XBT_INFO(" %s", SD_task_get_name(dependency->dst));
585 /** @brief Dumps the task in dotty formalism into the FILE* passed as second argument */
586 void SD_task_dotty(SD_task_t task, void *out)
588 unsigned int counter;
589 SD_dependency_t dependency;
590 FILE *fout = (FILE*)out;
591 fprintf(fout, " T%p [label=\"%.20s\"", task, task->name);
592 switch (task->kind) {
593 case SD_TASK_COMM_E2E:
594 case SD_TASK_COMM_PAR_MXN_1D_BLOCK:
595 fprintf(fout, ", shape=box");
597 case SD_TASK_COMP_SEQ:
598 case SD_TASK_COMP_PAR_AMDAHL:
599 fprintf(fout, ", shape=circle");
602 xbt_die("Unknown task type!");
604 fprintf(fout, "];\n");
605 xbt_dynar_foreach(task->tasks_before, counter, dependency) {
606 fprintf(fout, " T%p -> T%p;\n", dependency->src, dependency->dst);
610 /* Destroys a dependency between two tasks.
612 static void __SD_task_dependency_destroy(void *dependency)
614 xbt_free(((SD_dependency_t)dependency)->name);
615 xbt_free(dependency);
619 * \brief Adds a dependency between two tasks
621 * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
622 * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_RUNNABLE.
624 * \param name the name of the new dependency (can be \c NULL)
625 * \param data the user data you want to associate with this dependency (can be \c NULL)
626 * \param src the task which must be executed first
627 * \param dst the task you want to make depend on \a src
628 * \see SD_task_dependency_remove()
630 void SD_task_dependency_add(const char *name, void *data, SD_task_t src,
634 unsigned long length;
637 SD_dependency_t dependency;
639 dynar = src->tasks_after;
640 length = xbt_dynar_length(dynar);
644 "Cannot add a dependency between task '%s' and itself",
645 SD_task_get_name(src));
647 if (!__SD_task_is_not_scheduled(src) && !__SD_task_is_schedulable(src)
648 && !__SD_task_is_scheduled_or_runnable(src) && !__SD_task_is_running(src))
650 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULABLE, SD_SCHEDULED or SD_RUNNABLE"
652 SD_task_get_name(src));
654 if (!__SD_task_is_not_scheduled(dst) && !__SD_task_is_schedulable(dst)
655 && !__SD_task_is_scheduled_or_runnable(dst))
657 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULABLE, SD_SCHEDULED or SD_RUNNABLE",
658 SD_task_get_name(dst));
660 XBT_DEBUG("SD_task_dependency_add: src = %s, dst = %s",
661 SD_task_get_name(src), SD_task_get_name(dst));
662 for (i = 0; i < length && !found; i++) {
663 xbt_dynar_get_cpy(dynar, i, &dependency);
664 found = (dependency->dst == dst);
665 XBT_DEBUG("Dependency %lu: dependency->dst = %s", i,
666 SD_task_get_name(dependency->dst));
671 "A dependency already exists between task '%s' and task '%s'",
672 SD_task_get_name(src), SD_task_get_name(dst));
674 dependency = xbt_new(s_SD_dependency_t, 1);
676 dependency->name = xbt_strdup(name); /* xbt_strdup is cleaver enough to deal with NULL args itself */
677 dependency->data = data;
678 dependency->src = src;
679 dependency->dst = dst;
681 /* src must be executed before dst */
682 xbt_dynar_push(src->tasks_after, &dependency);
683 xbt_dynar_push(dst->tasks_before, &dependency);
685 dst->unsatisfied_dependencies++;
688 /* if the task was runnable, then dst->tasks_before is not empty anymore,
689 so we must go back to state SD_SCHEDULED */
690 if (__SD_task_is_runnable(dst)) {
692 ("SD_task_dependency_add: %s was runnable and becomes scheduled!",
693 SD_task_get_name(dst));
694 __SD_task_set_state(dst, SD_SCHEDULED);
698 * \brief Returns the name given as input when dependency has been created..
701 * \param dst a task depending on \a src
704 const char *SD_task_dependency_get_name(SD_task_t src, SD_task_t dst){
706 SD_dependency_t dependency;
708 xbt_dynar_foreach(src->tasks_after, i, dependency){
709 if (dependency->dst == dst)
710 return dependency->name;
716 * \brief Indicates whether there is a dependency between two tasks.
719 * \param dst a task depending on \a src
721 * If src is NULL, checks whether dst has any pre-dependency.
722 * If dst is NULL, checks whether src has any post-dependency.
724 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst)
726 unsigned int counter;
727 SD_dependency_t dependency;
729 xbt_assert(src != NULL
731 "Invalid parameter: both src and dst are NULL");
735 xbt_dynar_foreach(src->tasks_after, counter, dependency) {
736 if (dependency->dst == dst)
740 return xbt_dynar_length(src->tasks_after);
743 return xbt_dynar_length(dst->tasks_before);
749 * \brief Remove a dependency between two tasks
752 * \param dst a task depending on \a src
753 * \see SD_task_dependency_add()
755 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst)
759 unsigned long length;
762 SD_dependency_t dependency;
764 /* remove the dependency from src->tasks_after */
765 dynar = src->tasks_after;
766 length = xbt_dynar_length(dynar);
768 for (i = 0; i < length && !found; i++) {
769 xbt_dynar_get_cpy(dynar, i, &dependency);
770 if (dependency->dst == dst) {
771 xbt_dynar_remove_at(dynar, i, NULL);
777 "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
778 SD_task_get_name(src), SD_task_get_name(dst),
779 SD_task_get_name(dst), SD_task_get_name(src));
781 /* remove the dependency from dst->tasks_before */
782 dynar = dst->tasks_before;
783 length = xbt_dynar_length(dynar);
786 for (i = 0; i < length && !found; i++) {
787 xbt_dynar_get_cpy(dynar, i, &dependency);
788 if (dependency->src == src) {
789 xbt_dynar_remove_at(dynar, i, NULL);
790 __SD_task_dependency_destroy(dependency);
791 dst->unsatisfied_dependencies--;
796 /* should never happen... */
798 "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
799 SD_task_get_name(dst), SD_task_get_name(src),
800 SD_task_get_name(src), SD_task_get_name(dst));
802 /* if the task was scheduled and dst->tasks_before is empty now, we can make it runnable */
804 if (dst->unsatisfied_dependencies == 0) {
805 if (__SD_task_is_scheduled(dst))
806 __SD_task_set_state(dst, SD_RUNNABLE);
808 __SD_task_set_state(dst, SD_SCHEDULABLE);
811 if (dst->is_not_ready == 0)
812 __SD_task_set_state(dst, SD_SCHEDULABLE);
814 /* __SD_print_dependencies(src);
815 __SD_print_dependencies(dst); */
819 * \brief Returns the user data associated with a dependency between two tasks
822 * \param dst a task depending on \a src
823 * \return the user data associated with this dependency (can be \c NULL)
824 * \see SD_task_dependency_add()
826 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst)
830 unsigned long length;
833 SD_dependency_t dependency;
835 dynar = src->tasks_after;
836 length = xbt_dynar_length(dynar);
838 for (i = 0; i < length && !found; i++) {
839 xbt_dynar_get_cpy(dynar, i, &dependency);
840 found = (dependency->dst == dst);
843 THROWF(arg_error, 0, "No dependency found between task '%s' and '%s'",
844 SD_task_get_name(src), SD_task_get_name(dst));
845 return dependency->data;
848 /* temporary function for debugging */
849 static void __SD_print_watch_points(SD_task_t task)
851 static const int state_masks[] =
852 { SD_SCHEDULABLE, SD_SCHEDULED, SD_RUNNING, SD_RUNNABLE, SD_DONE,
855 static const char *state_names[] =
856 { "schedulable", "scheduled", "running", "runnable", "done",
861 XBT_INFO("Task '%s' watch points (%x): ", SD_task_get_name(task),
865 for (i = 0; i < 5; i++) {
866 if (task->watch_points & state_masks[i])
867 XBT_INFO("%s ", state_names[i]);
872 * \brief Adds a watch point to a task
874 * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
875 * task becomes the one given in argument. The
876 * watch point is then automatically removed.
879 * \param state the \ref e_SD_task_state_t "state" you want to watch
880 * (cannot be #SD_NOT_SCHEDULED)
881 * \see SD_task_unwatch()
883 void SD_task_watch(SD_task_t task, e_SD_task_state_t state)
885 if (state & SD_NOT_SCHEDULED)
887 "Cannot add a watch point for state SD_NOT_SCHEDULED");
889 task->watch_points = task->watch_points | state;
890 /* __SD_print_watch_points(task); */
894 * \brief Removes a watch point from a task
897 * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
898 * \see SD_task_watch()
900 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state)
902 xbt_assert(state != SD_NOT_SCHEDULED,
903 "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
905 task->watch_points = task->watch_points & ~state;
906 /* __SD_print_watch_points(task); */
910 * \brief Returns an approximative estimation of the execution time of a task.
912 * The estimation is very approximative because the value returned is the time
913 * the task would take if it was executed now and if it was the only task.
915 * \param task the task to evaluate
916 * \param workstation_nb number of workstations on which the task would be executed
917 * \param workstation_list the workstations on which the task would be executed
918 * \param flops_amount computation amount for each workstation
919 * \param bytes_amount communication amount between each pair of workstations
922 double SD_task_get_execution_time(SD_task_t task,
924 const SD_workstation_t *
926 const double *flops_amount,
927 const double *bytes_amount)
929 double time, max_time = 0.0;
931 xbt_assert(workstation_nb > 0, "Invalid parameter");
933 /* the task execution time is the maximum execution time of the parallel tasks */
935 for (i = 0; i < workstation_nb; i++) {
937 if (flops_amount != NULL)
939 SD_workstation_get_computation_time(workstation_list[i],
942 if (bytes_amount != NULL)
943 for (j = 0; j < workstation_nb; j++) {
945 SD_route_get_communication_time(workstation_list[i],
952 if (time > max_time) {
959 static XBT_INLINE void SD_task_do_schedule(SD_task_t task)
961 if (!__SD_task_is_not_scheduled(task) && !__SD_task_is_schedulable(task))
962 THROWF(arg_error, 0, "Task '%s' has already been scheduled",
963 SD_task_get_name(task));
965 /* update the task state */
966 if (task->unsatisfied_dependencies == 0)
967 __SD_task_set_state(task, SD_RUNNABLE);
969 __SD_task_set_state(task, SD_SCHEDULED);
973 * \brief Schedules a task
975 * The task state must be #SD_NOT_SCHEDULED.
976 * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
977 * i.e. when its dependencies are satisfied.
979 * \param task the task you want to schedule
980 * \param workstation_count number of workstations on which the task will be executed
981 * \param workstation_list the workstations on which the task will be executed
982 * \param flops_amount computation amount for each workstation
983 * \param bytes_amount communication amount between each pair of workstations
984 * \param rate task execution speed rate
985 * \see SD_task_unschedule()
987 void SD_task_schedule(SD_task_t task, int workstation_count,
988 const SD_workstation_t * workstation_list,
989 const double *flops_amount,
990 const double *bytes_amount, double rate)
992 xbt_assert(workstation_count > 0, "workstation_nb must be positive");
994 task->workstation_nb = workstation_count;
998 task->flops_amount = (double*)xbt_realloc(task->flops_amount,
999 sizeof(double) * workstation_count);
1000 memcpy(task->flops_amount, flops_amount,
1001 sizeof(double) * workstation_count);
1003 xbt_free(task->flops_amount);
1004 task->flops_amount = NULL;
1007 int communication_nb = workstation_count * workstation_count;
1009 task->bytes_amount = (double*)xbt_realloc(task->bytes_amount,
1010 sizeof(double) * communication_nb);
1011 memcpy(task->bytes_amount, bytes_amount,
1012 sizeof(double) * communication_nb);
1014 xbt_free(task->bytes_amount);
1015 task->bytes_amount = NULL;
1018 task->workstation_list = (SD_workstation_t*)
1019 xbt_realloc(task->workstation_list,
1020 sizeof(SD_workstation_t) * workstation_count);
1021 memcpy(task->workstation_list, workstation_list,
1022 sizeof(SD_workstation_t) * workstation_count);
1024 SD_task_do_schedule(task);
1028 * \brief Unschedules a task
1030 * The task state must be #SD_SCHEDULED, #SD_RUNNABLE, #SD_RUNNING or #SD_FAILED.
1031 * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
1032 * Call SD_task_schedule() to schedule it again.
1034 * \param task the task you want to unschedule
1035 * \see SD_task_schedule()
1037 void SD_task_unschedule(SD_task_t task)
1039 if (task->state_set != sd_global->scheduled_task_set &&
1040 task->state_set != sd_global->runnable_task_set &&
1041 task->state_set != sd_global->running_task_set &&
1042 task->state_set != sd_global->failed_task_set)
1043 THROWF(arg_error, 0,
1044 "Task %s: the state must be SD_SCHEDULED, SD_RUNNABLE, SD_RUNNING or SD_FAILED",
1045 SD_task_get_name(task));
1047 if (__SD_task_is_scheduled_or_runnable(task) /* if the task is scheduled or runnable */
1048 && ((task->kind == SD_TASK_COMP_PAR_AMDAHL) ||
1049 (task->kind == SD_TASK_COMM_PAR_MXN_1D_BLOCK))) { /* Don't free scheduling data for typed tasks */
1050 __SD_task_destroy_scheduling_data(task);
1051 xbt_free(task->workstation_list);
1052 task->workstation_list=NULL;
1053 task->workstation_nb = 0;
1056 if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
1057 task->surf_action->cancel();
1059 if (task->unsatisfied_dependencies == 0)
1060 __SD_task_set_state(task, SD_SCHEDULABLE);
1062 __SD_task_set_state(task, SD_NOT_SCHEDULED);
1064 task->remains = task->amount;
1065 task->start_time = -1.0;
1068 /* Destroys the data memorized by SD_task_schedule.
1069 * Task state must be SD_SCHEDULED or SD_RUNNABLE.
1071 static void __SD_task_destroy_scheduling_data(SD_task_t task)
1073 if (!__SD_task_is_scheduled_or_runnable(task)
1074 && !__SD_task_is_in_fifo(task))
1075 THROWF(arg_error, 0,
1076 "Task '%s' must be SD_SCHEDULED, SD_RUNNABLE or SD_IN_FIFO",
1077 SD_task_get_name(task));
1079 xbt_free(task->flops_amount);
1080 xbt_free(task->bytes_amount);
1081 task->flops_amount = task->bytes_amount = NULL;
1084 /* Runs a task. This function is directly called by __SD_task_try_to_run if
1085 * the task doesn't have to wait in FIFOs. Otherwise, it is called by
1086 * __SD_task_just_done when the task gets out of its FIFOs.
1088 void __SD_task_really_run(SD_task_t task)
1094 xbt_assert(__SD_task_is_runnable_or_in_fifo(task),
1095 "Task '%s' is not runnable or in a fifo! Task state: %d",
1096 SD_task_get_name(task), (int)SD_task_get_state(task));
1097 xbt_assert(task->workstation_list != NULL,
1098 "Task '%s': workstation_list is NULL!",
1099 SD_task_get_name(task));
1101 XBT_DEBUG("Really running task '%s'", SD_task_get_name(task));
1102 int host_nb = task->workstation_nb;
1104 /* set this task as current task for the workstations in sequential mode */
1105 for (i = 0; i < host_nb; i++) {
1106 if (SD_workstation_get_access_mode(task->workstation_list[i]) ==
1107 SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1108 sg_host_sd(task->workstation_list[i])->current_task = task;
1109 xbt_assert(__SD_workstation_is_busy(task->workstation_list[i]),
1110 "The workstation should be busy now");
1114 XBT_DEBUG("Task '%s' set as current task for its workstations",
1115 SD_task_get_name(task));
1117 /* start the task */
1119 /* Copy the elements of the task into the action */
1120 hosts = xbt_new(sg_host_t, host_nb);
1122 for (i = 0; i < host_nb; i++)
1123 hosts[i] = task->workstation_list[i];
1125 double *flops_amount = xbt_new0(double, host_nb);
1126 double *bytes_amount = xbt_new0(double, host_nb * host_nb);
1129 if(task->flops_amount)
1130 memcpy(flops_amount, task->flops_amount, sizeof(double) *
1132 if(task->bytes_amount)
1133 memcpy(bytes_amount, task->bytes_amount,
1134 sizeof(double) * host_nb * host_nb);
1136 task->surf_action = surf_host_model_execute_parallel_task((surf_host_model_t)surf_host_model,
1143 task->surf_action->setData(task);
1145 XBT_DEBUG("surf_action = %p", task->surf_action);
1148 TRACE_surf_action(task->surf_action, task->category);
1150 __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
1151 __SD_task_set_state(task, SD_RUNNING);
1152 xbt_assert(__SD_task_is_running(task), "Bad state of task '%s': %d",
1153 SD_task_get_name(task), (int)SD_task_get_state(task));
1157 /* Tries to run a task. This function is called by SD_simulate() when a
1158 * scheduled task becomes SD_RUNNABLE (i.e., when its dependencies are
1160 * If one of the workstations where the task is scheduled on is busy (in
1161 * sequential mode), the task doesn't start.
1162 * Returns whether the task has started.
1164 int __SD_task_try_to_run(SD_task_t task)
1169 SD_workstation_t workstation;
1171 xbt_assert(__SD_task_is_runnable(task),
1172 "Task '%s' is not runnable! Task state: %d",
1173 SD_task_get_name(task), (int)SD_task_get_state(task));
1176 for (i = 0; i < task->workstation_nb; i++) {
1177 can_start = can_start &&
1178 !__SD_workstation_is_busy(task->workstation_list[i]);
1181 XBT_DEBUG("Task '%s' can start: %d", SD_task_get_name(task), can_start);
1183 if (!can_start) { /* if the task cannot start and is not in the FIFOs yet */
1184 for (i = 0; i < task->workstation_nb; i++) {
1185 workstation = task->workstation_list[i];
1186 if (sg_host_sd(workstation)->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1187 XBT_DEBUG("Pushing task '%s' in the FIFO of workstation '%s'",
1188 SD_task_get_name(task),
1189 SD_workstation_get_name(workstation));
1190 xbt_fifo_push(sg_host_sd(workstation)->task_fifo, task);
1193 __SD_task_set_state(task, SD_IN_FIFO);
1194 xbt_assert(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
1195 SD_task_get_name(task), (int)SD_task_get_state(task));
1196 XBT_DEBUG("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
1198 __SD_task_really_run(task);
1204 /* This function is called by SD_simulate when a task is done.
1205 * It updates task->state and task->action and executes if necessary the tasks
1206 * which were waiting in FIFOs for the end of `task'
1208 void __SD_task_just_done(SD_task_t task)
1211 SD_workstation_t workstation;
1213 SD_task_t candidate;
1214 int candidate_nb = 0;
1215 int candidate_capacity = 8;
1216 SD_task_t *candidates;
1219 xbt_assert(__SD_task_is_running(task),
1220 "The task must be running! Task state: %d",
1221 (int)SD_task_get_state(task));
1222 xbt_assert(task->workstation_list != NULL,
1223 "Task '%s': workstation_list is NULL!",
1224 SD_task_get_name(task));
1227 candidates = xbt_new(SD_task_t, 8);
1229 __SD_task_set_state(task, SD_DONE);
1230 task->surf_action->unref();
1231 task->surf_action = NULL;
1233 XBT_DEBUG("Looking for candidates");
1235 /* if the task was executed on sequential workstations,
1236 maybe we can execute the next task of the FIFO for each workstation */
1237 for (i = 0; i < task->workstation_nb; i++) {
1238 workstation = task->workstation_list[i];
1239 XBT_DEBUG("Workstation '%s': access_mode = %d",
1240 SD_workstation_get_name(workstation), (int)sg_host_sd(workstation)->access_mode);
1241 if (sg_host_sd(workstation)->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1242 xbt_assert(sg_host_sd(workstation)->task_fifo != NULL,
1243 "Workstation '%s' has sequential access but no FIFO!",
1244 SD_workstation_get_name(workstation));
1245 xbt_assert(sg_host_sd(workstation)->current_task =
1246 task, "Workstation '%s': current task should be '%s'",
1247 SD_workstation_get_name(workstation),
1248 SD_task_get_name(task));
1250 /* the task is over so we can release the workstation */
1251 sg_host_sd(workstation)->current_task = NULL;
1253 XBT_DEBUG("Getting candidate in FIFO");
1254 candidate = (SD_task_t)
1255 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1256 (sg_host_sd(workstation)->task_fifo));
1258 if (candidate != NULL) {
1259 XBT_DEBUG("Candidate: '%s'", SD_task_get_name(candidate));
1260 xbt_assert(__SD_task_is_in_fifo(candidate),
1261 "Bad state of candidate '%s': %d",
1262 SD_task_get_name(candidate),
1263 (int)SD_task_get_state(candidate));
1266 XBT_DEBUG("Candidate in fifo: %p", candidate);
1268 /* if there was a task waiting for my place */
1269 if (candidate != NULL) {
1270 /* Unfortunately, we are not sure yet that we can execute the task now,
1271 because the task can be waiting more deeply in some other
1272 workstation's FIFOs ...
1273 So we memorize all candidate tasks, and then we will check for each
1274 candidate whether or not all its workstations are available. */
1276 /* realloc if necessary */
1277 if (candidate_nb == candidate_capacity) {
1278 candidate_capacity *= 2;
1279 candidates = (SD_task_t*)
1280 xbt_realloc(candidates,
1281 sizeof(SD_task_t) * candidate_capacity);
1284 /* register the candidate */
1285 candidates[candidate_nb++] = candidate;
1286 candidate->fifo_checked = 0;
1291 XBT_DEBUG("Candidates found: %d", candidate_nb);
1293 /* now we check every candidate task */
1294 for (i = 0; i < candidate_nb; i++) {
1295 candidate = candidates[i];
1297 if (candidate->fifo_checked) {
1298 continue; /* we have already evaluated that task */
1301 xbt_assert(__SD_task_is_in_fifo(candidate),
1302 "Bad state of candidate '%s': %d",
1303 SD_task_get_name(candidate), (int)SD_task_get_state(candidate));
1305 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1306 workstation = candidate->workstation_list[j];
1308 /* I can start on this workstation if the workstation is shared
1309 or if I am the first task in the FIFO */
1310 can_start = sg_host_sd(workstation)->access_mode == SD_WORKSTATION_SHARED_ACCESS
1312 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1313 (sg_host_sd(workstation)->task_fifo));
1316 XBT_DEBUG("Candidate '%s' can start: %d", SD_task_get_name(candidate),
1319 /* now we are sure that I can start! */
1321 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1322 workstation = candidate->workstation_list[j];
1324 /* update the FIFO */
1325 if (sg_host_sd(workstation)->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1326 candidate = (SD_task_t)xbt_fifo_shift(sg_host_sd(workstation)->task_fifo); /* the return value is stored just for debugging */
1327 XBT_DEBUG("Head of the FIFO: '%s'",
1329 NULL) ? SD_task_get_name(candidate) : "NULL");
1330 xbt_assert(candidate == candidates[i],
1331 "Error in __SD_task_just_done: bad first task in the FIFO");
1333 } /* for each workstation */
1335 /* finally execute the task */
1336 XBT_DEBUG("Task '%s' state: %d", SD_task_get_name(candidate),
1337 (int)SD_task_get_state(candidate));
1338 __SD_task_really_run(candidate);
1341 ("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
1342 SD_task_get_name(candidate), candidate->state_set,
1343 sd_global->running_task_set, __SD_task_is_running(candidate));
1344 xbt_assert(__SD_task_is_running(candidate),
1345 "Bad state of task '%s': %d",
1346 SD_task_get_name(candidate),
1347 (int)SD_task_get_state(candidate));
1348 XBT_DEBUG("Okay, the task is running.");
1351 candidate->fifo_checked = 1;
1352 } /* for each candidate */
1354 xbt_free(candidates);
1358 * Remove all dependencies associated with a task. This function is called
1359 * when the task is destroyed.
1361 static void __SD_task_remove_dependencies(SD_task_t task)
1363 /* we must destroy the dependencies carefuly (with SD_dependency_remove)
1364 because each one is stored twice */
1365 SD_dependency_t dependency;
1366 while (!xbt_dynar_is_empty(task->tasks_before)) {
1367 xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
1368 SD_task_dependency_remove(dependency->src, dependency->dst);
1371 while (!xbt_dynar_is_empty(task->tasks_after)) {
1372 xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
1373 SD_task_dependency_remove(dependency->src, dependency->dst);
1378 * \brief Returns the start time of a task
1380 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1382 * \param task: a task
1383 * \return the start time of this task
1385 double SD_task_get_start_time(SD_task_t task)
1387 if (task->surf_action)
1388 return task->surf_action->getStartTime();
1390 return task->start_time;
1394 * \brief Returns the finish time of a task
1396 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1397 * If the state is not completed yet, the returned value is an
1398 * estimation of the task finish time. This value can fluctuate
1399 * until the task is completed.
1401 * \param task: a task
1402 * \return the start time of this task
1404 double SD_task_get_finish_time(SD_task_t task)
1406 if (task->surf_action) /* should never happen as actions are destroyed right after their completion */
1407 return task->surf_action->getFinishTime();
1409 return task->finish_time;
1414 void SD_task_distribute_comp_amdahl(SD_task_t task, int ws_count)
1417 xbt_assert(task->kind == SD_TASK_COMP_PAR_AMDAHL,
1418 "Task %s is not a SD_TASK_COMP_PAR_AMDAHL typed task."
1419 "Cannot use this function.",
1420 SD_task_get_name(task));
1421 task->flops_amount = xbt_new0(double, ws_count);
1422 task->bytes_amount = xbt_new0(double, ws_count * ws_count);
1423 xbt_free(task->workstation_list);
1424 task->workstation_nb = ws_count;
1425 task->workstation_list = xbt_new0(SD_workstation_t, ws_count);
1427 for(i=0;i<ws_count;i++){
1428 task->flops_amount[i] =
1429 (task->alpha + (1 - task->alpha)/ws_count) * task->amount;
1434 /** @brief Auto-schedules a task.
1436 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1437 * allows to specify the task costs at creation, and decouple them from the
1438 * scheduling process where you just specify which resource should deliver the
1441 * To be auto-schedulable, a task must be created with SD_task_create_comm_e2e()
1442 * or SD_task_create_comp_seq(). Check their definitions for the exact semantic
1446 * We should create tasks kind for the following categories:
1447 * - Point to point communication (done)
1448 * - Sequential computation (done)
1449 * - group communication (redistribution, several kinds)
1450 * - parallel tasks with no internal communication (one kind per speedup
1451 * model such as Amdahl)
1452 * - idem+ internal communication. Task type not enough since we cannot store
1453 * comm cost alongside to comp one)
1455 void SD_task_schedulev(SD_task_t task, int count,
1456 const SD_workstation_t * list)
1459 SD_dependency_t dep;
1461 xbt_assert(task->kind != 0,
1462 "Task %s is not typed. Cannot automatically schedule it.",
1463 SD_task_get_name(task));
1464 switch (task->kind) {
1465 case SD_TASK_COMP_PAR_AMDAHL:
1466 SD_task_distribute_comp_amdahl(task, count);
1467 case SD_TASK_COMM_E2E:
1468 case SD_TASK_COMP_SEQ:
1469 xbt_assert(task->workstation_nb == count,
1470 "Got %d locations, but were expecting %d locations",
1471 count,task->workstation_nb);
1472 for (i = 0; i < count; i++)
1473 task->workstation_list[i] = list[i];
1474 if (SD_task_get_kind(task)== SD_TASK_COMP_SEQ && !task->flops_amount){
1475 /*This task has failed and is rescheduled. Reset the flops_amount*/
1476 task->flops_amount = xbt_new0(double, 1);
1477 task->flops_amount[0] = task->remains;
1479 SD_task_do_schedule(task);
1482 xbt_die("Kind of task %s not supported by SD_task_schedulev()",
1483 SD_task_get_name(task));
1485 if (task->kind == SD_TASK_COMM_E2E) {
1486 XBT_VERB("Schedule comm task %s between %s -> %s. It costs %.f bytes",
1487 SD_task_get_name(task),
1488 SD_workstation_get_name(task->workstation_list[0]),
1489 SD_workstation_get_name(task->workstation_list[1]),
1490 task->bytes_amount[2]);
1494 /* Iterate over all children and parents being COMM_E2E to say where I am
1495 * located (and start them if runnable) */
1496 if (task->kind == SD_TASK_COMP_SEQ) {
1497 XBT_VERB("Schedule computation task %s on %s. It costs %.f flops",
1498 SD_task_get_name(task),
1499 SD_workstation_get_name(task->workstation_list[0]),
1500 task->flops_amount[0]);
1502 xbt_dynar_foreach(task->tasks_before, cpt, dep) {
1503 SD_task_t before = dep->src;
1504 if (before->kind == SD_TASK_COMM_E2E) {
1505 before->workstation_list[1] = task->workstation_list[0];
1507 if (before->workstation_list[0] &&
1508 (__SD_task_is_schedulable(before)
1509 || __SD_task_is_not_scheduled(before))) {
1510 SD_task_do_schedule(before);
1512 ("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1513 SD_task_get_name(before),
1514 SD_workstation_get_name(before->workstation_list[0]),
1515 SD_workstation_get_name(before->workstation_list[1]),
1516 before->bytes_amount[2]);
1520 xbt_dynar_foreach(task->tasks_after, cpt, dep) {
1521 SD_task_t after = dep->dst;
1522 if (after->kind == SD_TASK_COMM_E2E) {
1523 after->workstation_list[0] = task->workstation_list[0];
1524 if (after->workstation_list[1]
1525 && (__SD_task_is_not_scheduled(after)
1526 || __SD_task_is_schedulable(after))) {
1527 SD_task_do_schedule(after);
1529 ("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1530 SD_task_get_name(after),
1531 SD_workstation_get_name(after->workstation_list[0]),
1532 SD_workstation_get_name(after->workstation_list[1]),
1533 after->bytes_amount[2]);
1539 /* Iterate over all children and parents being MXN_1D_BLOCK to say where I am
1540 * located (and start them if runnable) */
1541 if (task->kind == SD_TASK_COMP_PAR_AMDAHL) {
1542 XBT_VERB("Schedule computation task %s on %d workstations. %.f flops"
1543 " will be distributed following Amdahl's Law",
1544 SD_task_get_name(task), task->workstation_nb,
1545 task->flops_amount[0]);
1546 xbt_dynar_foreach(task->tasks_before, cpt, dep) {
1547 SD_task_t before = dep->src;
1548 if (before->kind == SD_TASK_COMM_PAR_MXN_1D_BLOCK){
1549 if (!before->workstation_list){
1550 XBT_VERB("Sender side of Task %s is not scheduled yet",
1551 SD_task_get_name(before));
1552 before->workstation_list = xbt_new0(SD_workstation_t, count);
1553 before->workstation_nb = count;
1554 XBT_VERB("Fill the workstation list with list of Task '%s'",
1555 SD_task_get_name(task));
1556 for (i=0;i<count;i++)
1557 before->workstation_list[i] = task->workstation_list[i];
1559 XBT_VERB("Build communication matrix for task '%s'",
1560 SD_task_get_name(before));
1562 double src_start, src_end, dst_start, dst_end;
1563 src_nb = before->workstation_nb;
1565 before->workstation_list = (SD_workstation_t*) xbt_realloc(
1566 before->workstation_list,
1567 (before->workstation_nb+count)*sizeof(s_SD_workstation_t));
1568 for(i=0; i<count; i++)
1569 before->workstation_list[before->workstation_nb+i] =
1570 task->workstation_list[i];
1572 before->workstation_nb += count;
1573 xbt_free(before->flops_amount);
1574 xbt_free(before->bytes_amount);
1575 before->flops_amount = xbt_new0(double,
1576 before->workstation_nb);
1577 before->bytes_amount = xbt_new0(double,
1578 before->workstation_nb*
1579 before->workstation_nb);
1581 for(i=0;i<src_nb;i++){
1582 src_start = i*before->amount/src_nb;
1583 src_end = src_start + before->amount/src_nb;
1584 for(j=0; j<dst_nb; j++){
1585 dst_start = j*before->amount/dst_nb;
1586 dst_end = dst_start + before->amount/dst_nb;
1587 XBT_VERB("(%s->%s): (%.2f, %.2f)-> (%.2f, %.2f)",
1588 SD_workstation_get_name(before->workstation_list[i]),
1589 SD_workstation_get_name(before->workstation_list[src_nb+j]),
1590 src_start, src_end, dst_start, dst_end);
1591 if ((src_end <= dst_start) || (dst_end <= src_start)) {
1592 before->bytes_amount[i*(src_nb+dst_nb)+src_nb+j]=0.0;
1594 before->bytes_amount[i*(src_nb+dst_nb)+src_nb+j] =
1595 MIN(src_end, dst_end) - MAX(src_start, dst_start);
1597 XBT_VERB("==> %.2f",
1598 before->bytes_amount[i*(src_nb+dst_nb)+src_nb+j]);
1602 if (__SD_task_is_schedulable(before) ||
1603 __SD_task_is_not_scheduled(before)) {
1604 SD_task_do_schedule(before);
1606 ("Auto-Schedule redistribution task %s. Send %.f bytes from %d hosts to %d hosts.",
1607 SD_task_get_name(before),before->amount, src_nb, dst_nb);
1612 xbt_dynar_foreach(task->tasks_after, cpt, dep) {
1613 SD_task_t after = dep->dst;
1614 if (after->kind == SD_TASK_COMM_PAR_MXN_1D_BLOCK){
1615 if (!after->workstation_list){
1616 XBT_VERB("Receiver side of Task '%s' is not scheduled yet",
1617 SD_task_get_name(after));
1618 after->workstation_list = xbt_new0(SD_workstation_t, count);
1619 after->workstation_nb = count;
1620 XBT_VERB("Fill the workstation list with list of Task '%s'",
1621 SD_task_get_name(task));
1622 for (i=0;i<count;i++)
1623 after->workstation_list[i] = task->workstation_list[i];
1626 double src_start, src_end, dst_start, dst_end;
1628 dst_nb = after->workstation_nb;
1629 after->workstation_list = (SD_workstation_t*) xbt_realloc(
1630 after->workstation_list,
1631 (after->workstation_nb+count)*sizeof(s_SD_workstation_t));
1632 for(i=after->workstation_nb - 1; i>=0; i--)
1633 after->workstation_list[count+i] = after->workstation_list[i];
1634 for(i=0; i<count; i++)
1635 after->workstation_list[i] = task->workstation_list[i];
1637 after->workstation_nb += count;
1639 xbt_free(after->flops_amount);
1640 xbt_free(after->bytes_amount);
1642 after->flops_amount = xbt_new0(double, after->workstation_nb);
1643 after->bytes_amount = xbt_new0(double,
1644 after->workstation_nb*
1645 after->workstation_nb);
1647 for(i=0;i<src_nb;i++){
1648 src_start = i*after->amount/src_nb;
1649 src_end = src_start + after->amount/src_nb;
1650 for(j=0; j<dst_nb; j++){
1651 dst_start = j*after->amount/dst_nb;
1652 dst_end = dst_start + after->amount/dst_nb;
1653 XBT_VERB("(%d->%d): (%.2f, %.2f)-> (%.2f, %.2f)",
1654 i, j, src_start, src_end, dst_start, dst_end);
1655 if ((src_end <= dst_start) || (dst_end <= src_start)) {
1656 after->bytes_amount[i*(src_nb+dst_nb)+src_nb+j]=0.0;
1658 after->bytes_amount[i*(src_nb+dst_nb)+src_nb+j] =
1659 MIN(src_end, dst_end)- MAX(src_start, dst_start);
1661 XBT_VERB("==> %.2f",
1662 after->bytes_amount[i*(src_nb+dst_nb)+src_nb+j]);
1666 if (__SD_task_is_schedulable(after) ||
1667 __SD_task_is_not_scheduled(after)) {
1668 SD_task_do_schedule(after);
1670 ("Auto-Schedule redistribution task %s. Send %.f bytes from %d hosts to %d hosts.",
1671 SD_task_get_name(after),after->amount, src_nb, dst_nb);
1679 /** @brief autoschedule a task on a list of workstations
1681 * This function is very similar to SD_task_schedulev(),
1682 * but takes the list of workstations to schedule onto as separate parameters.
1683 * It builds a proper vector of workstations and then call SD_task_schedulev()
1685 void SD_task_schedulel(SD_task_t task, int count, ...)
1688 SD_workstation_t *list = xbt_new(SD_workstation_t, count);
1690 va_start(ap, count);
1691 for (i = 0; i < count; i++) {
1692 list[i] = va_arg(ap, SD_workstation_t);
1695 SD_task_schedulev(task, count, list);