Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Simplify the code of typed tasks in simdag by allocating the comm_ and comp_amount...
[simgrid.git] / src / simdag / sd_task.c
index 501f2a4..9538594 100644 (file)
@@ -1,3 +1,8 @@
+/* Copyright (c) 2007-2009 Da SimGrid Team.  All rights reserved.           */
+
+/* This program is free software; you can redistribute it and/or modify it
+ * under the terms of the license (GNU LGPL) which comes with this package. */
+
 #include "private.h"
 #include "simdag/simdag.h"
 #include "xbt/sysdep.h"
@@ -28,11 +33,8 @@ SD_task_t SD_task_create(const char *name, void *data, double amount)
 
   /* general information */
   task->data = data;            /* user data */
-  if (name != NULL)
-    task->name = xbt_strdup(name);
-  else
-    task->name = NULL;
-
+  task->name = xbt_strdup(name);
+  task->kind = 0;
   task->state_hookup.prev = NULL;
   task->state_hookup.next = NULL;
   task->state_set = sd_global->not_scheduled_task_set;
@@ -129,14 +131,12 @@ void __SD_task_set_state(SD_task_t task, e_SD_task_state_t new_state)
   case SD_RUNNING:
     task->state_set = sd_global->running_task_set;
     task->start_time =
-      surf_workstation_model->common_public->action_get_start_time(task->
-                                                                   surf_action);
+      surf_workstation_model->action_get_start_time(task->surf_action);
     break;
   case SD_DONE:
     task->state_set = sd_global->done_task_set;
     task->finish_time =
-      surf_workstation_model->common_public->action_get_finish_time(task->
-                                                                    surf_action);
+      surf_workstation_model->action_get_finish_time(task->surf_action);
     task->remains = 0;
     break;
   case SD_FAILED:
@@ -168,6 +168,77 @@ const char *SD_task_get_name(SD_task_t task)
   return task->name;
 }
 
