Browse Source

Add some experimental heteroprio batch heuristics

Samuel Thibault 5 years ago
parent
commit
635eb6f440

+ 2 - 0
ChangeLog

@@ -45,6 +45,8 @@ New features:
   * Add 'ready' heuristic to modular schedulers.
   * New modular-heteroprio scheduler.
   * Add STARPU_TASK_SCHED_DATA
+  * Add support for staging schedulers.
+  * New modular-heteroprio-heft scheduler.
 
 Changes:
   * Modification in the Native Fortran interface of the functions

+ 46 - 1
include/starpu_sched_component.h

@@ -373,6 +373,11 @@ void starpu_sched_component_worker_post_exec_hook(struct starpu_task *task, unsi
 */
 
 /**
+   default function for the pull component method, just call pull of parents until one of them returns a task
+*/
+struct starpu_task * starpu_sched_component_parents_pull_task(struct starpu_sched_component * component, struct starpu_sched_component * to);
+
+/**
    default function for the can_push component method, just call can_push of parents until one of them returns non-zero
 */
 int starpu_sched_component_can_push(struct starpu_sched_component * component, struct starpu_sched_component * to);
@@ -557,7 +562,13 @@ int starpu_sched_component_is_heft(struct starpu_sched_component *component);
    @{
 */
 
-struct starpu_sched_component * starpu_sched_component_heteroprio_create(struct starpu_sched_tree *tree, struct starpu_sched_component_mct_data * params) STARPU_ATTRIBUTE_MALLOC;
+struct starpu_sched_component_heteroprio_data
+{
+	struct starpu_sched_component_mct_data *mct;
+	unsigned batch;
+};
+
+struct starpu_sched_component * starpu_sched_component_heteroprio_create(struct starpu_sched_tree *tree, struct starpu_sched_component_heteroprio_data * params) STARPU_ATTRIBUTE_MALLOC;
 int starpu_sched_component_is_heteroprio(struct starpu_sched_component *component);
 
 /** @} */
@@ -594,6 +605,26 @@ int starpu_sched_component_is_perfmodel_select(struct starpu_sched_component *co
 /** @} */
 
 /**
+   @name Staged pull Component API
+   @{
+*/
+
+struct starpu_sched_component * starpu_sched_component_stage_create(struct starpu_sched_tree *tree, void *arg) STARPU_ATTRIBUTE_MALLOC;
+int starpu_sched_component_is_stage(struct starpu_sched_component *component);
+
+/** @} */
+
+/**
+   @name User-choice push Component API
+   @{
+*/
+
+struct starpu_sched_component * starpu_sched_component_userchoice_create(struct starpu_sched_tree *tree, void *arg) STARPU_ATTRIBUTE_MALLOC;
+int starpu_sched_component_is_userchoice(struct starpu_sched_component *component);
+
+/** @} */
+
+/**
    @name Recipe Component API
    @{
 */
@@ -756,6 +787,20 @@ struct starpu_sched_tree *starpu_sched_component_make_scheduler(unsigned sched_c
 */
 void starpu_sched_component_initialize_simple_scheduler(starpu_sched_component_create_t create_decision_component, void *data, unsigned flags, unsigned sched_ctx_id);
 
+/**
+   Create a simple modular scheduler tree around several scheduling decision-making
+   components. The parameters are similar to
+   starpu_sched_component_initialize_simple_scheduler, but per scheduling decision, for instance:
+
+   starpu_sched_component_initialize_simple_schedulers(sched_ctx_id, 2,
+     create1, data1, flags1,
+     create2, data2, flags2);
+
+   The different flags parameters must be coherent: same decision flags. They
+   must not include the perfmodel flag (not supported yet).
+*/
+void starpu_sched_component_initialize_simple_schedulers(unsigned sched_ctx_id, unsigned ndecisions, ...);
+
 /** @} */
 
 #define STARPU_COMPONENT_MUTEX_LOCK(m) \

+ 3 - 0
src/Makefile.am

@@ -292,6 +292,8 @@ libstarpu_@STARPU_EFFECTIVE_VERSION@_la_SOURCES = 		\
 	sched_policies/component_perfmodel_select.c				\
 	sched_policies/component_composed.c				\
 	sched_policies/component_work_stealing.c				\
+	sched_policies/component_stage.c				\
+	sched_policies/component_userchoice.c				\
 	sched_policies/modular_eager.c				\
 	sched_policies/modular_eager_prio.c				\
 	sched_policies/modular_eager_prefetching.c				\
@@ -305,6 +307,7 @@ libstarpu_@STARPU_EFFECTIVE_VERSION@_la_SOURCES = 		\
 	sched_policies/modular_heft.c				\
 	sched_policies/modular_heft_prio.c			\
 	sched_policies/modular_heteroprio.c			\
+	sched_policies/modular_heteroprio_heft.c		\
 	sched_policies/modular_heft2.c				\
 	sched_policies/modular_ws.c				\
 	sched_policies/modular_ez.c

+ 1 - 0
src/core/sched_policy.c

@@ -71,6 +71,7 @@ static struct starpu_sched_policy *predefined_policies[] =
 	&_starpu_sched_modular_heft_prio_policy,
 	&_starpu_sched_modular_heft2_policy,
 	&_starpu_sched_modular_heteroprio_policy,
+	&_starpu_sched_modular_heteroprio_heft_policy,
 	&_starpu_sched_modular_parallel_heft_policy,
 	&_starpu_sched_eager_policy,
 	&_starpu_sched_prio_policy,

+ 1 - 0
src/core/sched_policy.h

@@ -108,6 +108,7 @@ extern struct starpu_sched_policy _starpu_sched_modular_heft_policy;
 extern struct starpu_sched_policy _starpu_sched_modular_heft_prio_policy;
 extern struct starpu_sched_policy _starpu_sched_modular_heft2_policy;
 extern struct starpu_sched_policy _starpu_sched_modular_heteroprio_policy;
+extern struct starpu_sched_policy _starpu_sched_modular_heteroprio_heft_policy;
 extern struct starpu_sched_policy _starpu_sched_modular_parallel_heft_policy;
 extern struct starpu_sched_policy _starpu_sched_graph_test_policy;
 extern struct starpu_sched_policy _starpu_sched_tree_heft_hierarchical_policy;

+ 68 - 21
src/sched_policies/component_heteroprio.c

@@ -47,13 +47,15 @@ struct _starpu_heteroprio_data
 	starpu_pthread_mutex_t mutex;
 
 	struct _starpu_mct_data *mct_data;
+
+	unsigned batch;
 };
 
 static int heteroprio_progress_accel(struct starpu_sched_component *component, struct _starpu_heteroprio_data *data, enum starpu_worker_archtype archtype, int front)
 {
 	struct starpu_task *task = NULL;
 	starpu_pthread_mutex_t * mutex = &data->mutex;
-	int j, ret;
+	int j, ret = 1;
 	double acceleration = INFINITY;
 
 	struct _starpu_mct_data * d = data->mct_data;
@@ -71,7 +73,10 @@ static int heteroprio_progress_accel(struct starpu_sched_component *component, s
 		/* Pick up accelerated tasks last */
 		for (j = (int) data->naccel-1; j >= 0; j--)
 		{
-			task = _starpu_prio_deque_pop_task(data->bucket[j]);
+			if (data->batch && 0)
+				task = _starpu_prio_deque_pop_back_task(data->bucket[j]);
+			else
+				task = _starpu_prio_deque_pop_task(data->bucket[j]);
 			if (task)
 				break;
 		}
@@ -87,6 +92,11 @@ static int heteroprio_progress_accel(struct starpu_sched_component *component, s
 	if (!task)
 		return 1;
 
+	if (data->batch)
+		/* In batch mode the fifos below do not use priorities. Do not
+		 * leak a priority for the data prefetches either */
+		task->priority = INT_MAX;
+
 	/* TODO: we might want to prefer to pick up a task whose data is already on some GPU */
 
 	struct starpu_sched_component * best_component;
@@ -111,6 +121,34 @@ static int heteroprio_progress_accel(struct starpu_sched_component *component, s
 			estimated_transfer_length,
 			suitable_components);
 
+	if (data->batch && 0)
+	{
+		/* In batch mode, we may want to insist on filling workers with tasks
+		 * by ignoring when other workers would finish this. */
+
+		unsigned i;
+		for (i = 0; i < component->nchildren; i++)
+		{
+			int idworker;
+			for(idworker = starpu_bitmap_first(component->children[i]->workers);
+				idworker != -1;
+				idworker = starpu_bitmap_next(component->children[i]->workers, idworker))
+			{
+				if (starpu_worker_get_type(idworker) == archtype)
+					break;
+			}
+
+			if (idworker == -1)
+			{
+				/* Not the targetted arch, avoid it */
+
+				/* XXX: INFINITY doesn't seem to be working properly */
+				estimated_lengths[i] = 1000000000;
+				estimated_transfer_length[i] = 1000000000;
+			}
+		}
+	}
+
 	/* Entering critical section to make sure no two workers
 	   make scheduling decisions at the same time */
 	STARPU_COMPONENT_MUTEX_LOCK(&d->scheduling_mutex);
@@ -131,6 +169,9 @@ static int heteroprio_progress_accel(struct starpu_sched_component *component, s
 			min_exp_end_with_task, max_exp_end_with_task,
 			suitable_components, nsuitable_components);
 
+	if (best_icomponent == -1)
+		goto out;
+
 	best_component = component->children[best_icomponent];
 
 	int idworker;
@@ -143,27 +184,25 @@ static int heteroprio_progress_accel(struct starpu_sched_component *component, s
 	}
 
 	if (idworker == -1)
-	{
-		STARPU_COMPONENT_MUTEX_UNLOCK(&d->scheduling_mutex);
-	}
-	else
-	{
-		/* Ok, we do have a worker there of that type, try to push it there. */
-		STARPU_ASSERT(!starpu_sched_component_is_worker(best_component));
-		starpu_sched_task_break(task);
-		ret = starpu_sched_component_push_task(component,best_component,task);
+		goto out;
+
+	/* Ok, we do have a worker there of that type, try to push it there. */
+	STARPU_ASSERT(!starpu_sched_component_is_worker(best_component));
+	starpu_sched_task_break(task);
+	ret = starpu_sched_component_push_task(component,best_component,task);
 
-		/* I can now exit the critical section: Pushing the task above ensures that its execution
-		   time will be taken into account for subsequent scheduling decisions */
+	/* I can now exit the critical section: Pushing the task above ensures that its execution
+	   time will be taken into account for subsequent scheduling decisions */
+	if (!ret)
+	{
 		STARPU_COMPONENT_MUTEX_UNLOCK(&d->scheduling_mutex);
-		if (!ret)
-		{
-			//fprintf(stderr, "pushed %p to %d\n", task, best_icomponent);
-			/* Great! */
-			return 0;
-		}
+		//fprintf(stderr, "pushed %p to %d\n", task, best_icomponent);
+		/* Great! */
+		return 0;
 	}
 
+out:
+	STARPU_COMPONENT_MUTEX_UNLOCK(&d->scheduling_mutex);
 	/* No such kind of worker there, or it refused our task, abort */
 
 	//fprintf(stderr, "could not push %p to %d actually\n", task, best_icomponent);
@@ -311,6 +350,10 @@ static int heteroprio_push_task(struct starpu_sched_component * component, struc
 	double min_expected = INFINITY, max_expected = -INFINITY;
 	double acceleration;
 
+	if (data->batch && 0)
+		/* Batch mode, we may want to ignore priorities completely */
+		task->priority = INT_MAX;
+
 	/* Compute acceleration between best-performing arch and least-performing arch */
 	int workerid;
 	for(workerid = starpu_bitmap_first(component->workers_in_ctx);
@@ -466,10 +509,10 @@ int starpu_sched_component_is_heteroprio(struct starpu_sched_component * compone
 	return component->push_task == heteroprio_push_task;
 }
 
-struct starpu_sched_component * starpu_sched_component_heteroprio_create(struct starpu_sched_tree *tree, struct starpu_sched_component_mct_data * params)
+struct starpu_sched_component * starpu_sched_component_heteroprio_create(struct starpu_sched_tree *tree, struct starpu_sched_component_heteroprio_data * params)
 {
 	struct starpu_sched_component * component = starpu_sched_component_create(tree, "heteroprio");
-	struct _starpu_mct_data *mct_data = starpu_mct_init_parameters(params);
+	struct _starpu_mct_data *mct_data = starpu_mct_init_parameters(params ? params->mct : NULL);
 	struct _starpu_heteroprio_data *data;
 	_STARPU_MALLOC(data, sizeof(*data));
 
@@ -480,6 +523,10 @@ struct starpu_sched_component * starpu_sched_component_heteroprio_create(struct
 	STARPU_PTHREAD_MUTEX_INIT(&data->mutex,NULL);
 	data->mct_data = mct_data;
 	STARPU_PTHREAD_MUTEX_INIT(&mct_data->scheduling_mutex,NULL);
+	if (params)
+		data->batch = params->batch;
+	else
+		data->batch = 1;
 	component->data = data;
 
 	component->push_task = heteroprio_push_task;

+ 1 - 1
src/sched_policies/component_sched.c

@@ -562,7 +562,7 @@ static void starpu_sched_component_remove_parent(struct starpu_sched_component *
 /* default implementation for component->pull_task()
  * just perform a recursive call on parent
  */
-static struct starpu_task * starpu_sched_component_parents_pull_task(struct starpu_sched_component * component, struct starpu_sched_component * to STARPU_ATTRIBUTE_UNUSED)
+struct starpu_task * starpu_sched_component_parents_pull_task(struct starpu_sched_component * component, struct starpu_sched_component * to STARPU_ATTRIBUTE_UNUSED)
 {
 	STARPU_ASSERT(component);
 	struct starpu_task * task = NULL;

+ 56 - 0
src/sched_policies/component_userchoice.c

@@ -0,0 +1,56 @@
+/* StarPU --- Runtime system for heterogeneous multicore architectures.
+ *
+ * Copyright (C) 2019                                     Université de Bordeaux
+ *
+ * StarPU is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * StarPU is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU Lesser General Public License in COPYING.LGPL for more details.
+ */
+
+/* This component uses (uintptr_t) tasks->sched_data as the child number it
+ * should push its tasks to. It can thus be used to let the user choose which
+ * scheduler a task should go to. */
+
+#include <starpu_sched_component.h>
+#include <starpu_scheduler.h>
+
+static int userchoice_push_task(struct starpu_sched_component * component, struct starpu_task * task)
+{
+	unsigned target = (uintptr_t) task->sched_data;
+	STARPU_ASSERT(target < component->nchildren);
+	return starpu_sched_component_push_task(component, component->children[target], task);
+}
+
+static struct starpu_task * userchoice_pull_task(struct starpu_sched_component * component, struct starpu_sched_component * to STARPU_ATTRIBUTE_UNUSED)
+{
+	_STARPU_DISP("stage component is not supposed to be pull from...\n");
+	return starpu_sched_component_parents_pull_task(component, to);
+}
+
+int userchoice_can_pull(struct starpu_sched_component * component)
+{
+	_STARPU_DISP("stage component is not supposed to be pull from...\n");
+	return starpu_sched_component_can_pull(component);
+}
+
+int starpu_sched_component_is_userchoice(struct starpu_sched_component * component)
+{
+	return component->push_task == userchoice_push_task;
+}
+
+struct starpu_sched_component * starpu_sched_component_userchoice_create(struct starpu_sched_tree *tree, void *args STARPU_ATTRIBUTE_UNUSED)
+{
+	struct starpu_sched_component *component = starpu_sched_component_create(tree, "userchoice");
+	component->push_task = userchoice_push_task;
+	component->pull_task = userchoice_pull_task;
+	component->can_pull = userchoice_can_pull;
+
+	return component;
+}

+ 218 - 146
src/sched_policies/modular_ez.c

@@ -50,46 +50,81 @@
 #define _STARPU_SCHED_NTASKS_THRESHOLD_DEFAULT 2
 #define _STARPU_SCHED_EXP_LEN_THRESHOLD_DEFAULT 1000000000.0
 
-void starpu_sched_component_initialize_simple_scheduler(starpu_sched_component_create_t create_decision_component, void *data, unsigned flags, unsigned sched_ctx_id)
+void starpu_sched_component_initialize_simple_schedulers(unsigned sched_ctx_id, unsigned ndecisions, ...)
 {
 	struct starpu_sched_tree * t;
 	struct starpu_sched_component *last = NULL;	/* Stores the last created component, from top to bottom */
 	unsigned i, j, n;
+	struct starpu_sched_component *userchoice_component = NULL;
 	struct starpu_sched_component *decision_component = NULL;
 	struct starpu_sched_component *no_perfmodel_component = NULL;
 	struct starpu_sched_component *calibrator_component = NULL;
+	unsigned sched;
+	va_list varg_list;
+	unsigned decide_flags;
+	unsigned flags;
 
 	/* Start building the tree */
 	t = starpu_sched_tree_create(sched_ctx_id);
 	t->root = NULL;
 	starpu_sched_ctx_set_policy_data(sched_ctx_id, (void*)t);
 
-	/* Create combined workers if requested */
-	if (flags & STARPU_SCHED_SIMPLE_COMBINED_WORKERS)
-		starpu_sched_find_all_worker_combinations();
-
-	/* Components parameters */
+	STARPU_ASSERT(ndecisions >= 1);
 
-	if (flags & STARPU_SCHED_SIMPLE_FIFO_ABOVE_PRIO || flags & STARPU_SCHED_SIMPLE_FIFOS_BELOW_PRIO)
+	if (ndecisions != 1)
 	{
-		/* The application may use any integer */
-		if (starpu_sched_ctx_min_priority_is_set(sched_ctx_id) == 0)
-			starpu_sched_ctx_set_min_priority(sched_ctx_id, INT_MIN);
-		if (starpu_sched_ctx_max_priority_is_set(sched_ctx_id) == 0)
-			starpu_sched_ctx_set_max_priority(sched_ctx_id, INT_MAX);
+		/* Take choice between schedulers from user */
+		userchoice_component = starpu_sched_component_userchoice_create(t, NULL);
+		t->root = userchoice_component;
 	}
 
-	/* See what the component will decide */
+
 	unsigned nbelow;
 	unsigned nummaxids;
-	nummaxids = starpu_worker_get_count() + starpu_combined_worker_get_count();
-	if (starpu_memory_nodes_get_count() > nummaxids)
-		nummaxids = starpu_memory_nodes_get_count();
-	if (STARPU_ANY_WORKER > nummaxids)
-		nummaxids = STARPU_ANY_WORKER;
+
+	va_start(varg_list, ndecisions);
+	for (sched = 0; sched < ndecisions; sched++)
+	{
+		last = userchoice_component;
+
+		starpu_sched_component_create_t create_decision_component = va_arg(varg_list, starpu_sched_component_create_t);
+		void *data = va_arg(varg_list, void *);
+		flags = va_arg(varg_list, unsigned);
+		(void) create_decision_component;
+		(void) data;
+
+		/* Create combined workers if requested */
+		if (flags & STARPU_SCHED_SIMPLE_COMBINED_WORKERS)
+			starpu_sched_find_all_worker_combinations();
+
+		/* Components parameters */
+
+		if (flags & STARPU_SCHED_SIMPLE_FIFO_ABOVE_PRIO || flags & STARPU_SCHED_SIMPLE_FIFOS_BELOW_PRIO)
+		{
+			/* The application may use any integer */
+			if (starpu_sched_ctx_min_priority_is_set(sched_ctx_id) == 0)
+				starpu_sched_ctx_set_min_priority(sched_ctx_id, INT_MIN);
+			if (starpu_sched_ctx_max_priority_is_set(sched_ctx_id) == 0)
+				starpu_sched_ctx_set_max_priority(sched_ctx_id, INT_MAX);
+		}
+
+		/* See what the component will decide */
+		nummaxids = starpu_worker_get_count() + starpu_combined_worker_get_count();
+		if (starpu_memory_nodes_get_count() > nummaxids)
+			nummaxids = starpu_memory_nodes_get_count();
+		if (STARPU_ANY_WORKER > nummaxids)
+			nummaxids = STARPU_ANY_WORKER;
+
+		if (sched == 0)
+			decide_flags = flags & STARPU_SCHED_SIMPLE_DECIDE_MASK;
+		else
+			STARPU_ASSERT(decide_flags == (flags & STARPU_SCHED_SIMPLE_DECIDE_MASK));
+	}
+	va_end(varg_list);
+
 	unsigned below_id[nummaxids];
 
-	switch (flags & STARPU_SCHED_SIMPLE_DECIDE_MASK)
+	switch (decide_flags)
 	{
 		case STARPU_SCHED_SIMPLE_DECIDE_WORKERS:
 			/* Count workers */
@@ -134,162 +169,193 @@ void starpu_sched_component_initialize_simple_scheduler(starpu_sched_component_c
 	}
 	STARPU_ASSERT(nbelow > 0);
 
-	if (nbelow == 1)
-	{
-		/* Oh, no choice, we don't actually need to decide, just
-		 * use an eager scheduler */
-		decision_component = starpu_sched_component_eager_create(t, NULL);
-                /* But make sure we have a fifo above it, fifos below it would
-                 * possibly refuse tasks out of available room */
-		flags |= STARPU_SCHED_SIMPLE_FIFO_ABOVE;
-	}
-	else
+	struct starpu_sched_component *last_below[nbelow];
+	memset(&last_below, 0, sizeof(last_below));
+
+	if (ndecisions != 1)
 	{
-		decision_component = create_decision_component(t, data);
+		/* Will need to stage pulls, create one per choice */
+		for (i = 0; i < nbelow; i++)
+			last_below[i] = starpu_sched_component_stage_create(t, NULL);
 	}
 
-	/* First, a fifo if requested */
-	if (flags & STARPU_SCHED_SIMPLE_FIFO_ABOVE)
+	va_start(varg_list, ndecisions);
+	for (sched = 0; sched < ndecisions; sched++)
 	{
-		struct starpu_sched_component *fifo_above;
-		if (flags & STARPU_SCHED_SIMPLE_FIFO_ABOVE_PRIO)
+		last = userchoice_component;
+
+		starpu_sched_component_create_t create_decision_component = va_arg(varg_list, starpu_sched_component_create_t);
+		void *data = va_arg(varg_list, void *);
+		flags = va_arg(varg_list, unsigned);
+
+		if (nbelow == 1)
 		{
-			fifo_above = starpu_sched_component_prio_create(t, NULL);
+			/* Oh, no choice, we don't actually need to decide, just
+			 * use an eager scheduler */
+			decision_component = starpu_sched_component_eager_create(t, NULL);
+			/* But make sure we have a fifo above it, fifos below it would
+			 * possibly refuse tasks out of available room */
+			flags |= STARPU_SCHED_SIMPLE_FIFO_ABOVE;
 		}
 		else
 		{
-			fifo_above = starpu_sched_component_fifo_create(t, NULL);
+			decision_component = create_decision_component(t, data);
 		}
-		last = t->root = fifo_above;
-	}
 
-	/* Then, perfmodel calibration if requested, and plug the scheduling decision-making component to it */
-	if (flags & STARPU_SCHED_SIMPLE_PERFMODEL)
-	{
-		no_perfmodel_component = starpu_sched_component_eager_create(t, NULL);
-		calibrator_component = starpu_sched_component_eager_calibration_create(t, NULL);
-
-		struct starpu_sched_component_perfmodel_select_data perfmodel_select_data =
+		/* First, a fifo if requested */
+		if (flags & STARPU_SCHED_SIMPLE_FIFO_ABOVE)
+		{
+			struct starpu_sched_component *fifo_above;
+			if (flags & STARPU_SCHED_SIMPLE_FIFO_ABOVE_PRIO)
 			{
-				.calibrator_component = calibrator_component,
-				.no_perfmodel_component = no_perfmodel_component,
-				.perfmodel_component = decision_component,
-			};
-
-		struct starpu_sched_component * perfmodel_select_component = starpu_sched_component_perfmodel_select_create(t, &perfmodel_select_data);
-
-		if (!t->root)
-			t->root = perfmodel_select_component;
-		else
-			starpu_sched_component_connect(last, perfmodel_select_component);
+				fifo_above = starpu_sched_component_prio_create(t, NULL);
+			}
+			else
+			{
+				fifo_above = starpu_sched_component_fifo_create(t, NULL);
+			}
+			if (!last)
+				last = t->root = fifo_above;
+			else
+			{
+				starpu_sched_component_connect(last, fifo_above);
+				last = fifo_above;
+			}
+		}
 
-		starpu_sched_component_connect(perfmodel_select_component, decision_component);
-		starpu_sched_component_connect(perfmodel_select_component, calibrator_component);
-		starpu_sched_component_connect(perfmodel_select_component, no_perfmodel_component);
-	}
-	else
-	{
-		/* No perfmodel calibration */
-		if (!t->root)
-			/* Plug decision_component directly */
-			t->root = decision_component;
-		else
-			/* Plug decision_component to fifo */
-			starpu_sched_component_connect(last, decision_component);
-	}
+		/* Then, perfmodel calibration if requested, and plug the scheduling decision-making component to it */
+		if (flags & STARPU_SCHED_SIMPLE_PERFMODEL)
+		{
+			no_perfmodel_component = starpu_sched_component_eager_create(t, NULL);
+			calibrator_component = starpu_sched_component_eager_calibration_create(t, NULL);
 
-	/* Take default ntasks_threshold */
-	unsigned ntasks_threshold;
-	if (starpu_sched_component_is_heft(decision_component) ||
-	    starpu_sched_component_is_mct(decision_component) ||
-	    starpu_sched_component_is_heteroprio(decision_component)) {
-		/* These need more queueing to allow CPUs to take some share of the work */
-		ntasks_threshold = _STARPU_SCHED_NTASKS_THRESHOLD_HEFT;
-	} else {
-		ntasks_threshold = _STARPU_SCHED_NTASKS_THRESHOLD_DEFAULT;
-	}
-	/* But let user tune it */
-	ntasks_threshold = starpu_get_env_number_default("STARPU_NTASKS_THRESHOLD", ntasks_threshold);
+			struct starpu_sched_component_perfmodel_select_data perfmodel_select_data =
+				{
+					.calibrator_component = calibrator_component,
+					.no_perfmodel_component = no_perfmodel_component,
+					.perfmodel_component = decision_component,
+				};
 
-	double exp_len_threshold = _STARPU_SCHED_EXP_LEN_THRESHOLD_DEFAULT;
-	exp_len_threshold = starpu_get_env_float_default("STARPU_EXP_LEN_THRESHOLD", exp_len_threshold);
+			struct starpu_sched_component * perfmodel_select_component = starpu_sched_component_perfmodel_select_create(t, &perfmodel_select_data);
 
-	int ready = starpu_get_env_number_default("STARPU_SCHED_READY", flags & STARPU_SCHED_SIMPLE_FIFOS_BELOW_READY ? 1 : 0);
+			if (!last)
+				last = t->root = perfmodel_select_component;
+			else
+				starpu_sched_component_connect(last, perfmodel_select_component);
 
-	struct starpu_sched_component_prio_data prio_data =
+			starpu_sched_component_connect(perfmodel_select_component, decision_component);
+			starpu_sched_component_connect(perfmodel_select_component, calibrator_component);
+			starpu_sched_component_connect(perfmodel_select_component, no_perfmodel_component);
+		}
+		else
 		{
-			.ntasks_threshold = ntasks_threshold,
-			.exp_len_threshold = exp_len_threshold,
-			.ready = ready,
-		};
+			/* No perfmodel calibration */
+			if (!last)
+				/* Plug decision_component directly */
+				last = t->root = decision_component;
+			else
+				/* Plug decision_component to fifo */
+				starpu_sched_component_connect(last, decision_component);
+		}
 
-	struct starpu_sched_component_fifo_data fifo_data =
-		{
-			.ntasks_threshold = ntasks_threshold,
-			.exp_len_threshold = exp_len_threshold,
-			.ready = ready,
-		};
+		/* Take default ntasks_threshold */
+		unsigned ntasks_threshold;
+		if (starpu_sched_component_is_heft(decision_component) ||
+		    starpu_sched_component_is_mct(decision_component) ||
+		    starpu_sched_component_is_heteroprio(decision_component)) {
+			/* These need more queueing to allow CPUs to take some share of the work */
+			ntasks_threshold = _STARPU_SCHED_NTASKS_THRESHOLD_HEFT;
+		} else {
+			ntasks_threshold = _STARPU_SCHED_NTASKS_THRESHOLD_DEFAULT;
+		}
+		/* But let user tune it */
+		ntasks_threshold = starpu_get_env_number_default("STARPU_NTASKS_THRESHOLD", ntasks_threshold);
 
+		double exp_len_threshold = _STARPU_SCHED_EXP_LEN_THRESHOLD_DEFAULT;
+		exp_len_threshold = starpu_get_env_float_default("STARPU_EXP_LEN_THRESHOLD", exp_len_threshold);
 
-	/* Create one fifo+eager component pair per choice, below scheduling decision */
-	struct starpu_sched_component *last_below[nbelow];
-	memset(&last_below, 0, sizeof(last_below));
-	for(i = 0; i < nbelow; i++)
-	{
-		last = decision_component;
-		if (flags & STARPU_SCHED_SIMPLE_FIFOS_BELOW
-			&& !((flags & STARPU_SCHED_SIMPLE_DECIDE_MASK) == STARPU_SCHED_SIMPLE_DECIDE_WORKERS
-				&& i >= starpu_worker_get_count()))
-		{
-			struct starpu_sched_component *fifo_below;
-			if (flags & STARPU_SCHED_SIMPLE_FIFOS_BELOW_PRIO)
+		int ready = starpu_get_env_number_default("STARPU_SCHED_READY", flags & STARPU_SCHED_SIMPLE_FIFOS_BELOW_READY ? 1 : 0);
+
+		struct starpu_sched_component_prio_data prio_data =
 			{
-				fifo_below = starpu_sched_component_prio_create(t, &prio_data);
-			}
-			else
+				.ntasks_threshold = ntasks_threshold,
+				.exp_len_threshold = exp_len_threshold,
+				.ready = ready,
+			};
+
+		struct starpu_sched_component_fifo_data fifo_data =
 			{
-				fifo_below = starpu_sched_component_fifo_create(t, &fifo_data);
-			}
-			starpu_sched_component_connect(last, fifo_below);
-			last = fifo_below;
-		}
-		switch (flags & STARPU_SCHED_SIMPLE_DECIDE_MASK)
+				.ntasks_threshold = ntasks_threshold,
+				.exp_len_threshold = exp_len_threshold,
+				.ready = ready,
+			};
+
+		/* Create one fifo+eager component pair per choice, below scheduling decision */
+		for(i = 0; i < nbelow; i++)
 		{
-			case STARPU_SCHED_SIMPLE_DECIDE_WORKERS:
-				/* 1-1 mapping between choice and worker, no need for an eager component */
-				n = 1;
-				break;
-			case STARPU_SCHED_SIMPLE_DECIDE_MEMNODES:
-				n = 0;
-				for (j = 0; j < starpu_worker_get_count() + starpu_combined_worker_get_count(); j++)
-					if (starpu_worker_get_memory_node(j) == below_id[i])
-						n++;
-				break;
-			case STARPU_SCHED_SIMPLE_DECIDE_ARCHS:
-				n = starpu_worker_get_count_by_type(i);
-				break;
-			default:
-				STARPU_ABORT();
-		}
-		STARPU_ASSERT(n >= 1);
-		if (n > 1) {
-			/* Several workers for this choice, need to introduce
-			 * a component to distribute the work */
-			struct starpu_sched_component *distribute;
-			if (flags & STARPU_SCHED_SIMPLE_WS_BELOW)
+			last = decision_component;
+
+			if (flags & STARPU_SCHED_SIMPLE_FIFOS_BELOW
+					&& !(decide_flags == STARPU_SCHED_SIMPLE_DECIDE_WORKERS
+					&& i >= starpu_worker_get_count()))
 			{
-				distribute = starpu_sched_component_work_stealing_create(t, NULL);
+				struct starpu_sched_component *fifo_below;
+				if (flags & STARPU_SCHED_SIMPLE_FIFOS_BELOW_PRIO)
+				{
+					fifo_below = starpu_sched_component_prio_create(t, &prio_data);
+				}
+				else
+				{
+					fifo_below = starpu_sched_component_fifo_create(t, &fifo_data);
+				}
+				starpu_sched_component_connect(last, fifo_below);
+				last = fifo_below;
 			}
-			else
+			switch (decide_flags)
 			{
-				distribute = starpu_sched_component_eager_create(t, NULL);
+				case STARPU_SCHED_SIMPLE_DECIDE_WORKERS:
+					/* 1-1 mapping between choice and worker, no need for an eager component */
+					n = 1;
+					break;
+				case STARPU_SCHED_SIMPLE_DECIDE_MEMNODES:
+					n = 0;
+					for (j = 0; j < starpu_worker_get_count() + starpu_combined_worker_get_count(); j++)
+						if (starpu_worker_get_memory_node(j) == below_id[i])
+							n++;
+					break;
+				case STARPU_SCHED_SIMPLE_DECIDE_ARCHS:
+					n = starpu_worker_get_count_by_type(i);
+					break;
+				default:
+					STARPU_ABORT();
+			}
+			STARPU_ASSERT(n >= 1);
+			if (n > 1) {
+				/* Several workers for this choice, need to introduce
+				 * a component to distribute the work */
+				struct starpu_sched_component *distribute;
+				if (flags & STARPU_SCHED_SIMPLE_WS_BELOW)
+				{
+					distribute = starpu_sched_component_work_stealing_create(t, NULL);
+				}
+				else
+				{
+					distribute = starpu_sched_component_eager_create(t, NULL);
+				}
+
+				starpu_sched_component_connect(last, distribute);
+				last = distribute;
 			}
 
-			starpu_sched_component_connect(last, distribute);
-			last = distribute;
+			if (ndecisions != 1)
+				/* Connect to stage component */
+				starpu_sched_component_connect(last, last_below[i]);
+			else
+				/* Directly let it connected to worker */
+				last_below[i] = last;
 		}
-		last_below[i] = last;
 	}
+	va_end(varg_list);
 
 	/* Finish by creating components per worker */
 	for(i = 0; i < starpu_worker_get_count() + starpu_combined_worker_get_count(); i++)
@@ -308,7 +374,7 @@ void starpu_sched_component_initialize_simple_scheduler(starpu_sched_component_c
 			worker = impl_component;
 		}
 
-		switch (flags & STARPU_SCHED_SIMPLE_DECIDE_MASK)
+		switch (decide_flags)
 		{
 			case STARPU_SCHED_SIMPLE_DECIDE_WORKERS:
 				id = i;
@@ -334,6 +400,7 @@ void starpu_sched_component_initialize_simple_scheduler(starpu_sched_component_c
 		starpu_sched_component_connect(last, worker);
 
 		/* Plug perfmodel calibrator if requested */
+		/* FIXME: this won't work with several scheduling decisions */
 		if (flags & STARPU_SCHED_SIMPLE_PERFMODEL)
 		{
 			starpu_sched_component_connect(no_perfmodel_component, worker);
@@ -345,3 +412,8 @@ void starpu_sched_component_initialize_simple_scheduler(starpu_sched_component_c
 	starpu_sched_tree_update_workers(t);
 	starpu_sched_tree_update_workers_in_ctx(t);
 }
+
+void starpu_sched_component_initialize_simple_scheduler(starpu_sched_component_create_t create_decision_component, void *data, unsigned flags, unsigned sched_ctx_id)
+{
+	starpu_sched_component_initialize_simple_schedulers(sched_ctx_id, 1, create_decision_component, data, flags);
+}

+ 67 - 0
src/sched_policies/modular_heteroprio_heft.c

@@ -0,0 +1,67 @@
+/* StarPU --- Runtime system for heterogeneous multicore architectures.
+ *
+ * Copyright (C) 2013-2015,2017                           Inria
+ * Copyright (C) 2014,2015,2017                           CNRS
+ * Copyright (C) 2013-2015,2017,2018-2019                 Université de Bordeaux
+ * Copyright (C) 2013                                     Simon Archipoff
+ *
+ * StarPU is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * StarPU is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU Lesser General Public License in COPYING.LGPL for more details.
+ */
+
+#include <starpu_sched_component.h>
+#include <starpu_scheduler.h>
+#include <float.h>
+#include <limits.h>
+
+static void initialize_heteroprio_heft_center_policy(unsigned sched_ctx_id)
+{
+	struct starpu_sched_component_heteroprio_data heteroprio_data = {
+		.mct = NULL,
+		.batch = 1,
+	};
+	starpu_sched_component_initialize_simple_schedulers(sched_ctx_id, 2,
+			(starpu_sched_component_create_t) starpu_sched_component_heteroprio_create, &heteroprio_data,
+			STARPU_SCHED_SIMPLE_DECIDE_WORKERS |
+			STARPU_SCHED_SIMPLE_FIFOS_BELOW |
+			STARPU_SCHED_SIMPLE_FIFOS_BELOW_READY |
+			STARPU_SCHED_SIMPLE_IMPL,
+			(starpu_sched_component_create_t) starpu_sched_component_heft_create, NULL,
+			STARPU_SCHED_SIMPLE_DECIDE_WORKERS |
+			STARPU_SCHED_SIMPLE_FIFO_ABOVE |
+			STARPU_SCHED_SIMPLE_FIFO_ABOVE_PRIO |
+			STARPU_SCHED_SIMPLE_FIFOS_BELOW |
+			STARPU_SCHED_SIMPLE_FIFOS_BELOW_PRIO |
+			STARPU_SCHED_SIMPLE_FIFOS_BELOW_READY |
+			STARPU_SCHED_SIMPLE_IMPL);
+}
+
+static void deinitialize_heteroprio_heft_center_policy(unsigned sched_ctx_id)
+{
+	struct starpu_sched_tree *t = (struct starpu_sched_tree*)starpu_sched_ctx_get_policy_data(sched_ctx_id);
+	starpu_sched_tree_destroy(t);
+}
+
+struct starpu_sched_policy _starpu_sched_modular_heteroprio_heft_policy =
+{
+	.init_sched = initialize_heteroprio_heft_center_policy,
+	.deinit_sched = deinitialize_heteroprio_heft_center_policy,
+	.add_workers = starpu_sched_tree_add_workers,
+	.remove_workers = starpu_sched_tree_remove_workers,
+	.push_task = starpu_sched_tree_push_task,
+	.pop_task = starpu_sched_tree_pop_task,
+	.pre_exec_hook = starpu_sched_component_worker_pre_exec_hook,
+	.post_exec_hook = starpu_sched_component_worker_post_exec_hook,
+	.pop_every_task = NULL,
+	.policy_name = "modular-heteroprio-heft",
+	.policy_description = "heteroprio+heft modular policy",
+	.worker_type = STARPU_WORKER_LIST,
+};

+ 10 - 0
src/sched_policies/prio_deque.h

@@ -97,6 +97,16 @@ static inline struct starpu_task * _starpu_prio_deque_pop_task(struct _starpu_pr
 	return task;
 }
 
+static inline struct starpu_task * _starpu_prio_deque_pop_back_task(struct _starpu_prio_deque *pdeque)
+{
+	struct starpu_task *task;
+	if (starpu_task_prio_list_empty(&pdeque->list))
+		return NULL;
+	task = starpu_task_prio_list_pop_back_lowest(&pdeque->list);
+	pdeque->ntasks--;
+	return task;
+}
+
 static inline int _starpu_prio_deque_pop_this_task(struct _starpu_prio_deque *pdeque, int workerid, struct starpu_task *task)
 {
 	unsigned nimpl = 0;