1 /* Copyright (c) 2006-2014. 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. */
8 #include "simgrid/simdag.h"
9 #include "xbt/sysdep.h"
10 #include "xbt/dynar.h"
11 #include "instr/instr_private.h"
13 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(sd_task, sd,
14 "Logging specific to SimDag (task)");
16 static void __SD_task_remove_dependencies(SD_task_t task);
17 static void __SD_task_destroy_scheduling_data(SD_task_t task);
19 void* SD_task_new_f(void)
21 SD_task_t task = xbt_new0(s_SD_task_t, 1);
22 task->tasks_before = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
23 task->tasks_after = xbt_dynar_new(sizeof(SD_dependency_t), NULL);
28 void SD_task_recycle_f(void *t)
30 SD_task_t task = (SD_task_t) t;
32 /* Reset the content */
33 task->kind = SD_TASK_NOT_TYPED;
34 task->state_hookup.prev = NULL;
35 task->state_hookup.next = NULL;
36 task->state_set = sd_global->not_scheduled_task_set;
37 xbt_swag_insert(task, task->state_set);
38 task->state = SD_NOT_SCHEDULED;
39 task->return_hookup.prev = NULL;
40 task->return_hookup.next = NULL;
44 task->start_time = -1.0;
45 task->finish_time = -1.0;
46 task->surf_action = NULL;
47 task->watch_points = 0;
50 xbt_dynar_reset(task->tasks_before);
51 xbt_dynar_reset(task->tasks_after);
52 task->unsatisfied_dependencies = 0;
53 task->is_not_ready = 0;
55 /* scheduling parameters */
56 task->workstation_nb = 0;
57 task->workstation_list = NULL;
58 task->flops_amount = NULL;
59 task->bytes_amount = NULL;
63 void SD_task_free_f(void *t)
65 SD_task_t task = (SD_task_t)t;
67 xbt_dynar_free(&task->tasks_before);
68 xbt_dynar_free(&task->tasks_after);
73 * \brief Creates a new task.
75 * \param name the name of the task (can be \c NULL)
76 * \param data the user data you want to associate with the task (can be \c NULL)
77 * \param amount amount of the task
78 * \return the new task
79 * \see SD_task_destroy()
81 SD_task_t SD_task_create(const char *name, void *data, double amount)
83 SD_task_t task = xbt_mallocator_get(sd_global->task_mallocator);
85 /* general information */
86 task->data = data; /* user data */
87 task->name = xbt_strdup(name);
88 task->amount = amount;
89 task->remains = amount;
91 sd_global->task_number++;
93 TRACE_sd_task_create(task);
98 static XBT_INLINE SD_task_t SD_task_create_sized(const char *name,
99 void *data, double amount,
102 SD_task_t task = SD_task_create(name, data, amount);
103 task->bytes_amount = xbt_new0(double, ws_count * ws_count);
104 task->flops_amount = xbt_new0(double, ws_count);
105 task->workstation_nb = ws_count;
106 task->workstation_list = xbt_new0(SD_workstation_t, ws_count);
110 /** @brief create a end-to-end communication task that can then be auto-scheduled
112 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
113 * allows to specify the task costs at creation, and decouple them from the
114 * scheduling process where you just specify which resource should deliver the
117 * A end-to-end communication must be scheduled on 2 hosts, and the amount
118 * specified at creation is sent from hosts[0] to hosts[1].
120 SD_task_t SD_task_create_comm_e2e(const char *name, void *data,
123 SD_task_t res = SD_task_create_sized(name, data, amount, 2);
124 res->bytes_amount[2] = amount;
125 res->kind = SD_TASK_COMM_E2E;
127 TRACE_category("COMM_E2E");
128 TRACE_sd_set_task_category(res, "COMM_E2E");
133 /** @brief create a sequential computation task that can then be auto-scheduled
135 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
136 * allows to specify the task costs at creation, and decouple them from the
137 * scheduling process where you just specify which resource should deliver the
140 * A sequential computation must be scheduled on 1 host, and the amount
141 * specified at creation to be run on hosts[0].
143 * \param name the name of the task (can be \c NULL)
144 * \param data the user data you want to associate with the task (can be \c NULL)
145 * \param flops_amount amount of compute work to be done by the task
146 * \return the new SD_TASK_COMP_SEQ typed task
148 SD_task_t SD_task_create_comp_seq(const char *name, void *data,
151 SD_task_t res = SD_task_create_sized(name, data, flops_amount, 1);
152 res->flops_amount[0] = flops_amount;
153 res->kind = SD_TASK_COMP_SEQ;
155 TRACE_category("COMP_SEQ");
156 TRACE_sd_set_task_category(res, "COMP_SEQ");
161 /** @brief create a parallel computation task that can then be auto-scheduled
163 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
164 * allows to specify the task costs at creation, and decouple them from the
165 * scheduling process where you just specify which resource should deliver the
168 * A parallel computation can be scheduled on any number of host.
169 * The underlying speedup model is Amdahl's law.
170 * To be auto-scheduled, \see SD_task_distribute_comp_amdahl has to be called
172 * \param name the name of the task (can be \c NULL)
173 * \param data the user data you want to associate with the task (can be \c NULL)
174 * \param flops_amount amount of compute work to be done by the task
175 * \param alpha purely serial fraction of the work to be done (in [0.;1.[)
176 * \return the new task
178 SD_task_t SD_task_create_comp_par_amdahl(const char *name, void *data,
179 double flops_amount, double alpha)
181 xbt_assert(alpha < 1. && alpha >= 0.,
182 "Invalid parameter: alpha must be in [0.;1.[");
184 SD_task_t res = SD_task_create(name, data, flops_amount);
186 res->kind = SD_TASK_COMP_PAR_AMDAHL;
188 TRACE_category("COMP_PAR_AMDAHL");
189 TRACE_sd_set_task_category(res, "COMP_PAR_AMDAHL");
194 /** @brief create a complex data redistribution task that can then be
197 * Auto-scheduling mean that the task can be used with SD_task_schedulev().
198 * This allows to specify the task costs at creation, and decouple them from
199 * the scheduling process where you just specify which resource should
202 * A data redistribution can be scheduled on any number of host.
203 * The assumed distribution is a 1D block distribution. Each host owns the same
204 * share of the \see amount.
205 * To be auto-scheduled, \see SD_task_distribute_comm_mxn_1d_block has to be
207 * \param name the name of the task (can be \c NULL)
208 * \param data the user data you want to associate with the task (can be
210 * \param amount amount of data to redistribute by the task
211 * \return the new task
213 SD_task_t SD_task_create_comm_par_mxn_1d_block(const char *name, void *data,
216 SD_task_t res = SD_task_create(name, data, amount);
217 res->workstation_list=NULL;
218 res->kind = SD_TASK_COMM_PAR_MXN_1D_BLOCK;
220 TRACE_category("COMM_PAR_MXN_1D_BLOCK");
221 TRACE_sd_set_task_category(res, "COMM_PAR_MXN_1D_BLOCK");
227 * \brief Destroys a task.
229 * The user data (if any) should have been destroyed first.
231 * \param task the task you want to destroy
232 * \see SD_task_create()
234 void SD_task_destroy(SD_task_t task)
236 XBT_DEBUG("Destroying task %s...", SD_task_get_name(task));
238 __SD_task_remove_dependencies(task);
239 /* if the task was scheduled or runnable we have to free the scheduling parameters */
240 if (__SD_task_is_scheduled_or_runnable(task))
241 __SD_task_destroy_scheduling_data(task);
242 if (task->state_set != NULL) /* would be null if just created */
243 xbt_swag_remove(task, task->state_set);
245 xbt_swag_remove(task, sd_global->return_set);
247 xbt_free(task->name);
249 if (task->surf_action != NULL)
250 surf_action_unref(task->surf_action);
252 xbt_free(task->workstation_list);
253 xbt_free(task->bytes_amount);
254 xbt_free(task->flops_amount);
256 TRACE_sd_task_destroy(task);
258 xbt_mallocator_release(sd_global->task_mallocator,task);
259 sd_global->task_number--;
261 XBT_DEBUG("Task destroyed.");
265 * \brief Returns the user data of a task
268 * \return the user data associated with this task (can be \c NULL)
269 * \see SD_task_set_data()
271 void *SD_task_get_data(SD_task_t task)
277 * \brief Sets the user data of a task
279 * The new data can be \c NULL. The old data should have been freed first
280 * if it was not \c NULL.
283 * \param data the new data you want to associate with this task
284 * \see SD_task_get_data()
286 void SD_task_set_data(SD_task_t task, void *data)
292 * \brief Sets the rate of a task
294 * This will change the network bandwidth a task can use. This rate
295 * depends on both the nominal bandwidth on the route onto which the task is
296 * scheduled (\see SD_task_get_current_bandwidth) and the amount of data to
299 * To divide the nominal bandwidth by 2, the rate then has to be :
300 * rate = bandwidth/(2*amount)
302 * \param task a \see SD_TASK_COMM_E2E task (end-to-end communication)
303 * \param rate the new rate you want to associate with this task.
305 void SD_task_set_rate(SD_task_t task, double rate)
307 xbt_assert(task->kind == SD_TASK_COMM_E2E,
308 "The rate can be modified for end-to-end communications only.");
314 * \brief Returns the state of a task
317 * \return the current \ref e_SD_task_state_t "state" of this task:
318 * #SD_NOT_SCHEDULED, #SD_SCHEDULED, #SD_RUNNABLE, #SD_RUNNING, #SD_DONE or #SD_FAILED
319 * \see e_SD_task_state_t
321 e_SD_task_state_t SD_task_get_state(SD_task_t task)
326 /* Changes the state of a task. Updates the swags and the flag sd_global->watch_point_reached.
328 void __SD_task_set_state(SD_task_t task, e_SD_task_state_t new_state)
330 xbt_swag_remove(task, task->state_set);
332 case SD_NOT_SCHEDULED:
333 task->state_set = sd_global->not_scheduled_task_set;
336 task->state_set = sd_global->schedulable_task_set;
339 task->state_set = sd_global->scheduled_task_set;
342 task->state_set = sd_global->runnable_task_set;
345 task->state_set = sd_global->in_fifo_task_set;
348 task->state_set = sd_global->running_task_set;
349 task->start_time = surf_action_get_start_time(task->surf_action);
352 task->state_set = sd_global->done_task_set;
353 task->finish_time = surf_action_get_finish_time(task->surf_action);
356 jedule_log_sd_event(task);
360 task->state_set = sd_global->failed_task_set;
363 xbt_die( "Invalid state");
365 xbt_swag_insert(task, task->state_set);
366 task->state = new_state;
368 if (task->watch_points & new_state) {
369 XBT_VERB("Watch point reached with task '%s'!", SD_task_get_name(task));
370 sd_global->watch_point_reached = 1;
371 SD_task_unwatch(task, new_state); /* remove the watch point */
376 * \brief Returns the name of a task
379 * \return the name of this task (can be \c NULL)
381 const char *SD_task_get_name(SD_task_t task)
386 /** @brief Allows to change the name of a task */
387 void SD_task_set_name(SD_task_t task, const char *name)
389 xbt_free(task->name);
390 task->name = xbt_strdup(name);
393 /** @brief Returns the dynar of the parents of a task
396 * \return a newly allocated dynar comprising the parents of this task
399 xbt_dynar_t SD_task_get_parents(SD_task_t task)
405 parents = xbt_dynar_new(sizeof(SD_task_t), NULL);
406 xbt_dynar_foreach(task->tasks_before, i, dep) {
407 xbt_dynar_push(parents, &(dep->src));
412 /** @brief Returns the dynar of the parents of a task
415 * \return a newly allocated dynar comprising the parents of this task
417 xbt_dynar_t SD_task_get_children(SD_task_t task)
420 xbt_dynar_t children;
423 children = xbt_dynar_new(sizeof(SD_task_t), NULL);
424 xbt_dynar_foreach(task->tasks_after, i, dep) {
425 xbt_dynar_push(children, &(dep->dst));
431 * \brief Returns the amount of workstations involved in a task
433 * Only call this on already scheduled tasks!
436 int SD_task_get_workstation_count(SD_task_t task)
438 return task->workstation_nb;
442 * \brief Returns the list of workstations involved in a task
444 * Only call this on already scheduled tasks!
447 SD_workstation_t *SD_task_get_workstation_list(SD_task_t task)
449 return task->workstation_list;
453 * \brief Returns the total amount of work contained in a task
456 * \return the total amount of work (computation or data transfer) for this task
457 * \see SD_task_get_remaining_amount()
459 double SD_task_get_amount(SD_task_t task)
465 * \brief Sets the total amount of work of a task
466 * For sequential typed tasks (COMP_SEQ and COMM_E2E), it also sets the
467 * appropriate values in the flops_amount and bytes_amount arrays
468 * respectively. Nothing more than modifying task->amount is done for paralle
469 * typed tasks (COMP_PAR_AMDAHL and COMM_PAR_MXN_1D_BLOCK) as the distribution
470 * of the amount of work is done at scheduling time.
473 * \param amount the new amount of work to execute
475 void SD_task_set_amount(SD_task_t task, double amount)
477 task->amount = amount;
478 if (task->kind == SD_TASK_COMP_SEQ)
479 task->flops_amount[0] = amount;
480 if (task->kind == SD_TASK_COMM_E2E)
481 task->bytes_amount[2] = amount;
485 * \brief Returns the alpha parameter of a SD_TASK_COMP_PAR_AMDAHL task
487 * \param task a parallel task assuming Amdahl's law as speedup model
488 * \return the alpha parameter (serial part of a task in percent) for this task
490 double SD_task_get_alpha(SD_task_t task)
492 xbt_assert(SD_task_get_kind(task) == SD_TASK_COMP_PAR_AMDAHL,
493 "Alpha parameter is not defined for this kink of task");
499 * \brief Returns the remaining amount work to do till the completion of a task
502 * \return the remaining amount of work (computation or data transfer) of this task
503 * \see SD_task_get_amount()
505 double SD_task_get_remaining_amount(SD_task_t task)
507 if (task->surf_action)
508 return surf_action_get_remains(task->surf_action);
510 return task->remains;
513 int SD_task_get_kind(SD_task_t task)
518 /** @brief Displays debugging informations about a task */
519 void SD_task_dump(SD_task_t task)
521 unsigned int counter;
522 SD_dependency_t dependency;
525 XBT_INFO("Displaying task %s", SD_task_get_name(task));
526 statename = bprintf("%s %s %s %s %s %s %s %s",
527 (task->state == SD_NOT_SCHEDULED ? "not scheduled" :
529 (task->state == SD_SCHEDULABLE ? "schedulable" : ""),
530 (task->state == SD_SCHEDULED ? "scheduled" : ""),
531 (task->state == SD_RUNNABLE ? "runnable" :
533 (task->state == SD_IN_FIFO ? "in fifo" : ""),
534 (task->state == SD_RUNNING ? "running" : ""),
535 (task->state == SD_DONE ? "done" : ""),
536 (task->state == SD_FAILED ? "failed" : ""));
537 XBT_INFO(" - state: %s", statename);
540 if (task->kind != 0) {
541 switch (task->kind) {
542 case SD_TASK_COMM_E2E:
543 XBT_INFO(" - kind: end-to-end communication");
545 case SD_TASK_COMP_SEQ:
546 XBT_INFO(" - kind: sequential computation");
548 case SD_TASK_COMP_PAR_AMDAHL:
549 XBT_INFO(" - kind: parallel computation following Amdahl's law");
551 case SD_TASK_COMM_PAR_MXN_1D_BLOCK:
552 XBT_INFO(" - kind: MxN data redistribution assuming 1D block distribution");
555 XBT_INFO(" - (unknown kind %d)", task->kind);
560 XBT_INFO(" - tracing category: %s", task->category);
562 XBT_INFO(" - amount: %.0f", SD_task_get_amount(task));
563 if (task->kind == SD_TASK_COMP_PAR_AMDAHL)
564 XBT_INFO(" - alpha: %.2f", task->alpha);
565 XBT_INFO(" - Dependencies to satisfy: %d", task->unsatisfied_dependencies);
566 if (!xbt_dynar_is_empty(task->tasks_before)) {
567 XBT_INFO(" - pre-dependencies:");
568 xbt_dynar_foreach(task->tasks_before, counter, dependency) {
569 XBT_INFO(" %s", SD_task_get_name(dependency->src));
572 if (!xbt_dynar_is_empty(task->tasks_after)) {
573 XBT_INFO(" - post-dependencies:");
574 xbt_dynar_foreach(task->tasks_after, counter, dependency) {
575 XBT_INFO(" %s", SD_task_get_name(dependency->dst));
580 /** @brief Dumps the task in dotty formalism into the FILE* passed as second argument */
581 void SD_task_dotty(SD_task_t task, void *out)
583 unsigned int counter;
584 SD_dependency_t dependency;
585 fprintf(out, " T%p [label=\"%.20s\"", task, task->name);
586 switch (task->kind) {
587 case SD_TASK_COMM_E2E:
588 case SD_TASK_COMM_PAR_MXN_1D_BLOCK:
589 fprintf(out, ", shape=box");
591 case SD_TASK_COMP_SEQ:
592 case SD_TASK_COMP_PAR_AMDAHL:
593 fprintf(out, ", shape=circle");
596 xbt_die("Unknown task type!");
598 fprintf(out, "];\n");
599 xbt_dynar_foreach(task->tasks_before, counter, dependency) {
600 fprintf(out, " T%p -> T%p;\n", dependency->src, dependency->dst);
604 /* Destroys a dependency between two tasks.
606 static void __SD_task_dependency_destroy(void *dependency)
608 xbt_free(((SD_dependency_t)dependency)->name);
609 xbt_free(dependency);
613 * \brief Adds a dependency between two tasks
615 * \a dst will depend on \a src, ie \a dst will not start before \a src is finished.
616 * Their \ref e_SD_task_state_t "state" must be #SD_NOT_SCHEDULED, #SD_SCHEDULED or #SD_RUNNABLE.
618 * \param name the name of the new dependency (can be \c NULL)
619 * \param data the user data you want to associate with this dependency (can be \c NULL)
620 * \param src the task which must be executed first
621 * \param dst the task you want to make depend on \a src
622 * \see SD_task_dependency_remove()
624 void SD_task_dependency_add(const char *name, void *data, SD_task_t src,
628 unsigned long length;
631 SD_dependency_t dependency;
633 dynar = src->tasks_after;
634 length = xbt_dynar_length(dynar);
638 "Cannot add a dependency between task '%s' and itself",
639 SD_task_get_name(src));
641 if (!__SD_task_is_not_scheduled(src) && !__SD_task_is_schedulable(src)
642 && !__SD_task_is_scheduled_or_runnable(src) && !__SD_task_is_running(src))
644 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULABLE, SD_SCHEDULED or SD_RUNNABLE"
646 SD_task_get_name(src));
648 if (!__SD_task_is_not_scheduled(dst) && !__SD_task_is_schedulable(dst)
649 && !__SD_task_is_scheduled_or_runnable(dst))
651 "Task '%s' must be SD_NOT_SCHEDULED, SD_SCHEDULABLE, SD_SCHEDULED or SD_RUNNABLE",
652 SD_task_get_name(dst));
654 XBT_DEBUG("SD_task_dependency_add: src = %s, dst = %s",
655 SD_task_get_name(src), SD_task_get_name(dst));
656 for (i = 0; i < length && !found; i++) {
657 xbt_dynar_get_cpy(dynar, i, &dependency);
658 found = (dependency->dst == dst);
659 XBT_DEBUG("Dependency %lu: dependency->dst = %s", i,
660 SD_task_get_name(dependency->dst));
665 "A dependency already exists between task '%s' and task '%s'",
666 SD_task_get_name(src), SD_task_get_name(dst));
668 dependency = xbt_new(s_SD_dependency_t, 1);
670 dependency->name = xbt_strdup(name); /* xbt_strdup is cleaver enough to deal with NULL args itself */
671 dependency->data = data;
672 dependency->src = src;
673 dependency->dst = dst;
675 /* src must be executed before dst */
676 xbt_dynar_push(src->tasks_after, &dependency);
677 xbt_dynar_push(dst->tasks_before, &dependency);
679 dst->unsatisfied_dependencies++;
682 /* if the task was runnable, then dst->tasks_before is not empty anymore,
683 so we must go back to state SD_SCHEDULED */
684 if (__SD_task_is_runnable(dst)) {
686 ("SD_task_dependency_add: %s was runnable and becomes scheduled!",
687 SD_task_get_name(dst));
688 __SD_task_set_state(dst, SD_SCHEDULED);
692 * \brief Returns the name given as input when dependency has been created..
695 * \param dst a task depending on \a src
698 const char *SD_task_dependency_get_name(SD_task_t src, SD_task_t dst){
700 SD_dependency_t dependency;
702 xbt_dynar_foreach(src->tasks_after, i, dependency){
703 if (dependency->dst == dst)
704 return dependency->name;
710 * \brief Indicates whether there is a dependency between two tasks.
713 * \param dst a task depending on \a src
715 * If src is NULL, checks whether dst has any pre-dependency.
716 * If dst is NULL, checks whether src has any post-dependency.
718 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst)
720 unsigned int counter;
721 SD_dependency_t dependency;
723 xbt_assert(src != NULL
725 "Invalid parameter: both src and dst are NULL");
729 xbt_dynar_foreach(src->tasks_after, counter, dependency) {
730 if (dependency->dst == dst)
734 return xbt_dynar_length(src->tasks_after);
737 return xbt_dynar_length(dst->tasks_before);
743 * \brief Remove a dependency between two tasks
746 * \param dst a task depending on \a src
747 * \see SD_task_dependency_add()
749 void SD_task_dependency_remove(SD_task_t src, SD_task_t dst)
753 unsigned long length;
756 SD_dependency_t dependency;
758 /* remove the dependency from src->tasks_after */
759 dynar = src->tasks_after;
760 length = xbt_dynar_length(dynar);
762 for (i = 0; i < length && !found; i++) {
763 xbt_dynar_get_cpy(dynar, i, &dependency);
764 if (dependency->dst == dst) {
765 xbt_dynar_remove_at(dynar, i, NULL);
771 "No dependency found between task '%s' and '%s': task '%s' is not a successor of task '%s'",
772 SD_task_get_name(src), SD_task_get_name(dst),
773 SD_task_get_name(dst), SD_task_get_name(src));
775 /* remove the dependency from dst->tasks_before */
776 dynar = dst->tasks_before;
777 length = xbt_dynar_length(dynar);
780 for (i = 0; i < length && !found; i++) {
781 xbt_dynar_get_cpy(dynar, i, &dependency);
782 if (dependency->src == src) {
783 xbt_dynar_remove_at(dynar, i, NULL);
784 __SD_task_dependency_destroy(dependency);
785 dst->unsatisfied_dependencies--;
790 /* should never happen... */
792 "SimDag error: task '%s' is a successor of '%s' but task '%s' is not a predecessor of task '%s'",
793 SD_task_get_name(dst), SD_task_get_name(src),
794 SD_task_get_name(src), SD_task_get_name(dst));
796 /* if the task was scheduled and dst->tasks_before is empty now, we can make it runnable */
798 if (dst->unsatisfied_dependencies == 0) {
799 if (__SD_task_is_scheduled(dst))
800 __SD_task_set_state(dst, SD_RUNNABLE);
802 __SD_task_set_state(dst, SD_SCHEDULABLE);
805 if (dst->is_not_ready == 0)
806 __SD_task_set_state(dst, SD_SCHEDULABLE);
808 /* __SD_print_dependencies(src);
809 __SD_print_dependencies(dst); */
813 * \brief Returns the user data associated with a dependency between two tasks
816 * \param dst a task depending on \a src
817 * \return the user data associated with this dependency (can be \c NULL)
818 * \see SD_task_dependency_add()
820 void *SD_task_dependency_get_data(SD_task_t src, SD_task_t dst)
824 unsigned long length;
827 SD_dependency_t dependency;
829 dynar = src->tasks_after;
830 length = xbt_dynar_length(dynar);
832 for (i = 0; i < length && !found; i++) {
833 xbt_dynar_get_cpy(dynar, i, &dependency);
834 found = (dependency->dst == dst);
837 THROWF(arg_error, 0, "No dependency found between task '%s' and '%s'",
838 SD_task_get_name(src), SD_task_get_name(dst));
839 return dependency->data;
842 /* temporary function for debugging */
843 static void __SD_print_watch_points(SD_task_t task)
845 static const int state_masks[] =
846 { SD_SCHEDULABLE, SD_SCHEDULED, SD_RUNNING, SD_RUNNABLE, SD_DONE,
849 static const char *state_names[] =
850 { "schedulable", "scheduled", "running", "runnable", "done",
855 XBT_INFO("Task '%s' watch points (%x): ", SD_task_get_name(task),
859 for (i = 0; i < 5; i++) {
860 if (task->watch_points & state_masks[i])
861 XBT_INFO("%s ", state_names[i]);
866 * \brief Adds a watch point to a task
868 * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
869 * task becomes the one given in argument. The
870 * watch point is then automatically removed.
873 * \param state the \ref e_SD_task_state_t "state" you want to watch
874 * (cannot be #SD_NOT_SCHEDULED)
875 * \see SD_task_unwatch()
877 void SD_task_watch(SD_task_t task, e_SD_task_state_t state)
879 if (state & SD_NOT_SCHEDULED)
881 "Cannot add a watch point for state SD_NOT_SCHEDULED");
883 task->watch_points = task->watch_points | state;
884 /* __SD_print_watch_points(task); */
888 * \brief Removes a watch point from a task
891 * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
892 * \see SD_task_watch()
894 void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state)
896 xbt_assert(state != SD_NOT_SCHEDULED,
897 "SimDag error: Cannot have a watch point for state SD_NOT_SCHEDULED");
899 task->watch_points = task->watch_points & ~state;
900 /* __SD_print_watch_points(task); */
904 * \brief Returns an approximative estimation of the execution time of a task.
906 * The estimation is very approximative because the value returned is the time
907 * the task would take if it was executed now and if it was the only task.
909 * \param task the task to evaluate
910 * \param workstation_nb number of workstations on which the task would be executed
911 * \param workstation_list the workstations on which the task would be executed
912 * \param flops_amount computation amount for each workstation
913 * \param bytes_amount communication amount between each pair of workstations
916 double SD_task_get_execution_time(SD_task_t task,
918 const SD_workstation_t *
920 const double *flops_amount,
921 const double *bytes_amount)
923 double time, max_time = 0.0;
925 xbt_assert(workstation_nb > 0, "Invalid parameter");
927 /* the task execution time is the maximum execution time of the parallel tasks */
929 for (i = 0; i < workstation_nb; i++) {
931 if (flops_amount != NULL)
933 SD_workstation_get_computation_time(workstation_list[i],
936 if (bytes_amount != NULL)
937 for (j = 0; j < workstation_nb; j++) {
939 SD_route_get_communication_time(workstation_list[i],
946 if (time > max_time) {
953 static XBT_INLINE void SD_task_do_schedule(SD_task_t task)
955 if (!__SD_task_is_not_scheduled(task) && !__SD_task_is_schedulable(task))
956 THROWF(arg_error, 0, "Task '%s' has already been scheduled",
957 SD_task_get_name(task));
959 /* update the task state */
960 if (task->unsatisfied_dependencies == 0)
961 __SD_task_set_state(task, SD_RUNNABLE);
963 __SD_task_set_state(task, SD_SCHEDULED);
967 * \brief Schedules a task
969 * The task state must be #SD_NOT_SCHEDULED.
970 * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
971 * i.e. when its dependencies are satisfied.
973 * \param task the task you want to schedule
974 * \param workstation_count number of workstations on which the task will be executed
975 * \param workstation_list the workstations on which the task will be executed
976 * \param flops_amount computation amount for each workstation
977 * \param bytes_amount communication amount between each pair of workstations
978 * \param rate task execution speed rate
979 * \see SD_task_unschedule()
981 void SD_task_schedule(SD_task_t task, int workstation_count,
982 const SD_workstation_t * workstation_list,
983 const double *flops_amount,
984 const double *bytes_amount, double rate)
986 int communication_nb;
987 task->workstation_nb = 0;
989 xbt_assert(workstation_count > 0, "workstation_nb must be positive");
991 task->workstation_nb = workstation_count;
995 task->flops_amount = xbt_realloc(task->flops_amount,
996 sizeof(double) * workstation_count);
997 memcpy(task->flops_amount, flops_amount,
998 sizeof(double) * workstation_count);
1000 xbt_free(task->flops_amount);
1001 task->flops_amount = NULL;
1004 communication_nb = workstation_count * workstation_count;
1006 task->bytes_amount = xbt_realloc(task->bytes_amount,
1007 sizeof(double) * communication_nb);
1008 memcpy(task->bytes_amount, bytes_amount,
1009 sizeof(double) * communication_nb);
1011 xbt_free(task->bytes_amount);
1012 task->bytes_amount = NULL;
1015 task->workstation_list =
1016 xbt_realloc(task->workstation_list,
1017 sizeof(SD_workstation_t) * workstation_count);
1018 memcpy(task->workstation_list, workstation_list,
1019 sizeof(SD_workstation_t) * workstation_count);
1021 SD_task_do_schedule(task);
1025 * \brief Unschedules a task
1027 * The task state must be #SD_SCHEDULED, #SD_RUNNABLE, #SD_RUNNING or #SD_FAILED.
1028 * If you call this function, the task state becomes #SD_NOT_SCHEDULED.
1029 * Call SD_task_schedule() to schedule it again.
1031 * \param task the task you want to unschedule
1032 * \see SD_task_schedule()
1034 void SD_task_unschedule(SD_task_t task)
1036 if (task->state_set != sd_global->scheduled_task_set &&
1037 task->state_set != sd_global->runnable_task_set &&
1038 task->state_set != sd_global->running_task_set &&
1039 task->state_set != sd_global->failed_task_set)
1040 THROWF(arg_error, 0,
1041 "Task %s: the state must be SD_SCHEDULED, SD_RUNNABLE, SD_RUNNING or SD_FAILED",
1042 SD_task_get_name(task));
1044 if (__SD_task_is_scheduled_or_runnable(task) /* if the task is scheduled or runnable */
1045 && ((task->kind == SD_TASK_COMP_PAR_AMDAHL) ||
1046 (task->kind == SD_TASK_COMM_PAR_MXN_1D_BLOCK))) { /* Don't free scheduling data for typed tasks */
1047 __SD_task_destroy_scheduling_data(task);
1048 xbt_free(task->workstation_list);
1049 task->workstation_list=NULL;
1050 task->workstation_nb = 0;
1053 if (__SD_task_is_running(task)) /* the task should become SD_FAILED */
1054 surf_action_cancel(task->surf_action);
1056 if (task->unsatisfied_dependencies == 0)
1057 __SD_task_set_state(task, SD_SCHEDULABLE);
1059 __SD_task_set_state(task, SD_NOT_SCHEDULED);
1061 task->remains = task->amount;
1062 task->start_time = -1.0;
1065 /* Destroys the data memorized by SD_task_schedule.
1066 * Task state must be SD_SCHEDULED or SD_RUNNABLE.
1068 static void __SD_task_destroy_scheduling_data(SD_task_t task)
1070 if (!__SD_task_is_scheduled_or_runnable(task)
1071 && !__SD_task_is_in_fifo(task))
1072 THROWF(arg_error, 0,
1073 "Task '%s' must be SD_SCHEDULED, SD_RUNNABLE or SD_IN_FIFO",
1074 SD_task_get_name(task));
1076 xbt_free(task->flops_amount);
1077 xbt_free(task->bytes_amount);
1078 task->flops_amount = task->bytes_amount = NULL;
1081 /* Runs a task. This function is directly called by __SD_task_try_to_run if
1082 * the task doesn't have to wait in FIFOs. Otherwise, it is called by
1083 * __SD_task_just_done when the task gets out of its FIFOs.
1085 void __SD_task_really_run(SD_task_t task)
1089 void **surf_workstations;
1091 xbt_assert(__SD_task_is_runnable_or_in_fifo(task),
1092 "Task '%s' is not runnable or in a fifo! Task state: %d",
1093 SD_task_get_name(task), (int)SD_task_get_state(task));
1094 xbt_assert(task->workstation_list != NULL,
1095 "Task '%s': workstation_list is NULL!",
1096 SD_task_get_name(task));
1098 XBT_DEBUG("Really running task '%s'", SD_task_get_name(task));
1099 int workstation_nb = task->workstation_nb;
1101 /* set this task as current task for the workstations in sequential mode */
1102 for (i = 0; i < workstation_nb; i++) {
1103 if (SD_workstation_get_access_mode(task->workstation_list[i]) ==
1104 SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1105 SD_workstation_priv(task->workstation_list[i])->current_task = task;
1106 xbt_assert(__SD_workstation_is_busy(task->workstation_list[i]),
1107 "The workstation should be busy now");
1111 XBT_DEBUG("Task '%s' set as current task for its workstations",
1112 SD_task_get_name(task));
1114 /* start the task */
1116 /* we have to create a Surf workstation array instead of the SimDag
1117 * workstation array */
1118 surf_workstations = xbt_new(void *, workstation_nb);
1120 for (i = 0; i < workstation_nb; i++)
1121 surf_workstations[i] = surf_workstation_resource_priv(task->workstation_list[i]);
1123 double *flops_amount = xbt_new0(double, workstation_nb);
1124 double *bytes_amount = xbt_new0(double, workstation_nb * workstation_nb);
1127 if(task->flops_amount)
1128 memcpy(flops_amount, task->flops_amount, sizeof(double) *
1130 if(task->bytes_amount)
1131 memcpy(bytes_amount, task->bytes_amount,
1132 sizeof(double) * workstation_nb * workstation_nb);
1134 task->surf_action = surf_workstation_model_execute_parallel_task((surf_workstation_model_t)surf_workstation_model,
1141 surf_action_set_data(task->surf_action, task);
1143 XBT_DEBUG("surf_action = %p", task->surf_action);
1146 TRACE_surf_action(task->surf_action, task->category);
1148 __SD_task_destroy_scheduling_data(task); /* now the scheduling data are not useful anymore */
1149 __SD_task_set_state(task, SD_RUNNING);
1150 xbt_assert(__SD_task_is_running(task), "Bad state of task '%s': %d",
1151 SD_task_get_name(task), (int)SD_task_get_state(task));
1155 /* Tries to run a task. This function is called by SD_simulate() when a
1156 * scheduled task becomes SD_RUNNABLE (i.e., when its dependencies are
1158 * If one of the workstations where the task is scheduled on is busy (in
1159 * sequential mode), the task doesn't start.
1160 * Returns whether the task has started.
1162 int __SD_task_try_to_run(SD_task_t task)
1167 SD_workstation_t workstation;
1169 xbt_assert(__SD_task_is_runnable(task),
1170 "Task '%s' is not runnable! Task state: %d",
1171 SD_task_get_name(task), (int)SD_task_get_state(task));
1174 for (i = 0; i < task->workstation_nb; i++) {
1175 can_start = can_start &&
1176 !__SD_workstation_is_busy(task->workstation_list[i]);
1179 XBT_DEBUG("Task '%s' can start: %d", SD_task_get_name(task), can_start);
1181 if (!can_start) { /* if the task cannot start and is not in the FIFOs yet */
1182 for (i = 0; i < task->workstation_nb; i++) {
1183 workstation = task->workstation_list[i];
1184 if (SD_workstation_priv(workstation)->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1185 XBT_DEBUG("Pushing task '%s' in the FIFO of workstation '%s'",
1186 SD_task_get_name(task),
1187 SD_workstation_get_name(workstation));
1188 xbt_fifo_push(SD_workstation_priv(workstation)->task_fifo, task);
1191 __SD_task_set_state(task, SD_IN_FIFO);
1192 xbt_assert(__SD_task_is_in_fifo(task), "Bad state of task '%s': %d",
1193 SD_task_get_name(task), (int)SD_task_get_state(task));
1194 XBT_DEBUG("Task '%s' state is now SD_IN_FIFO", SD_task_get_name(task));
1196 __SD_task_really_run(task);
1202 /* This function is called by SD_simulate when a task is done.
1203 * It updates task->state and task->action and executes if necessary the tasks
1204 * which were waiting in FIFOs for the end of `task'
1206 void __SD_task_just_done(SD_task_t task)
1209 SD_workstation_t workstation;
1211 SD_task_t candidate;
1212 int candidate_nb = 0;
1213 int candidate_capacity = 8;
1214 SD_task_t *candidates;
1217 xbt_assert(__SD_task_is_running(task),
1218 "The task must be running! Task state: %d",
1219 (int)SD_task_get_state(task));
1220 xbt_assert(task->workstation_list != NULL,
1221 "Task '%s': workstation_list is NULL!",
1222 SD_task_get_name(task));
1225 candidates = xbt_new(SD_task_t, 8);
1227 __SD_task_set_state(task, SD_DONE);
1228 surf_action_unref(task->surf_action);
1229 task->surf_action = NULL;
1231 XBT_DEBUG("Looking for candidates");
1233 /* if the task was executed on sequential workstations,
1234 maybe we can execute the next task of the FIFO for each workstation */
1235 for (i = 0; i < task->workstation_nb; i++) {
1236 workstation = task->workstation_list[i];
1237 XBT_DEBUG("Workstation '%s': access_mode = %d",
1238 SD_workstation_get_name(workstation), (int)SD_workstation_priv(workstation)->access_mode);
1239 if (SD_workstation_priv(workstation)->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1240 xbt_assert(SD_workstation_priv(workstation)->task_fifo != NULL,
1241 "Workstation '%s' has sequential access but no FIFO!",
1242 SD_workstation_get_name(workstation));
1243 xbt_assert(SD_workstation_priv(workstation)->current_task =
1244 task, "Workstation '%s': current task should be '%s'",
1245 SD_workstation_get_name(workstation),
1246 SD_task_get_name(task));
1248 /* the task is over so we can release the workstation */
1249 SD_workstation_priv(workstation)->current_task = NULL;
1251 XBT_DEBUG("Getting candidate in FIFO");
1253 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1254 (SD_workstation_priv(workstation)->task_fifo));
1256 if (candidate != NULL) {
1257 XBT_DEBUG("Candidate: '%s'", SD_task_get_name(candidate));
1258 xbt_assert(__SD_task_is_in_fifo(candidate),
1259 "Bad state of candidate '%s': %d",
1260 SD_task_get_name(candidate),
1261 (int)SD_task_get_state(candidate));
1264 XBT_DEBUG("Candidate in fifo: %p", candidate);
1266 /* if there was a task waiting for my place */
1267 if (candidate != NULL) {
1268 /* Unfortunately, we are not sure yet that we can execute the task now,
1269 because the task can be waiting more deeply in some other
1270 workstation's FIFOs ...
1271 So we memorize all candidate tasks, and then we will check for each
1272 candidate whether or not all its workstations are available. */
1274 /* realloc if necessary */
1275 if (candidate_nb == candidate_capacity) {
1276 candidate_capacity *= 2;
1278 xbt_realloc(candidates,
1279 sizeof(SD_task_t) * candidate_capacity);
1282 /* register the candidate */
1283 candidates[candidate_nb++] = candidate;
1284 candidate->fifo_checked = 0;
1289 XBT_DEBUG("Candidates found: %d", candidate_nb);
1291 /* now we check every candidate task */
1292 for (i = 0; i < candidate_nb; i++) {
1293 candidate = candidates[i];
1295 if (candidate->fifo_checked) {
1296 continue; /* we have already evaluated that task */
1299 xbt_assert(__SD_task_is_in_fifo(candidate),
1300 "Bad state of candidate '%s': %d",
1301 SD_task_get_name(candidate), (int)SD_task_get_state(candidate));
1303 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1304 workstation = candidate->workstation_list[j];
1306 /* I can start on this workstation if the workstation is shared
1307 or if I am the first task in the FIFO */
1308 can_start = SD_workstation_priv(workstation)->access_mode == SD_WORKSTATION_SHARED_ACCESS
1310 xbt_fifo_get_item_content(xbt_fifo_get_first_item
1311 (SD_workstation_priv(workstation)->task_fifo));
1314 XBT_DEBUG("Candidate '%s' can start: %d", SD_task_get_name(candidate),
1317 /* now we are sure that I can start! */
1319 for (j = 0; j < candidate->workstation_nb && can_start; j++) {
1320 workstation = candidate->workstation_list[j];
1322 /* update the FIFO */
1323 if (SD_workstation_priv(workstation)->access_mode == SD_WORKSTATION_SEQUENTIAL_ACCESS) {
1324 candidate = xbt_fifo_shift(SD_workstation_priv(workstation)->task_fifo); /* the return value is stored just for debugging */
1325 XBT_DEBUG("Head of the FIFO: '%s'",
1327 NULL) ? SD_task_get_name(candidate) : "NULL");
1328 xbt_assert(candidate == candidates[i],
1329 "Error in __SD_task_just_done: bad first task in the FIFO");
1331 } /* for each workstation */
1333 /* finally execute the task */
1334 XBT_DEBUG("Task '%s' state: %d", SD_task_get_name(candidate),
1335 (int)SD_task_get_state(candidate));
1336 __SD_task_really_run(candidate);
1339 ("Calling __SD_task_is_running: task '%s', state set: %p, running_task_set: %p, is running: %d",
1340 SD_task_get_name(candidate), candidate->state_set,
1341 sd_global->running_task_set, __SD_task_is_running(candidate));
1342 xbt_assert(__SD_task_is_running(candidate),
1343 "Bad state of task '%s': %d",
1344 SD_task_get_name(candidate),
1345 (int)SD_task_get_state(candidate));
1346 XBT_DEBUG("Okay, the task is running.");
1349 candidate->fifo_checked = 1;
1350 } /* for each candidate */
1352 xbt_free(candidates);
1356 * Remove all dependencies associated with a task. This function is called
1357 * when the task is destroyed.
1359 static void __SD_task_remove_dependencies(SD_task_t task)
1361 /* we must destroy the dependencies carefuly (with SD_dependency_remove)
1362 because each one is stored twice */
1363 SD_dependency_t dependency;
1364 while (!xbt_dynar_is_empty(task->tasks_before)) {
1365 xbt_dynar_get_cpy(task->tasks_before, 0, &dependency);
1366 SD_task_dependency_remove(dependency->src, dependency->dst);
1369 while (!xbt_dynar_is_empty(task->tasks_after)) {
1370 xbt_dynar_get_cpy(task->tasks_after, 0, &dependency);
1371 SD_task_dependency_remove(dependency->src, dependency->dst);
1376 * \brief Returns the start time of a task
1378 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1380 * \param task: a task
1381 * \return the start time of this task
1383 double SD_task_get_start_time(SD_task_t task)
1385 if (task->surf_action)
1386 return surf_action_get_start_time(task->surf_action);
1388 return task->start_time;
1392 * \brief Returns the finish time of a task
1394 * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
1395 * If the state is not completed yet, the returned value is an
1396 * estimation of the task finish time. This value can fluctuate
1397 * until the task is completed.
1399 * \param task: a task
1400 * \return the start time of this task
1402 double SD_task_get_finish_time(SD_task_t task)
1404 if (task->surf_action) /* should never happen as actions are destroyed right after their completion */
1405 return surf_action_get_finish_time(task->surf_action);
1407 return task->finish_time;
1412 void SD_task_distribute_comp_amdahl(SD_task_t task, int ws_count)
1415 xbt_assert(task->kind == SD_TASK_COMP_PAR_AMDAHL,
1416 "Task %s is not a SD_TASK_COMP_PAR_AMDAHL typed task."
1417 "Cannot use this function.",
1418 SD_task_get_name(task));
1419 task->flops_amount = xbt_new0(double, ws_count);
1420 task->bytes_amount = xbt_new0(double, ws_count * ws_count);
1421 xbt_free(task->workstation_list);
1422 task->workstation_nb = ws_count;
1423 task->workstation_list = xbt_new0(SD_workstation_t, ws_count);
1425 for(i=0;i<ws_count;i++){
1426 task->flops_amount[i] =
1427 (task->alpha + (1 - task->alpha)/ws_count) * task->amount;
1432 /** @brief Auto-schedules a task.
1434 * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
1435 * allows to specify the task costs at creation, and decouple them from the
1436 * scheduling process where you just specify which resource should deliver the
1439 * To be auto-schedulable, a task must be created with SD_task_create_comm_e2e()
1440 * or SD_task_create_comp_seq(). Check their definitions for the exact semantic
1444 * We should create tasks kind for the following categories:
1445 * - Point to point communication (done)
1446 * - Sequential computation (done)
1447 * - group communication (redistribution, several kinds)
1448 * - parallel tasks with no internal communication (one kind per speedup
1449 * model such as Amdahl)
1450 * - idem+ internal communication. Task type not enough since we cannot store
1451 * comm cost alongside to comp one)
1453 void SD_task_schedulev(SD_task_t task, int count,
1454 const SD_workstation_t * list)
1457 SD_dependency_t dep;
1459 xbt_assert(task->kind != 0,
1460 "Task %s is not typed. Cannot automatically schedule it.",
1461 SD_task_get_name(task));
1462 switch (task->kind) {
1463 case SD_TASK_COMP_PAR_AMDAHL:
1464 SD_task_distribute_comp_amdahl(task, count);
1465 case SD_TASK_COMM_E2E:
1466 case SD_TASK_COMP_SEQ:
1467 xbt_assert(task->workstation_nb == count,
1468 "Got %d locations, but were expecting %d locations",
1469 count,task->workstation_nb);
1470 for (i = 0; i < count; i++)
1471 task->workstation_list[i] = list[i];
1472 if (SD_task_get_kind(task)== SD_TASK_COMP_SEQ && !task->flops_amount){
1473 /*This task has failed and is rescheduled. Reset the flops_amount*/
1474 task->flops_amount = xbt_new0(double, 1);
1475 task->flops_amount[0] = task->remains;
1477 SD_task_do_schedule(task);
1480 xbt_die("Kind of task %s not supported by SD_task_schedulev()",
1481 SD_task_get_name(task));
1483 if (task->kind == SD_TASK_COMM_E2E) {
1484 XBT_VERB("Schedule comm task %s between %s -> %s. It costs %.f bytes",
1485 SD_task_get_name(task),
1486 SD_workstation_get_name(task->workstation_list[0]),
1487 SD_workstation_get_name(task->workstation_list[1]),
1488 task->bytes_amount[2]);
1492 /* Iterate over all children and parents being COMM_E2E to say where I am
1493 * located (and start them if runnable) */
1494 if (task->kind == SD_TASK_COMP_SEQ) {
1495 XBT_VERB("Schedule computation task %s on %s. It costs %.f flops",
1496 SD_task_get_name(task),
1497 SD_workstation_get_name(task->workstation_list[0]),
1498 task->flops_amount[0]);
1500 xbt_dynar_foreach(task->tasks_before, cpt, dep) {
1501 SD_task_t before = dep->src;
1502 if (before->kind == SD_TASK_COMM_E2E) {
1503 before->workstation_list[1] = task->workstation_list[0];
1505 if (before->workstation_list[0] &&
1506 (__SD_task_is_schedulable(before)
1507 || __SD_task_is_not_scheduled(before))) {
1508 SD_task_do_schedule(before);
1510 ("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1511 SD_task_get_name(before),
1512 SD_workstation_get_name(before->workstation_list[0]),
1513 SD_workstation_get_name(before->workstation_list[1]),
1514 before->bytes_amount[2]);
1518 xbt_dynar_foreach(task->tasks_after, cpt, dep) {
1519 SD_task_t after = dep->dst;
1520 if (after->kind == SD_TASK_COMM_E2E) {
1521 after->workstation_list[0] = task->workstation_list[0];
1522 if (after->workstation_list[1]
1523 && (__SD_task_is_not_scheduled(after)
1524 || __SD_task_is_schedulable(after))) {
1525 SD_task_do_schedule(after);
1527 ("Auto-Schedule comm task %s between %s -> %s. It costs %.f bytes",
1528 SD_task_get_name(after),
1529 SD_workstation_get_name(after->workstation_list[0]),
1530 SD_workstation_get_name(after->workstation_list[1]),
1531 after->bytes_amount[2]);
1537 /* Iterate over all children and parents being MXN_1D_BLOCK to say where I am
1538 * located (and start them if runnable) */
1539 if (task->kind == SD_TASK_COMP_PAR_AMDAHL) {
1540 XBT_VERB("Schedule computation task %s on %d workstations. %.f flops"
1541 " will be distributed following Amdahl's Law",
1542 SD_task_get_name(task), task->workstation_nb,
1543 task->flops_amount[0]);
1544 xbt_dynar_foreach(task->tasks_before, cpt, dep) {
1545 SD_task_t before = dep->src;
1546 if (before->kind == SD_TASK_COMM_PAR_MXN_1D_BLOCK){
1547 if (!before->workstation_list){
1548 XBT_VERB("Sender side of Task %s is not scheduled yet",
1549 SD_task_get_name(before));
1550 before->workstation_list = xbt_new0(SD_workstation_t, count);
1551 before->workstation_nb = count;
1552 XBT_VERB("Fill the workstation list with list of Task '%s'",
1553 SD_task_get_name(task));
1554 for (i=0;i<count;i++)
1555 before->workstation_list[i] = task->workstation_list[i];
1557 XBT_VERB("Build communication matrix for task '%s'",
1558 SD_task_get_name(before));
1560 double src_start, src_end, dst_start, dst_end;
1561 src_nb = before->workstation_nb;
1563 before->workstation_list = (SD_workstation_t*) xbt_realloc(
1564 before->workstation_list,
1565 (before->workstation_nb+count)*sizeof(s_SD_workstation_t));
1566 for(i=0; i<count; i++)
1567 before->workstation_list[before->workstation_nb+i] =
1568 task->workstation_list[i];
1570 before->workstation_nb += count;
1571 xbt_free(before->flops_amount);
1572 xbt_free(before->bytes_amount);
1573 before->flops_amount = xbt_new0(double,
1574 before->workstation_nb);
1575 before->bytes_amount = xbt_new0(double,
1576 before->workstation_nb*
1577 before->workstation_nb);
1579 for(i=0;i<src_nb;i++){
1580 src_start = i*before->amount/src_nb;
1581 src_end = src_start + before->amount/src_nb;
1582 for(j=0; j<dst_nb; j++){
1583 dst_start = j*before->amount/dst_nb;
1584 dst_end = dst_start + before->amount/dst_nb;
1585 XBT_VERB("(%s->%s): (%.2f, %.2f)-> (%.2f, %.2f)",
1586 SD_workstation_get_name(before->workstation_list[i]),
1587 SD_workstation_get_name(before->workstation_list[src_nb+j]),
1588 src_start, src_end, dst_start, dst_end);
1589 if ((src_end <= dst_start) || (dst_end <= src_start)) {
1590 before->bytes_amount[i*(src_nb+dst_nb)+src_nb+j]=0.0;
1592 before->bytes_amount[i*(src_nb+dst_nb)+src_nb+j] =
1593 MIN(src_end, dst_end) - MAX(src_start, dst_start);
1595 XBT_VERB("==> %.2f",
1596 before->bytes_amount[i*(src_nb+dst_nb)+src_nb+j]);
1600 if (__SD_task_is_schedulable(before) ||
1601 __SD_task_is_not_scheduled(before)) {
1602 SD_task_do_schedule(before);
1604 ("Auto-Schedule redistribution task %s. Send %.f bytes from %d hosts to %d hosts.",
1605 SD_task_get_name(before),before->amount, src_nb, dst_nb);
1610 xbt_dynar_foreach(task->tasks_after, cpt, dep) {
1611 SD_task_t after = dep->dst;
1612 if (after->kind == SD_TASK_COMM_PAR_MXN_1D_BLOCK){
1613 if (!after->workstation_list){
1614 XBT_VERB("Receiver side of Task '%s' is not scheduled yet",
1615 SD_task_get_name(after));
1616 after->workstation_list = xbt_new0(SD_workstation_t, count);
1617 after->workstation_nb = count;
1618 XBT_VERB("Fill the workstation list with list of Task '%s'",
1619 SD_task_get_name(task));
1620 for (i=0;i<count;i++)
1621 after->workstation_list[i] = task->workstation_list[i];
1624 double src_start, src_end, dst_start, dst_end;
1626 dst_nb = after->workstation_nb;
1627 after->workstation_list = (SD_workstation_t*) xbt_realloc(
1628 after->workstation_list,
1629 (after->workstation_nb+count)*sizeof(s_SD_workstation_t));
1630 for(i=after->workstation_nb - 1; i>=0; i--)
1631 after->workstation_list[count+i] = after->workstation_list[i];
1632 for(i=0; i<count; i++)
1633 after->workstation_list[i] = task->workstation_list[i];
1635 after->workstation_nb += count;
1637 xbt_free(after->flops_amount);
1638 xbt_free(after->bytes_amount);
1640 after->flops_amount = xbt_new0(double, after->workstation_nb);
1641 after->bytes_amount = xbt_new0(double,
1642 after->workstation_nb*
1643 after->workstation_nb);
1645 for(i=0;i<src_nb;i++){
1646 src_start = i*after->amount/src_nb;
1647 src_end = src_start + after->amount/src_nb;
1648 for(j=0; j<dst_nb; j++){
1649 dst_start = j*after->amount/dst_nb;
1650 dst_end = dst_start + after->amount/dst_nb;
1651 XBT_VERB("(%d->%d): (%.2f, %.2f)-> (%.2f, %.2f)",
1652 i, j, src_start, src_end, dst_start, dst_end);
1653 if ((src_end <= dst_start) || (dst_end <= src_start)) {
1654 after->bytes_amount[i*(src_nb+dst_nb)+src_nb+j]=0.0;
1656 after->bytes_amount[i*(src_nb+dst_nb)+src_nb+j] =
1657 MIN(src_end, dst_end)- MAX(src_start, dst_start);
1659 XBT_VERB("==> %.2f",
1660 after->bytes_amount[i*(src_nb+dst_nb)+src_nb+j]);
1664 if (__SD_task_is_schedulable(after) ||
1665 __SD_task_is_not_scheduled(after)) {
1666 SD_task_do_schedule(after);
1668 ("Auto-Schedule redistribution task %s. Send %.f bytes from %d hosts to %d hosts.",
1669 SD_task_get_name(after),after->amount, src_nb, dst_nb);
1677 /** @brief autoschedule a task on a list of workstations
1679 * This function is very similar to SD_task_schedulev(),
1680 * but takes the list of workstations to schedule onto as separate parameters.
1681 * It builds a proper vector of workstations and then call SD_task_schedulev()
1683 void SD_task_schedulel(SD_task_t task, int count, ...)
1686 SD_workstation_t *list = xbt_new(SD_workstation_t, count);
1688 va_start(ap, count);
1689 for (i = 0; i < count; i++) {
1690 list[i] = va_arg(ap, SD_workstation_t);
1693 SD_task_schedulev(task, count, list);