+/** @brief Returns the dynar of the parents of a task
+ *
+ * \param task a task
+ * \return a newly allocated dynar comprising the parents of this task
+ */
+
+xbt_dynar_t SD_task_get_parents(SD_task_t task)
+{
+  unsigned int i;
+  xbt_dynar_t parents;
+  SD_dependency_t dep;
+  SD_CHECK_INIT_DONE();
+  xbt_assert0(task != NULL, "Invalid parameter");
+
+  parents = xbt_dynar_new(sizeof(SD_task_t), NULL);
+  xbt_dynar_foreach(task->tasks_before, i, dep){
+    xbt_dynar_push(parents, &(dep->src));
+  }
+  return parents;
+}
+
+/** @brief Returns the dynar of the parents of a task
+ *
+ * \param task a task
+ * \return a newly allocated dynar comprising the parents of this task
+ */
+xbt_dynar_t SD_task_get_children(SD_task_t task)
+{
+  unsigned int i;
+  xbt_dynar_t children;
+  SD_dependency_t dep;
+  SD_CHECK_INIT_DONE();
+  xbt_assert0(task != NULL, "Invalid parameter");
+
+  children = xbt_dynar_new(sizeof(SD_task_t), NULL);
+  xbt_dynar_foreach(task->tasks_after, i, dep){
+    xbt_dynar_push(children, &(dep->dst));
+  }
+  return children;
+}
+
+/**
+ * \brief Returns the amount of workstations involved in a task
+ *
+ * Only call this on already scheduled tasks!
+ * \param task a task
+ */
+int SD_task_get_workstation_count(SD_task_t task)
+{
+  SD_CHECK_INIT_DONE();
+  xbt_assert0(task != NULL, "Invalid parameter");
+  //  xbt_assert1( task->state_set != sd_global->scheduled_task_set, 
+  //          "Unscheduled task %s", task->name);
+  return task->workstation_nb;
+}
+
+/**
+ * \brief Returns the list of workstations involved in a task
+ *
+ * Only call this on already scheduled tasks!
+ * \param task a task
+ */
+SD_workstation_t* SD_task_get_workstation_list(SD_task_t task)
+{
+  SD_CHECK_INIT_DONE();
+  xbt_assert0(task != NULL, "Invalid parameter");
+  //xbt_assert1( task->state_set != sd_global->scheduled_task_set, 
+  //          "Unscheduled task %s", task->name);
+  return task->workstation_list;
+}
+
 /**
  * \brief Returns the total amount of a task
  *
@@ -195,40 +266,61 @@ double SD_task_get_remaining_amount(SD_task_t task)
   xbt_assert0(task != NULL, "Invalid parameter");
 
   if (task->surf_action)
-    return task->surf_action->remains;
+    return surf_workstation_model->get_remains(task->surf_action);
   else
     return task->remains;
 }
 
-/* temporary function for debbuging */
-static void __SD_print_dependencies(SD_task_t task)
+/** @brief Displays debugging informations about a task */
+void SD_task_dump(SD_task_t task)
 {
-  xbt_dynar_t dynar;
-  int length;
-  int i;
+  unsigned int counter;
   SD_dependency_t dependency;
 
-  INFO1("The following tasks must be executed before %s:",
-        SD_task_get_name(task));
-  dynar = task->tasks_before;
-  length = xbt_dynar_length(dynar);
-
-
-  for (i = 0; i < length; i++) {
-    xbt_dynar_get_cpy(dynar, i, &dependency);
-    INFO1(" %s", SD_task_get_name(dependency->src));
+  INFO1("Displaying task %s",SD_task_get_name(task));
+  INFO1("  - amount: %.0f",SD_task_get_amount(task));
+  if (task->kind!=0) {
+    switch(task->kind){
+    case SD_TASK_COMM_E2E:
+      INFO0("  - kind: end-to-end communication");
+      break;
+    case SD_TASK_COMP_SEQ:
+      INFO0("  - kind: sequential computation");
+      break;
+    default:
+      INFO1("  - (unknown kind %d)",task->kind);
+    }
   }
-
-  INFO1("The following tasks must be executed after %s:",
-        SD_task_get_name(task));
-
-  dynar = task->tasks_after;
-  length = xbt_dynar_length(dynar);
-  for (i = 0; i < length; i++) {
-    xbt_dynar_get_cpy(dynar, i, &dependency);
-    INFO1(" %s", SD_task_get_name(dependency->dst));
+  if (xbt_dynar_length(task->tasks_before)) {
+    INFO0("  - pre-dependencies:");
+    xbt_dynar_foreach(task->tasks_before,counter,dependency) {
+      INFO1("    %s",SD_task_get_name(dependency->src));
+    }
+  }
+  if (xbt_dynar_length(task->tasks_after)) {
+    INFO0("  - post-dependencies:");
+    xbt_dynar_foreach(task->tasks_after,counter,dependency) {
+      INFO1("    %s",SD_task_get_name(dependency->dst));
+    }
+  }
+}
+/** @brief Dumps the task in dotty formalism into the FILE* passed as second argument */
+void SD_task_dotty(SD_task_t task,void* out) {
+  unsigned int counter;
+  SD_dependency_t dependency;
+  fprintf(out, "  T%d [label=\"%.10s\"",(unsigned int)task,task->name);
+  switch(task->kind){
+    case SD_TASK_COMM_E2E:
+      fprintf(out,", shape=box");
+      break;
+    case SD_TASK_COMP_SEQ:
+      fprintf(out,", shape=circle");
+      break;
+  }
+  fprintf(out,"];\n");
+  xbt_dynar_foreach(task->tasks_before,counter,dependency) {
+    fprintf(out," T%d -> T%d;\n",(unsigned int)dependency->src,(unsigned int)dependency->dst);
   }
-  INFO0("----------------------------");
 }
 
 /* Destroys a dependency between two tasks.
@@ -267,8 +359,6 @@ void SD_task_dependency_add(const char *name, void *data, SD_task_t src,
   dynar = src->tasks_after;
   length = xbt_dynar_length(dynar);
 
-
-
   if (src == dst)
     THROW1(arg_error, 0,
            "Cannot add a dependency between task '%s' and itself",
@@ -302,11 +392,7 @@ void SD_task_dependency_add(const char *name, void *data, SD_task_t src,
 
   dependency = xbt_new(s_SD_dependency_t, 1);
 
-  if (name != NULL)
-    dependency->name = xbt_strdup(name);
-  else
-    dependency->name = NULL;
-
+  dependency->name = xbt_strdup(name); /* xbt_strdup is cleaver enough to deal with NULL args itself */
   dependency->data = data;
   dependency->src = src;
   dependency->dst = dst;
@@ -332,24 +418,29 @@ void SD_task_dependency_add(const char *name, void *data, SD_task_t src,
  *
  * \param src a task
  * \param dst a task depending on \a src
+ *
+ * If src is NULL, checks whether dst has any pre-dependency.
+ * If dst is NULL, checks whether src has any post-dependency.
  */
 int SD_task_dependency_exists(SD_task_t src, SD_task_t dst)
 {
-  xbt_dynar_t dynar;
-  int length;
-  int i;
+  unsigned int counter;
   SD_dependency_t dependency;
 
   SD_CHECK_INIT_DONE();
-  xbt_assert0(src != NULL && dst != NULL, "Invalid parameter");
-
-  dynar = src->tasks_after;
-  length = xbt_dynar_length(dynar);
+  xbt_assert0(src != NULL || dst != NULL, "Invalid parameter: both src and dst are NULL");
 
-  for (i = 0; i < length; i++) {
-    xbt_dynar_get_cpy(dynar, i, &dependency);
-    if (dependency->dst == dst)
-      return 1;
+  if (src) {
+    if (dst) {
+      xbt_dynar_foreach(src->tasks_after,counter,dependency) {
+        if (dependency->dst == dst)
+          return 1;
+      }
+    } else {
+      return xbt_dynar_length(src->tasks_after);
+    }
+  } else {
+    return xbt_dynar_length(dst->tasks_before);
   }
   return 0;
 }
@@ -413,7 +504,7 @@ void SD_task_dependency_remove(SD_task_t src, SD_task_t dst)
   if (xbt_dynar_length(dst->tasks_before) == 0 && __SD_task_is_scheduled(dst))
     __SD_task_set_state(dst, SD_READY);
 
-  /*  __SD_print_dependencies(src); 
+  /*  __SD_print_dependencies(src);
      __SD_print_dependencies(dst); */
 }
 
@@ -476,7 +567,7 @@ static void __SD_print_watch_points(SD_task_t task)
  * SD_simulate() will stop as soon as the \ref e_SD_task_state_t "state" of this
  * task becomes the one given in argument. The
  * watch point is then automatically removed.
- * 
+ *
  * \param task a task
  * \param state the \ref e_SD_task_state_t "state" you want to watch
  * (cannot be #SD_NOT_SCHEDULED)
@@ -497,7 +588,7 @@ void SD_task_watch(SD_task_t task, e_SD_task_state_t state)
 
 /**
  * \brief Removes a watch point from a task
- * 
+ *
  * \param task a task
  * \param state the \ref e_SD_task_state_t "state" you no longer want to watch
  * \see SD_task_watch()
@@ -515,10 +606,10 @@ void SD_task_unwatch(SD_task_t task, e_SD_task_state_t state)
 
 /**
  * \brief Returns an approximative estimation of the execution time of a task.
- * 
+ *
  * The estimation is very approximative because the value returned is the time
  * the task would take if it was executed now and if it was the only task.
- * 
+ *
  * \param task the task to evaluate
  * \param workstation_nb number of workstations on which the task would be executed
  * \param workstation_list the workstations on which the task would be executed
@@ -563,6 +654,19 @@ double SD_task_get_execution_time(SD_task_t task,
   }
   return max_time * SD_task_get_amount(task);
 }
+static inline void SD_task_do_schedule(SD_task_t task) {
+  SD_CHECK_INIT_DONE();
+
+   if (!__SD_task_is_not_scheduled(task))
+     THROW1(arg_error, 0, "Task '%s' has already been scheduled",
+            SD_task_get_name(task));
+
+ /* update the task state */
+  if (xbt_dynar_length(task->tasks_before) == 0)
+    __SD_task_set_state(task, SD_READY);
+  else
+    __SD_task_set_state(task, SD_SCHEDULED);
+}
 
 /**
  * \brief Schedules a task
@@ -570,7 +674,7 @@ double SD_task_get_execution_time(SD_task_t task,
  * The task state must be #SD_NOT_SCHEDULED.
  * Once scheduled, a task will be executed as soon as possible in SD_simulate(),
  * i.e. when its dependencies are satisfied.
- * 
+ *
  * \param task the task you want to schedule
  * \param workstation_nb number of workstations on which the task will be executed
  * \param workstation_list the workstations on which the task will be executed
@@ -579,45 +683,33 @@ double SD_task_get_execution_time(SD_task_t task,
  * \param rate task execution speed rate
  * \see SD_task_unschedule()
  */
-void SD_task_schedule(SD_task_t task, int workstation_nb,
+void SD_task_schedule(SD_task_t task, int workstation_count,
                       const SD_workstation_t * workstation_list,
                       const double *computation_amount,
                       const double *communication_amount, double rate)
 {
+  xbt_assert0(workstation_count > 0, "workstation_nb must be positive");
 
   int communication_nb;
 
-  SD_CHECK_INIT_DONE();
-  xbt_assert0(task != NULL, "Invalid parameter");
-  xbt_assert0(workstation_nb > 0, "workstation_nb must be positive");
-
-  if (!__SD_task_is_not_scheduled(task))
-    THROW1(arg_error, 0, "Task '%s' has already been scheduled",
-           SD_task_get_name(task));
-
-  task->workstation_nb = workstation_nb;
+  task->workstation_nb = workstation_count;
   task->rate = rate;
 
-  task->computation_amount = xbt_new(double, workstation_nb);
+  task->computation_amount = xbt_new(double, workstation_count);
   memcpy(task->computation_amount, computation_amount,
-         sizeof(double) * workstation_nb);
+         sizeof(double) * workstation_count);
 
-  communication_nb = workstation_nb * workstation_nb;
+  communication_nb = workstation_count * workstation_count;
   task->communication_amount = xbt_new(double, communication_nb);
   memcpy(task->communication_amount, communication_amount,
          sizeof(double) * communication_nb);
 
-  task->workstation_list = xbt_new(SD_workstation_t, workstation_nb);
+  task->workstation_list = xbt_new(SD_workstation_t, workstation_count);
   memcpy(task->workstation_list, workstation_list,
-         sizeof(SD_workstation_t) * workstation_nb);
+         sizeof(SD_workstation_t) * workstation_count);
 
-  /* update the task state */
-  if (xbt_dynar_length(task->tasks_before) == 0)
-    __SD_task_set_state(task, SD_READY);
-  else
-    __SD_task_set_state(task, SD_SCHEDULED);
+  SD_task_do_schedule(task);
 }
-
 /**
  * \brief Unschedules a task
  *
@@ -645,7 +737,7 @@ void SD_task_unschedule(SD_task_t task)
     __SD_task_destroy_scheduling_data(task);
 
   if (__SD_task_is_running(task))       /* the task should become SD_FAILED */
-    surf_workstation_model->common_public->action_cancel(task->surf_action);
+    surf_workstation_model->action_cancel(task->surf_action);
   else
     __SD_task_set_state(task, SD_NOT_SCHEDULED);
   task->remains = task->amount;
@@ -713,18 +805,14 @@ void __SD_task_really_run(SD_task_t task)
   task->surf_action = NULL;
   if ((task->workstation_nb == 1) && (task->communication_amount[0] == 0.0)) {
     task->surf_action =
-      surf_workstation_model->extension_public->execute(surf_workstations[0],
-                                                        task->
-                                                        computation_amount
-                                                        [0]);
+      surf_workstation_model->extension.
+      workstation.execute(surf_workstations[0], task->computation_amount[0]);
   } else if ((task->workstation_nb == 1)
              && (task->computation_amount[0] == 0.0)) {
     task->surf_action =
-      surf_workstation_model->
-      extension_public->communicate(surf_workstations[0],
-                                    surf_workstations[0],
-                                    task->communication_amount[0],
-                                    task->rate);
+      surf_workstation_model->extension.
+      workstation.communicate(surf_workstations[0], surf_workstations[0],
+                              task->communication_amount[0], task->rate);
   } else if ((task->workstation_nb == 2)
              && (task->computation_amount[0] == 0.0)
              && (task->computation_amount[1] == 0.0)) {
@@ -739,10 +827,9 @@ void __SD_task_really_run(SD_task_t task)
     }
     if (nb == 1) {
       task->surf_action =
-        surf_workstation_model->
-        extension_public->communicate(surf_workstations[0],
-                                      surf_workstations[1], value,
-                                      task->rate);
+        surf_workstation_model->extension.
+        workstation.communicate(surf_workstations[0], surf_workstations[1],
+                                value, task->rate);
     }
   }
   if (!task->surf_action) {
@@ -756,21 +843,16 @@ void __SD_task_really_run(SD_task_t task)
            sizeof(double) * task->workstation_nb * task->workstation_nb);
 
     task->surf_action =
-      surf_workstation_model->extension_public->execute_parallel_task(task->
-                                                                      workstation_nb,
-                                                                      surf_workstations,
-                                                                      computation_amount,
-                                                                      communication_amount,
-                                                                      task->
-                                                                      amount,
-                                                                      task->
-                                                                      rate);
+      surf_workstation_model->extension.
+      workstation.execute_parallel_task(task->workstation_nb,
+                                        surf_workstations, computation_amount,
+                                        communication_amount, task->amount,
+                                        task->rate);
   } else {
     xbt_free(surf_workstations);
   }
 
-  surf_workstation_model->common_public->action_set_data(task->surf_action,
-                                                         task);
+  surf_workstation_model->action_data_set(task->surf_action, task);
 
   DEBUG1("surf_action = %p", task->surf_action);
 
@@ -854,7 +936,7 @@ void __SD_task_just_done(SD_task_t task)
   candidates = xbt_new(SD_task_t, 8);
 
   __SD_task_set_state(task, SD_DONE);
-  surf_workstation_model->common_public->action_free(task->surf_action);
+  surf_workstation_model->action_unref(task->surf_action);
   task->surf_action = NULL;
 
   DEBUG0("Looking for candidates");
@@ -1008,8 +1090,7 @@ double SD_task_get_start_time(SD_task_t task)
   SD_CHECK_INIT_DONE();
   xbt_assert0(task != NULL, "Invalid parameter");
   if (task->surf_action)
-    return surf_workstation_model->common_public->action_get_start_time(task->
-                                                                        surf_action);
+    return surf_workstation_model->action_get_start_time(task->surf_action);
   else
     return task->start_time;
 }
@@ -1019,7 +1100,7 @@ double SD_task_get_start_time(SD_task_t task)
  *
  * The task state must be SD_RUNNING, SD_DONE or SD_FAILED.
  * If the state is not completed yet, the returned value is an
- * estimation of the task finish time. This value can fluctuate 
+ * estimation of the task finish time. This value can fluctuate
  * until the task is completed.
  *
  * \param task: a task
@@ -1031,8 +1112,7 @@ double SD_task_get_finish_time(SD_task_t task)
   xbt_assert0(task != NULL, "Invalid parameter");
 
   if (task->surf_action)        /* should never happen as actions are destroyed right after their completion */
-    return surf_workstation_model->common_public->
-      action_get_finish_time(task->surf_action);
+    return surf_workstation_model->action_get_finish_time(task->surf_action);
   else
     return task->finish_time;
 }
@@ -1062,7 +1142,7 @@ void SD_task_destroy(SD_task_t task)
     xbt_free(task->name);
 
   if (task->surf_action != NULL)
-    surf_workstation_model->common_public->action_free(task->surf_action);
+    surf_workstation_model->action_unref(task->surf_action);
 
   if (task->workstation_list != NULL)
     xbt_free(task->workstation_list);
@@ -1075,3 +1155,99 @@ void SD_task_destroy(SD_task_t task)
 
   DEBUG0("Task destroyed.");
 }
+
+
+static inline SD_task_t SD_task_create_sized(const char*name,void*data,double amount,int ws_count) {
+  SD_task_t task = SD_task_create(name,data,amount);
+  task->communication_amount = xbt_new0(double,ws_count*ws_count);
+  task->computation_amount = xbt_new0(double,ws_count);
+  task->workstation_nb = ws_count;
+  task->workstation_list = xbt_new0(SD_workstation_t,ws_count);
+  return task;
+}
+/** @brief create a end-to-end communication task that can then be auto-scheduled
+ *
+ * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
+ * allows to specify the task costs at creation, and decorelate them from the
+ * scheduling process where you just specify which resource should deliver the
+ * mandatory power.
+ *
+ * A end-to-end communication must be scheduled on 2 hosts, and the amount
+ * specified at creation is sent from hosts[0] to hosts[1].
+ */
+SD_task_t SD_task_create_comm_e2e(const char*name, void *data, double amount) {
+  SD_task_t res = SD_task_create_sized(name,data,0,2);
+  res->communication_amount[1] = amount;
+  res->kind=SD_TASK_COMM_E2E;
+  return res;
+}
+/** @brief create a sequential computation task that can then be auto-scheduled
+ *
+ * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
+ * allows to specify the task costs at creation, and decorelate them from the
+ * scheduling process where you just specify which resource should deliver the
+ * mandatory power.
+ *
+ * A sequential computation must be scheduled on 1 host, and the amount
+ * specified at creation to be run on hosts[0].
+ */
+SD_task_t SD_task_create_comp_seq(const char*name, void *data, double amount) {
+  SD_task_t res = SD_task_create_sized(name,data,0,1);
+  res->computation_amount[0]=amount;
+  res->kind=SD_TASK_COMP_SEQ;
+  return res;
+}
+
+/** @brief Auto-schedules a task.
+ *
+ * Auto-scheduling mean that the task can be used with SD_task_schedulev(). This
+ * allows to specify the task costs at creation, and decorelate them from the
+ * scheduling process where you just specify which resource should deliver the
+ * mandatory power.
+ *
+ * To be auto-schedulable, a task must be created with SD_task_create_comm_e2e() or
+ * SD_task_create_comp_seq(). Check their definitions for the exact semantic of each
+ * of them.
+ *
+ * @todo
+ * We should create tasks kind for the following categories:
+ *  - Point to point communication (done)
+ *  - Sequential computation       (done)
+ *  - group communication (redistribution, several kinds)
+ *  - parallel tasks with no internal communication (one kind per speedup model such as amdal)
+ *  - idem+ internal communication. Task type not enough since we cannot store comm cost alongside to comp one)
+ */
+void SD_task_schedulev(SD_task_t task, int count, const SD_workstation_t*list) {
+  int i;
+  xbt_assert1(task->kind != 0,"Task %s is not typed. Cannot automatically schedule it.",SD_task_get_name(task));
+  switch(task->kind) {
+  case SD_TASK_COMM_E2E:
+  case SD_TASK_COMP_SEQ:
+    xbt_assert(task->workstation_nb==count);
+    for (i=0;i<count;i++)
+      task->workstation_list[i]=list[i];
+    SD_task_do_schedule(task);
+    break;
+  default:
+    xbt_die(bprintf("Kind of task %s not supported by SD_task_schedulev()",
+          SD_task_get_name(task)));
+  }
+}
+/** @brief autoschedule a task on a list of workstations
+ *
+ * This function is very similar to SD_task_schedulev(),
+ * but takes the list of workstations to schedule onto as separate parameters.
+ * It builds a proper vector of workstations and then call SD_task_schedulev()
+ */
+void SD_task_schedulel(SD_task_t task, int count, ...) {
+  va_list ap;
+  SD_workstation_t *list=xbt_new(SD_workstation_t,count);
+  int i;
+  va_start(ap,count);
+  for (i=0;i<count;i++) {
+      list[i] = va_arg(ap,SD_workstation_t);
+  }
+  va_end(ap);
+  SD_task_schedulev(task,count,list);
+  free(list);
+}