Browse Source

add steering knob preliminary framework

Olivier Aumage 6 years ago
parent
commit
0db52d49ea

+ 2 - 1
Makefile.am

@@ -115,7 +115,8 @@ versinclude_HEADERS = 				\
 	include/starpu_mod.f90			\
 	include/fstarpu_mod.f90			\
 	include/starpu_clusters.h		\
-	include/starpu_perf_monitoring.h
+	include/starpu_perf_monitoring.h	\
+	include/starpu_perf_steering.h
 
 nodist_versinclude_HEADERS = 			\
 	include/starpu_config.h

+ 3 - 0
examples/Makefile.am

@@ -257,6 +257,9 @@ STARPU_EXAMPLES +=				\
 	profiling/profiling			\
 	perf_monitoring/perf_counters_01	\
 	perf_monitoring/perf_counters_02	\
+	perf_steering/perf_knobs_01		\
+	perf_steering/perf_knobs_02		\
+	perf_steering/perf_knobs_03		\
 	scheduler/heteroprio_test		\
 	sched_ctx/sched_ctx			\
 	sched_ctx/sched_ctx_empty		\

+ 131 - 0
examples/perf_steering/perf_knobs_01.c

@@ -0,0 +1,131 @@
+/* StarPU --- Runtime system for heterogeneous multicore architectures.
+ *
+ * Copyright (C) 2019                                     Inria
+ *
+ * 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.h>
+#include <assert.h>
+#include <string.h>
+
+static void print_scope(const enum starpu_perf_knob_scope scope)
+{
+	int nb = starpu_perf_knob_nb(scope);
+	int i;
+	printf("scope %s\n", starpu_perf_knob_scope_id_to_name(scope));
+	for (i=0; i<nb; i++)
+	{
+		const int id = starpu_perf_knob_nth_to_id(scope, i);
+		const char *name = starpu_perf_knob_id_to_name(id);
+		const char *help = starpu_perf_knob_get_help_string(id);
+		int type_id = starpu_perf_knob_get_type_id(id);
+		const char *type_name = starpu_perf_knob_type_id_to_name(type_id);
+		printf("%d/%d - %s (0x%08x): [%s] / %s\n", i+1, nb, name, id, type_name, help);
+	}
+}
+
+int main(int argc, char **argv)
+{
+	int ret;
+
+	ret = starpu_init(NULL);
+	if (ret == -ENODEV)
+		return 77;
+	STARPU_CHECK_RETURN_VALUE(ret, "starpu_init");
+
+	{
+		int id;
+
+		id = starpu_perf_knob_scope_name_to_id("global");
+		STARPU_ASSERT(id == starpu_perf_knob_scope_global);
+		
+		id = starpu_perf_knob_scope_name_to_id("per_worker");
+		STARPU_ASSERT(id == starpu_perf_knob_scope_per_worker);
+		
+		id = starpu_perf_knob_scope_name_to_id("per_scheduler");
+		STARPU_ASSERT(id == starpu_perf_knob_scope_per_scheduler);
+
+		(void)id;
+	}
+
+	{
+		const char *name;
+		
+		name = starpu_perf_knob_scope_id_to_name(starpu_perf_knob_scope_global);
+		STARPU_ASSERT(strcmp(name, "global") == 0);
+		
+		name = starpu_perf_knob_scope_id_to_name(starpu_perf_knob_scope_per_worker);
+		STARPU_ASSERT(strcmp(name, "per_worker") == 0);
+		
+		name = starpu_perf_knob_scope_id_to_name(starpu_perf_knob_scope_per_scheduler);
+		STARPU_ASSERT(strcmp(name, "per_scheduler") == 0);
+
+		(void)name;
+	}
+
+	{
+		int id;
+
+		id = starpu_perf_knob_type_name_to_id("int32");
+		STARPU_ASSERT(id == starpu_perf_knob_type_int32);
+
+		id = starpu_perf_knob_type_name_to_id("int64");
+		STARPU_ASSERT(id == starpu_perf_knob_type_int64);
+
+		id = starpu_perf_knob_type_name_to_id("float");
+		STARPU_ASSERT(id == starpu_perf_knob_type_float);
+
+		id = starpu_perf_knob_type_name_to_id("double");
+		STARPU_ASSERT(id == starpu_perf_knob_type_double);
+
+		(void)id;
+	}
+
+	{
+		const char *name;
+		
+		name = starpu_perf_knob_type_id_to_name(starpu_perf_knob_type_int32);
+		STARPU_ASSERT(strcmp(name, "int32") == 0);
+		
+		name = starpu_perf_knob_type_id_to_name(starpu_perf_knob_type_int64);
+		STARPU_ASSERT(strcmp(name, "int64") == 0);
+		
+		name = starpu_perf_knob_type_id_to_name(starpu_perf_knob_type_float);
+		STARPU_ASSERT(strcmp(name, "float") == 0);
+		
+		name = starpu_perf_knob_type_id_to_name(starpu_perf_knob_type_double);
+		STARPU_ASSERT(strcmp(name, "double") == 0);
+
+		(void)name;
+	}
+
+	printf("programmatically get knobs per scope\n");
+	print_scope(starpu_perf_knob_scope_global);
+	print_scope(starpu_perf_knob_scope_per_worker);
+	print_scope(starpu_perf_knob_scope_per_scheduler);
+	printf("\n");
+
+	printf("list available knobs per scope\n");
+	starpu_perf_knob_list_avail(starpu_perf_knob_scope_global);
+	starpu_perf_knob_list_avail(starpu_perf_knob_scope_per_worker);
+	starpu_perf_knob_list_avail(starpu_perf_knob_scope_per_scheduler);
+	printf("\n");
+
+	printf("list all available knobs\n");
+	starpu_perf_knob_list_all_avail(starpu_perf_knob_scope_global);
+	printf("\n");
+
+	starpu_shutdown();
+
+	return 0;
+}

+ 149 - 0
examples/perf_steering/perf_knobs_02.c

@@ -0,0 +1,149 @@
+/* StarPU --- Runtime system for heterogeneous multicore architectures.
+ *
+ * Copyright (C) 2019                                     Inria
+ *
+ * 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.h>
+#include <assert.h>
+#include <string.h>
+
+int main(int argc, char **argv)
+{
+	int ret;
+
+	ret = starpu_init(NULL);
+	if (ret == -ENODEV)
+		return 77;
+	STARPU_CHECK_RETURN_VALUE(ret, "starpu_init");
+
+	{
+		const char * const knob_name       = "starpu.global.g_calibrate_knob";
+		const char * const knob_scope_name = "global";
+		const char * const knob_type_name  = "int32";
+		int32_t val, val_save;
+
+		const int scope_id = starpu_perf_knob_scope_name_to_id(knob_scope_name);
+		const int id = starpu_perf_knob_name_to_id(scope_id, knob_name);
+		STARPU_ASSERT(starpu_perf_knob_get_type_id(id) == starpu_perf_knob_type_name_to_id(knob_type_name));
+
+		printf("%s:\n", knob_name);
+
+		val_save = val = starpu_perf_knob_get_global_int32_value(id);
+		printf("- %d\n", val);
+
+		starpu_perf_knob_set_global_int32_value(id, 1);
+		val = starpu_perf_knob_get_global_int32_value(id);
+		printf("- %d\n", val);
+		STARPU_ASSERT(val == 1);
+
+		starpu_perf_knob_set_global_int32_value(id, 0);
+		val = starpu_perf_knob_get_global_int32_value(id);
+		printf("- %d\n", val);
+		STARPU_ASSERT(val == 0);
+
+		starpu_perf_knob_set_global_int32_value(id, val_save);
+		val = starpu_perf_knob_get_global_int32_value(id);
+		printf("- %d\n", val);
+		STARPU_ASSERT(val == val_save);
+	}
+
+	{
+		const char * const knob_name       = "starpu.global.g_enable_catch_signal_knob";
+		const char * const knob_scope_name = "global";
+		const char * const knob_type_name  = "int32";
+		int32_t val, val_save;
+
+		const int scope_id = starpu_perf_knob_scope_name_to_id(knob_scope_name);
+		const int id = starpu_perf_knob_name_to_id(scope_id, knob_name);
+		STARPU_ASSERT(starpu_perf_knob_get_type_id(id) == starpu_perf_knob_type_name_to_id(knob_type_name));
+
+		printf("%s:\n", knob_name);
+
+		val_save = val = starpu_perf_knob_get_global_int32_value(id);
+		printf("- %d\n", val);
+
+		starpu_perf_knob_set_global_int32_value(id, 1);
+		val = starpu_perf_knob_get_global_int32_value(id);
+		printf("- %d\n", val);
+		STARPU_ASSERT(val == 1);
+
+		starpu_perf_knob_set_global_int32_value(id, 0);
+		val = starpu_perf_knob_get_global_int32_value(id);
+		printf("- %d\n", val);
+		STARPU_ASSERT(val == 0);
+
+		starpu_perf_knob_set_global_int32_value(id, val_save);
+		val = starpu_perf_knob_get_global_int32_value(id);
+		printf("- %d\n", val);
+		STARPU_ASSERT(val == val_save);
+	}
+
+
+	{
+		const char * const knob_name       = "starpu.worker.w_bind_to_pu_knob";
+		const char * const knob_scope_name = "per_worker";
+		const char * const knob_type_name  = "int32";
+		int32_t val;
+
+		const int scope_id = starpu_perf_knob_scope_name_to_id(knob_scope_name);
+		const int id = starpu_perf_knob_name_to_id(scope_id, knob_name);
+		STARPU_ASSERT(starpu_perf_knob_get_type_id(id) == starpu_perf_knob_type_name_to_id(knob_type_name));
+
+		printf("%s:\n", knob_name);
+
+		unsigned int ncpu  = starpu_cpu_worker_get_count();
+		unsigned int i;
+		for (i=0; i<ncpu; i++)
+		{
+			val = starpu_perf_knob_get_per_worker_int32_value(id, i);
+			STARPU_ASSERT(val >= 0);
+			printf("- %u: %d\n", i, val);
+		}
+	}
+
+	{
+		const char * const knob_name       = "starpu.task.s_max_priority_cap_knob";
+		const char * const knob_scope_name = "per_scheduler";
+		const char * const knob_type_name  = "int32";
+		int32_t val;
+
+		const int scope_id = starpu_perf_knob_scope_name_to_id(knob_scope_name);
+		const int id = starpu_perf_knob_name_to_id(scope_id, knob_name);
+		STARPU_ASSERT(starpu_perf_knob_get_type_id(id) == starpu_perf_knob_type_name_to_id(knob_type_name));
+
+		printf("%s:\n", knob_name);
+		val = starpu_perf_knob_get_per_scheduler_int32_value(id, "prio");
+		printf("- %d\n", val);
+	}
+
+	{
+		const char * const knob_name       = "starpu.task.s_min_priority_cap_knob";
+		const char * const knob_scope_name = "per_scheduler";
+		const char * const knob_type_name  = "int32";
+		int32_t val;
+
+		const int scope_id = starpu_perf_knob_scope_name_to_id(knob_scope_name);
+		const int id = starpu_perf_knob_name_to_id(scope_id, knob_name);
+		STARPU_ASSERT(starpu_perf_knob_get_type_id(id) == starpu_perf_knob_type_name_to_id(knob_type_name));
+
+		printf("%s:\n", knob_name);
+		val = starpu_perf_knob_get_per_scheduler_int32_value(id, "prio");
+		printf("- %d\n", val);
+	}
+
+
+	starpu_shutdown();
+
+	return 0;
+}

+ 165 - 0
examples/perf_steering/perf_knobs_03.c

@@ -0,0 +1,165 @@
+/* StarPU --- Runtime system for heterogeneous multicore architectures.
+ *
+ * Copyright (C) 2019                                     Inria
+ *
+ * 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.h>
+#include <assert.h>
+#include <string.h>
+
+#define NTASKS 100
+
+volatile int task_count[2];
+
+void cpu_func(void *buffer[], void *cl_arg)
+{
+	(void)buffer;
+	(void)cl_arg;
+	int workerid = starpu_worker_get_id();
+	STARPU_ASSERT(workerid == 0 || workerid == 1);
+	task_count[workerid]++;
+}
+
+int main(int argc, char **argv)
+{
+	int ret;
+
+	struct starpu_conf conf;
+	starpu_conf_init(&conf);
+	conf.ncpus = 2;
+	conf.sched_policy_name = "prio";
+
+	ret = starpu_initialize(&conf, &argc, &argv);
+	if (ret == -ENODEV)
+		return 77;
+	STARPU_CHECK_RETURN_VALUE(ret, "starpu_init");
+
+	unsigned int ncpu  = starpu_cpu_worker_get_count();
+	if (ncpu < 2)
+	{
+		fprintf(stderr, "example needs two cpu cores.\n");
+		return 77;
+	}
+	STARPU_ASSERT(ncpu == 2);
+
+	{
+		const char * const max_prio_knob_name       = "starpu.task.s_max_priority_cap_knob";
+		const char * const min_prio_knob_name       = "starpu.task.s_min_priority_cap_knob";
+		const char * const knob_scope_name = "per_scheduler";
+		const char * const knob_type_name  = "int32";
+		int32_t max_prio_val;
+		int32_t min_prio_val;
+
+		const int scope_id = starpu_perf_knob_scope_name_to_id(knob_scope_name);
+
+		const int max_prio_id = starpu_perf_knob_name_to_id(scope_id, max_prio_knob_name);
+		STARPU_ASSERT(starpu_perf_knob_get_type_id(max_prio_id) == starpu_perf_knob_type_name_to_id(knob_type_name));
+
+		const int min_prio_id = starpu_perf_knob_name_to_id(scope_id, min_prio_knob_name);
+		STARPU_ASSERT(starpu_perf_knob_get_type_id(min_prio_id) == starpu_perf_knob_type_name_to_id(knob_type_name));
+
+		printf("%s:\n", max_prio_knob_name);
+		max_prio_val = starpu_perf_knob_get_per_scheduler_int32_value(max_prio_id, "prio");
+		printf("- %d\n", max_prio_val);
+
+		printf("%s:\n", min_prio_knob_name);
+		min_prio_val = starpu_perf_knob_get_per_scheduler_int32_value(min_prio_id, "prio");
+		printf("- %d\n", min_prio_val);
+		STARPU_ASSERT (max_prio_val >= min_prio_val);
+
+		if (min_prio_val > 0)
+		{
+			starpu_perf_knob_set_per_scheduler_int32_value(min_prio_id, "prio", 0);
+			starpu_perf_knob_set_per_scheduler_int32_value(max_prio_id, "prio", 0);
+		}
+		else
+		{
+			starpu_perf_knob_set_per_scheduler_int32_value(max_prio_id, "prio", 0);
+			starpu_perf_knob_set_per_scheduler_int32_value(min_prio_id, "prio", 0);
+		}
+
+		printf("%s:\n", max_prio_knob_name);
+		max_prio_val = starpu_perf_knob_get_per_scheduler_int32_value(max_prio_id, "prio");
+		printf("- %d\n", max_prio_val);
+
+		printf("%s:\n", min_prio_knob_name);
+		min_prio_val = starpu_perf_knob_get_per_scheduler_int32_value(min_prio_id, "prio");
+		printf("- %d\n", min_prio_val);
+		STARPU_ASSERT (max_prio_val == 0);
+		STARPU_ASSERT (min_prio_val == 0);
+
+	}
+
+	{
+		const char * const knob_name       = "starpu.worker.w_enable_worker_knob";
+		const char * const knob_scope_name = "per_worker";
+		const char * const knob_type_name  = "int32";
+		int32_t val;
+
+		const int scope_id = starpu_perf_knob_scope_name_to_id(knob_scope_name);
+		const int id = starpu_perf_knob_name_to_id(scope_id, knob_name);
+		STARPU_ASSERT(starpu_perf_knob_get_type_id(id) == starpu_perf_knob_type_name_to_id(knob_type_name));
+
+		struct starpu_codelet cl = {
+			.cpu_func = cpu_func
+		};
+
+		task_count[0] = 0;
+		task_count[1] = 0;
+
+		val = starpu_perf_knob_get_per_worker_int32_value(id, 0);
+		STARPU_ASSERT(val == 1);
+		val = starpu_perf_knob_get_per_worker_int32_value(id, 1);
+		STARPU_ASSERT(val == 1);
+
+		starpu_perf_knob_set_per_worker_int32_value(id, 1, 0);
+		val = starpu_perf_knob_get_per_worker_int32_value(id, 1);
+		STARPU_ASSERT(val == 0);
+
+		int i;
+		for (i=0; i<NTASKS; i++)
+		{
+			starpu_task_insert(&cl, 0);
+		}
+		starpu_task_wait_for_all();
+		STARPU_ASSERT(task_count[0] == NTASKS);
+		STARPU_ASSERT(task_count[1] == 0);
+
+		task_count[0] = 0;
+
+		starpu_perf_knob_set_per_worker_int32_value(id, 1, 1);
+		val = starpu_perf_knob_get_per_worker_int32_value(id, 1);
+		STARPU_ASSERT(val == 1);
+
+		starpu_perf_knob_set_per_worker_int32_value(id, 0, 0);
+		val = starpu_perf_knob_get_per_worker_int32_value(id, 0);
+		STARPU_ASSERT(val == 0);
+
+		for (i=0; i<NTASKS; i++)
+		{
+			starpu_task_insert(&cl, 0);
+		}
+		starpu_task_wait_for_all();
+		STARPU_ASSERT(task_count[0] == 0);
+		STARPU_ASSERT(task_count[1] == NTASKS);
+
+		starpu_perf_knob_set_per_worker_int32_value(id, 0, 1);
+		val = starpu_perf_knob_get_per_worker_int32_value(id, 0);
+		STARPU_ASSERT(val == 1);
+	}
+
+	starpu_shutdown();
+
+	return 0;
+}

+ 1 - 0
include/starpu.h

@@ -85,6 +85,7 @@ typedef INT_PTR intptr_t;
 #include <starpu_bitmap.h>
 #include <starpu_clusters.h>
 #include <starpu_perf_monitoring.h>
+#include <starpu_perf_steering.h>
 
 #ifdef __cplusplus
 extern "C"

+ 3 - 3
include/starpu_perf_monitoring.h

@@ -32,9 +32,9 @@ extern "C"
 enum starpu_perf_counter_scope
 {
 	starpu_perf_counter_scope_undefined     = 0,
-	starpu_perf_counter_scope_global        = 1,
-	starpu_perf_counter_scope_per_worker    = 2,
-	starpu_perf_counter_scope_per_codelet   = 3
+	starpu_perf_counter_scope_global        = 2,
+	starpu_perf_counter_scope_per_worker    = 4,
+	starpu_perf_counter_scope_per_codelet   = 6
 };
 
 enum starpu_perf_counter_type

+ 103 - 0
include/starpu_perf_steering.h

@@ -0,0 +1,103 @@
+/* StarPU --- Runtime system for heterogeneous multicore architectures.
+ *
+ * Copyright (C) 2019                                     Inria
+ *
+ * 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.
+ */
+
+#ifndef __STARPU_PERF_STEERING_H__
+#define __STARPU_PERF_STEERING_H__
+
+#include <starpu.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+   @defgroup API_Perf_Steering Perf_Steering
+   @{
+*/
+
+enum starpu_perf_knob_scope
+{
+	starpu_perf_knob_scope_undefined     = 0,
+	starpu_perf_knob_scope_global        = 1,
+	starpu_perf_knob_scope_per_worker    = 3,
+	starpu_perf_knob_scope_per_scheduler = 5
+};
+
+enum starpu_perf_knob_type
+{
+	starpu_perf_knob_type_undefined = 0,
+	starpu_perf_knob_type_int32     = 1,
+	starpu_perf_knob_type_int64     = 2,
+	starpu_perf_knob_type_float     = 3,
+	starpu_perf_knob_type_double    = 4
+};
+
+int starpu_perf_knob_scope_name_to_id(const char *name);
+const char *starpu_perf_knob_scope_id_to_name(enum starpu_perf_knob_scope scope);
+
+int starpu_perf_knob_type_name_to_id(const char *name);
+const char *starpu_perf_knob_type_id_to_name(enum starpu_perf_knob_type type);
+
+int starpu_perf_knob_nb(enum starpu_perf_knob_scope scope);
+int starpu_perf_knob_name_to_id(enum starpu_perf_knob_scope scope, const char *name);
+int starpu_perf_knob_nth_to_id(enum starpu_perf_knob_scope scope, int nth);
+const char *starpu_perf_knob_id_to_name(int id);
+int starpu_perf_knob_get_type_id(int id);
+const char *starpu_perf_knob_get_help_string(int id);
+
+void starpu_perf_knob_list_avail(enum starpu_perf_knob_scope scope);
+void starpu_perf_knob_list_all_avail(enum starpu_perf_knob_scope scope);
+
+int32_t starpu_perf_knob_get_global_int32_value (const int knob_id);
+int64_t starpu_perf_knob_get_global_int64_value (const int knob_id);
+float   starpu_perf_knob_get_global_float_value (const int knob_id);
+double  starpu_perf_knob_get_global_double_value(const int knob_id);
+
+void starpu_perf_knob_set_global_int32_value (const int knob_id, int32_t new_value);
+void starpu_perf_knob_set_global_int64_value (const int knob_id, int64_t new_value);
+void starpu_perf_knob_set_global_float_value (const int knob_id, float   new_value);
+void starpu_perf_knob_set_global_double_value(const int knob_id, double  new_value);
+
+
+int32_t starpu_perf_knob_get_per_worker_int32_value (const int knob_id, unsigned workerid);
+int64_t starpu_perf_knob_get_per_worker_int64_value (const int knob_id, unsigned workerid);
+float   starpu_perf_knob_get_per_worker_float_value (const int knob_id, unsigned workerid);
+double  starpu_perf_knob_get_per_worker_double_value(const int knob_id, unsigned workerid);
+
+void starpu_perf_knob_set_per_worker_int32_value (const int knob_id, unsigned workerid, int32_t new_value);
+void starpu_perf_knob_set_per_worker_int64_value (const int knob_id, unsigned workerid, int64_t new_value);
+void starpu_perf_knob_set_per_worker_float_value (const int knob_id, unsigned workerid, float   new_value);
+void starpu_perf_knob_set_per_worker_double_value(const int knob_id, unsigned workerid, double  new_value);
+
+
+int32_t starpu_perf_knob_get_per_scheduler_int32_value (const int knob_id, const char * sched_policy_name);
+int64_t starpu_perf_knob_get_per_scheduler_int64_value (const int knob_id, const char * sched_policy_name);
+float   starpu_perf_knob_get_per_scheduler_float_value (const int knob_id, const char * sched_policy_name);
+double  starpu_perf_knob_get_per_scheduler_double_value(const int knob_id, const char * sched_policy_name);
+
+void starpu_perf_knob_set_per_scheduler_int32_value (const int knob_id, const char * sched_policy_name, int32_t new_value);
+void starpu_perf_knob_set_per_scheduler_int64_value (const int knob_id, const char * sched_policy_name, int64_t new_value);
+void starpu_perf_knob_set_per_scheduler_float_value (const int knob_id, const char * sched_policy_name, float   new_value);
+void starpu_perf_knob_set_per_scheduler_double_value(const int knob_id, const char * sched_policy_name, double  new_value);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STARPU_PERF_STEERING_H__ */

+ 367 - 4
src/common/knobs.c

@@ -24,6 +24,7 @@
 #include <core/workers.h>
 #include <common/knobs.h>
 
+/* Performance Monitoring */
 struct perf_counter_array
 {
 	int size;
@@ -189,7 +190,7 @@ int _starpu_perf_counter_register(enum starpu_perf_counter_scope scope, const ch
 	return id;
 }
 
-static void _unregister_scope(enum starpu_perf_counter_scope scope)
+static void _unregister_counter_scope(enum starpu_perf_counter_scope scope)
 {
 	STARPU_ASSERT(!_starpu_machine_is_running());
 
@@ -203,9 +204,9 @@ void _starpu_perf_counter_unregister_all_scopes(void)
 {
 	STARPU_ASSERT(!_starpu_machine_is_running());
 
-	_unregister_scope(starpu_perf_counter_scope_global);
-	_unregister_scope(starpu_perf_counter_scope_per_worker);
-	_unregister_scope(starpu_perf_counter_scope_per_codelet);
+	_unregister_counter_scope(starpu_perf_counter_scope_global);
+	_unregister_counter_scope(starpu_perf_counter_scope_per_worker);
+	_unregister_counter_scope(starpu_perf_counter_scope_per_codelet);
 }
 
 /* - */
@@ -517,3 +518,365 @@ STARPU_PERF_COUNTER_SAMPLE_GET_TYPED_VALUE(float, float);
 STARPU_PERF_COUNTER_SAMPLE_GET_TYPED_VALUE(double, double);
 #undef STARPU_PERF_COUNTER_SAMPLE_GET_TYPED_VALUE
 
+/* -------------------------------------------------------------------- */
+/* Performance Steering */
+
+struct perf_knob_array
+{
+	int size;
+	struct starpu_perf_knob *array;
+};
+
+static struct perf_knob_array global_knobs	= { .size = 0, .array = NULL };
+static struct perf_knob_array per_worker_knobs	= { .size = 0, .array = NULL };
+static struct perf_knob_array per_scheduler_knobs	= { .size = 0, .array = NULL };
+
+void _starpu_perf_knob_init(void)
+{
+	STARPU_ASSERT(!_starpu_machine_is_running());
+	/* call knob registration routines in each modules */
+	_starpu__workers_c__register_knobs();
+	_starpu__task_c__register_knobs();
+}
+
+void _starpu_perf_knob_exit(void)
+{
+	STARPU_ASSERT(!_starpu_machine_is_running());
+
+	_starpu_perf_knob_unregister_all_scopes();
+}
+
+/* - */
+
+int starpu_perf_knob_scope_name_to_id(const char * const name)
+{
+	if (strcmp(name, "global") == 0)
+		return starpu_perf_knob_scope_global;
+	if (strcmp(name, "per_worker") == 0)
+		return starpu_perf_knob_scope_per_worker;
+	if (strcmp(name, "per_scheduler") == 0)
+		return starpu_perf_knob_scope_per_scheduler;
+	return -1;
+}
+
+const char *starpu_perf_knob_scope_id_to_name(const enum starpu_perf_knob_scope scope)
+{
+	switch (scope)
+	{
+		case starpu_perf_knob_scope_global:
+			return "global";
+
+		case starpu_perf_knob_scope_per_worker:
+			return "per_worker";
+
+		case starpu_perf_knob_scope_per_scheduler:
+			return "per_scheduler";
+
+		default:
+			return NULL;
+	};
+}
+
+/* - */
+
+int starpu_perf_knob_type_name_to_id(const char * const name)
+{
+	if (strcmp(name, "int32") == 0)
+		return starpu_perf_knob_type_int32;
+	if (strcmp(name, "int64") == 0)
+		return starpu_perf_knob_type_int64;
+	if (strcmp(name, "float") == 0)
+		return starpu_perf_knob_type_float;
+	if (strcmp(name, "double") == 0)
+		return starpu_perf_knob_type_double;
+	return -1;
+}
+
+const char *starpu_perf_knob_type_id_to_name(const enum starpu_perf_knob_type type)
+{
+	switch (type)
+	{
+		case starpu_perf_knob_type_int32:
+			return "int32";
+
+		case starpu_perf_knob_type_int64:
+			return "int64";
+
+		case starpu_perf_knob_type_float:
+			return "float";
+
+		case starpu_perf_knob_type_double:
+			return "double";
+
+		default:
+			return NULL;
+	};
+}
+
+static struct perf_knob_array *_get_knobs(const enum starpu_perf_knob_scope scope)
+{
+	STARPU_ASSERT_PERF_KNOB_SCOPE_DEFINED(scope);
+	switch (scope)
+	{
+		case starpu_perf_knob_scope_global:
+			return &global_knobs;
+
+		case starpu_perf_knob_scope_per_worker:
+			return &per_worker_knobs;
+
+		case starpu_perf_knob_scope_per_scheduler:
+			return &per_scheduler_knobs;
+
+		default:
+			STARPU_ABORT();
+	};
+};
+
+/* - */
+
+struct starpu_perf_knob_group *_starpu_perf_knob_group_register(
+	enum starpu_perf_knob_scope scope,
+	void (*set_func)(const struct starpu_perf_knob * const knob, void *context, const struct starpu_perf_knob_value * const value),
+	void (*get_func)(const struct starpu_perf_knob * const knob, void *context,       struct starpu_perf_knob_value * const value))
+{
+	STARPU_ASSERT_PERF_KNOB_SCOPE_DEFINED(scope);
+	STARPU_ASSERT(set_func != NULL);
+	STARPU_ASSERT(get_func != NULL);
+	struct starpu_perf_knob_group *new_group;
+	_STARPU_MALLOC(new_group, sizeof(*new_group));
+	new_group->scope = scope;
+	new_group->set = set_func;
+	new_group->get = get_func;
+	new_group->array_size = 0;
+	new_group->array = NULL;
+	return new_group;
+}
+
+void _starpu_perf_knob_group_unregister(struct starpu_perf_knob_group *group)
+{
+	STARPU_ASSERT((group->array_size > 0 && group->array != NULL)  ||  (group->array_size = 0 && group->array == NULL));
+	if (group->array != NULL)
+	{
+		free(group->array);
+	}
+	memset(group, 0, sizeof(*group));
+}
+
+/* - */
+
+int _starpu_perf_knob_register(struct starpu_perf_knob_group *group, const char *name, enum starpu_perf_knob_type type, const char *help)
+{
+	STARPU_ASSERT(!_starpu_machine_is_running());
+
+	struct perf_knob_array * const knobs = _get_knobs(group->scope);
+	STARPU_ASSERT_PERF_KNOB_TYPE_DEFINED(type);
+
+	const int index = knobs->size++;
+	_STARPU_REALLOC(knobs->array, knobs->size * sizeof(*knobs->array));
+
+	struct starpu_perf_knob * const new_knob = &knobs->array[index];
+	const int id = _starpu_perf_knob_id_build(group->scope, index);
+	new_knob->id = id;
+	new_knob->name = name;
+	new_knob->help = help;
+	new_knob->type = type;
+	new_knob->group = group;
+	new_knob->id_in_group = group->array_size++;
+	_STARPU_REALLOC(group->array, group->array_size * sizeof(*group->array));
+	group->array[new_knob->id_in_group] = new_knob;
+	return id;
+}
+
+static void _unregister_knob_scope(enum starpu_perf_knob_scope scope)
+{
+	STARPU_ASSERT(!_starpu_machine_is_running());
+
+	struct perf_knob_array * const knobs = _get_knobs(scope);
+	free(knobs->array);
+	knobs->array = NULL;
+	knobs->size  = 0;
+}
+
+void _starpu_perf_knob_unregister_all_scopes(void)
+{
+	STARPU_ASSERT(!_starpu_machine_is_running());
+
+	_unregister_knob_scope(starpu_perf_knob_scope_global);
+	_unregister_knob_scope(starpu_perf_knob_scope_per_worker);
+	_unregister_knob_scope(starpu_perf_knob_scope_per_scheduler);
+}
+
+/* - */
+
+int starpu_perf_knob_nb(enum starpu_perf_knob_scope scope)
+{
+	const struct perf_knob_array * const knobs = _get_knobs(scope);
+	return knobs->size;
+}
+
+int starpu_perf_knob_nth_to_id(enum starpu_perf_knob_scope scope, int nth)
+{
+	return _starpu_perf_knob_id_build(scope, nth);
+}
+
+int starpu_perf_knob_name_to_id(enum starpu_perf_knob_scope scope, const char *name)
+{
+	const struct perf_knob_array * const knobs = _get_knobs(scope);
+	int index;
+	for (index = 0; index < knobs->size; index++)
+	{
+		if (strcmp(name, knobs->array[index].name) == 0)
+		{
+			return _starpu_perf_knob_id_build(scope, index);
+		}
+	}
+	return -1;
+}
+
+const char *starpu_perf_knob_id_to_name(int id)
+{
+	const int scope = _starpu_perf_knob_id_get_scope(id);
+	const int index = _starpu_perf_knob_id_get_index(id);
+	const struct perf_knob_array * const knobs = _get_knobs(scope);
+	if (index < 0 || index >= knobs->size)
+		return NULL;
+	return knobs->array[index].name;
+}
+
+const char *starpu_perf_knob_get_help_string(int id)
+{
+	const int scope = _starpu_perf_knob_id_get_scope(id);
+	const int index = _starpu_perf_knob_id_get_index(id);
+	const struct perf_knob_array * const knobs = _get_knobs(scope);
+	STARPU_ASSERT(index >= 0 && index < knobs->size);
+	return knobs->array[index].help;
+}
+
+int starpu_perf_knob_get_type_id(int id)
+{
+	const int scope = _starpu_perf_knob_id_get_scope(id);
+	const int index = _starpu_perf_knob_id_get_index(id);
+	const struct perf_knob_array * const knobs = _get_knobs(scope);
+	STARPU_ASSERT(index >= 0 && index < knobs->size);
+	return knobs->array[index].type;
+}
+
+static struct starpu_perf_knob *get_knob(int id)
+{
+	const int scope = _starpu_perf_knob_id_get_scope(id);
+	struct perf_knob_array *knobs = _get_knobs(scope);
+	const int index = _starpu_perf_knob_id_get_index(id);
+	STARPU_ASSERT(index >= 0  &&  index < knobs->size);
+	return &knobs->array[index];
+}
+
+/* - */
+
+void starpu_perf_knob_list_avail(enum starpu_perf_knob_scope scope)
+{
+	const struct perf_knob_array * const knobs = _get_knobs(scope);
+	int index;
+	for (index = 0; index < knobs->size; index++)
+	{
+		const struct starpu_perf_knob * const knob = &knobs->array[index];
+		printf("0x%08x:%s [%s] - %s\n", _starpu_perf_knob_id_build(scope, index), knob->name, starpu_perf_knob_type_id_to_name(knob->type), knob->help);
+	}
+}
+
+void starpu_perf_knob_list_all_avail(enum starpu_perf_knob_scope scope)
+{
+	printf("scope: global\n");
+	starpu_perf_knob_list_avail(starpu_perf_knob_scope_global);
+
+	printf("scope: per_worker\n");
+	starpu_perf_knob_list_avail(starpu_perf_knob_scope_per_worker);
+
+	printf("scope: per_scheduler\n");
+	starpu_perf_knob_list_avail(starpu_perf_knob_scope_per_scheduler);
+}
+
+#define __STARPU_PERF_KNOB_SET_TYPED_VALUE(SCOPE_NAME, STRING, TYPE) \
+void starpu_perf_knob_set_##SCOPE_NAME##_##STRING##_value(const int knob_id, const TYPE value) \
+{ \
+	STARPU_ASSERT(_starpu_perf_knob_id_get_scope(knob_id) == starpu_perf_knob_scope_global); \
+	const struct starpu_perf_knob * const knob = get_knob(knob_id); \
+	STARPU_ASSERT(starpu_perf_knob_get_type_id(knob_id) == starpu_perf_knob_type_##STRING); \
+	const struct starpu_perf_knob_group * const knob_group = knob->group; \
+	const struct starpu_perf_knob_value kv = { .val_##TYPE = value }; \
+	knob_group->set(knob, NULL, &kv); \
+}
+
+__STARPU_PERF_KNOB_SET_TYPED_VALUE(global, int32, int32_t);
+__STARPU_PERF_KNOB_SET_TYPED_VALUE(global, int64, int64_t);
+__STARPU_PERF_KNOB_SET_TYPED_VALUE(global, float, float);
+__STARPU_PERF_KNOB_SET_TYPED_VALUE(global, double, double);
+
+#undef __STARPU_PERF_KNOB_SAMPLE_SET_TYPED_VALUE
+
+#define __STARPU_PERF_KNOB_GET_TYPED_VALUE(SCOPE_NAME, STRING, TYPE) \
+TYPE starpu_perf_knob_get_##SCOPE_NAME##_##STRING##_value(const int knob_id) \
+{ \
+	STARPU_ASSERT(_starpu_perf_knob_id_get_scope(knob_id) == starpu_perf_knob_scope_global); \
+	const struct starpu_perf_knob * const knob = get_knob(knob_id); \
+	STARPU_ASSERT(starpu_perf_knob_get_type_id(knob_id) == starpu_perf_knob_type_##STRING); \
+	const struct starpu_perf_knob_group * const knob_group = knob->group; \
+	struct starpu_perf_knob_value kv; \
+	knob_group->get(knob, NULL, &kv); \
+	return kv.val_##TYPE; \
+}
+
+__STARPU_PERF_KNOB_GET_TYPED_VALUE(global, int32, int32_t);
+__STARPU_PERF_KNOB_GET_TYPED_VALUE(global, int64, int64_t);
+__STARPU_PERF_KNOB_GET_TYPED_VALUE(global, float, float);
+__STARPU_PERF_KNOB_GET_TYPED_VALUE(global, double, double);
+
+#undef __STARPU_PERF_KNOB_SAMPLE_GET_TYPED_VALUE
+
+
+#define __STARPU_PERF_KNOB_SET_TYPED_VALUE_WITH_CONTEXT(SCOPE_NAME, STRING, TYPE, CONTEXT_TYPE, CONTEXT_VAR) \
+void starpu_perf_knob_set_##SCOPE_NAME##_##STRING##_value(const int knob_id, CONTEXT_TYPE CONTEXT_VAR, const TYPE value) \
+{ \
+	STARPU_ASSERT(_starpu_perf_knob_id_get_scope(knob_id) == starpu_perf_knob_scope_##SCOPE_NAME); \
+	const struct starpu_perf_knob * const knob = get_knob(knob_id); \
+	STARPU_ASSERT(starpu_perf_knob_get_type_id(knob_id) == starpu_perf_knob_type_##STRING); \
+	const struct starpu_perf_knob_group * const knob_group = knob->group; \
+	const struct starpu_perf_knob_value kv = { .val_##TYPE = value }; \
+	knob_group->set(knob, &CONTEXT_VAR, &kv); \
+}
+
+__STARPU_PERF_KNOB_SET_TYPED_VALUE_WITH_CONTEXT(per_worker, int32,  int32_t, unsigned, workerid);
+__STARPU_PERF_KNOB_SET_TYPED_VALUE_WITH_CONTEXT(per_worker, int64,  int64_t, unsigned, workerid);
+__STARPU_PERF_KNOB_SET_TYPED_VALUE_WITH_CONTEXT(per_worker, float,  float,   unsigned, workerid);
+__STARPU_PERF_KNOB_SET_TYPED_VALUE_WITH_CONTEXT(per_worker, double, double,  unsigned, workerid);
+
+__STARPU_PERF_KNOB_SET_TYPED_VALUE_WITH_CONTEXT(per_scheduler, int32,  int32_t, const char *, sched_policy_name);
+__STARPU_PERF_KNOB_SET_TYPED_VALUE_WITH_CONTEXT(per_scheduler, int64,  int64_t, const char *, sched_policy_name);
+__STARPU_PERF_KNOB_SET_TYPED_VALUE_WITH_CONTEXT(per_scheduler, float,  float,   const char *, sched_policy_name);
+__STARPU_PERF_KNOB_SET_TYPED_VALUE_WITH_CONTEXT(per_scheduler, double, double,  const char *, sched_policy_name);
+
+#undef __STARPU_PERF_KNOB_SAMPLE_SET_TYPED_VALUE_WITH_CONTEXT
+
+#define __STARPU_PERF_KNOB_GET_TYPED_VALUE_WITH_CONTEXT(SCOPE_NAME, STRING, TYPE, CONTEXT_TYPE, CONTEXT_VAR) \
+TYPE starpu_perf_knob_get_##SCOPE_NAME##_##STRING##_value(const int knob_id, CONTEXT_TYPE CONTEXT_VAR) \
+{ \
+	STARPU_ASSERT(_starpu_perf_knob_id_get_scope(knob_id) == starpu_perf_knob_scope_##SCOPE_NAME); \
+	const struct starpu_perf_knob * const knob = get_knob(knob_id); \
+	STARPU_ASSERT(starpu_perf_knob_get_type_id(knob_id) == starpu_perf_knob_type_##STRING); \
+	const struct starpu_perf_knob_group * const knob_group = knob->group; \
+	struct starpu_perf_knob_value kv; \
+	knob_group->get(knob, &CONTEXT_VAR, &kv); \
+	return kv.val_##TYPE; \
+}
+
+__STARPU_PERF_KNOB_GET_TYPED_VALUE_WITH_CONTEXT(per_worker, int32,  int32_t, unsigned, workerid);
+__STARPU_PERF_KNOB_GET_TYPED_VALUE_WITH_CONTEXT(per_worker, int64,  int64_t, unsigned, workerid);
+__STARPU_PERF_KNOB_GET_TYPED_VALUE_WITH_CONTEXT(per_worker, float,  float,   unsigned, workerid);
+__STARPU_PERF_KNOB_GET_TYPED_VALUE_WITH_CONTEXT(per_worker, double, double,  unsigned, workerid);
+
+__STARPU_PERF_KNOB_GET_TYPED_VALUE_WITH_CONTEXT(per_scheduler, int32,  int32_t, const char *, sched_policy_name);
+__STARPU_PERF_KNOB_GET_TYPED_VALUE_WITH_CONTEXT(per_scheduler, int64,  int64_t, const char *, sched_policy_name);
+__STARPU_PERF_KNOB_GET_TYPED_VALUE_WITH_CONTEXT(per_scheduler, float,  float,   const char *, sched_policy_name);
+__STARPU_PERF_KNOB_GET_TYPED_VALUE_WITH_CONTEXT(per_scheduler, double, double,  const char *, sched_policy_name);
+
+#undef __STARPU_PERF_KNOB_SAMPLE_GET_TYPED_VALUE_WITH_CONTEXT
+

+ 98 - 0
src/common/knobs.h

@@ -23,6 +23,7 @@
 #include <starpu.h>
 #include <common/config.h>
 
+/* Performance Monitoring */
 #define STARPU_ASSERT_PERF_COUNTER_SCOPE_DEFINED(t) STARPU_ASSERT( \
 		(t == starpu_perf_counter_scope_global ) \
 		|| (t == starpu_perf_counter_scope_per_worker ) \
@@ -252,4 +253,101 @@ extern int32_t _starpu_task__g_current_ready__value;
 void _starpu__task_c__register_counters(void);	/* module: task.c */
 
 
+/* -------------------------------------------------------------------- */
+/* Performance Steering */
+
+#define STARPU_ASSERT_PERF_KNOB_SCOPE_DEFINED(t) STARPU_ASSERT( \
+		(t == starpu_perf_knob_scope_global ) \
+		|| (t == starpu_perf_knob_scope_per_worker ) \
+		|| (t == starpu_perf_knob_scope_per_scheduler ) \
+	)
+
+
+#define STARPU_ASSERT_PERF_KNOB_TYPE_DEFINED(t) STARPU_ASSERT( \
+		(t == starpu_perf_knob_type_int32 ) \
+		|| (t == starpu_perf_knob_type_int64 ) \
+		|| (t == starpu_perf_knob_type_float ) \
+		|| (t == starpu_perf_knob_type_double ) \
+	)
+
+#define _STARPU_PERF_KNOBS_ID_SCOPE_BITS 4
+
+struct starpu_perf_knob;
+
+struct starpu_perf_knob_value
+{
+	enum starpu_perf_knob_type type;
+	union
+	{
+		int32_t val_int32_t;
+		int64_t val_int64_t;
+		float   val_float;
+		double  val_double;
+	};
+};
+
+struct starpu_perf_knob_group
+{
+	enum starpu_perf_knob_scope scope;
+	void (*set)(const struct starpu_perf_knob * const knob, void *context, const struct starpu_perf_knob_value * const value);
+	void (*get)(const struct starpu_perf_knob * const knob, void *context,       struct starpu_perf_knob_value * const value);
+	int array_size;
+	struct starpu_perf_knob **array;
+};
+
+struct starpu_perf_knob
+{
+	int id;
+	int id_in_group;
+	const char *name;
+	const char *help;
+	enum starpu_perf_knob_type type;
+	struct starpu_perf_knob_group *group;
+};
+
+#define __STARPU_PERF_KNOB_REG(PREFIX, SCOPE, CTR, TYPESTRING, HELP) \
+	do \
+		{ \
+			__##CTR =  _starpu_perf_knob_register(SCOPE, \
+					PREFIX "." #CTR, starpu_perf_knob_type_ ## TYPESTRING, \
+					HELP); \
+		} \
+	while (0)
+
+static inline int _starpu_perf_knob_id_get_scope(const int knob_id)
+{
+	STARPU_ASSERT(knob_id >= 0);
+	return knob_id & ((1 << _STARPU_PERF_KNOBS_ID_SCOPE_BITS) - 1);
+}
+
+static inline int _starpu_perf_knob_id_get_index(const int knob_id)
+{
+	STARPU_ASSERT(knob_id >= 0);
+	return knob_id >> _STARPU_PERF_KNOBS_ID_SCOPE_BITS;
+}
+
+static inline int _starpu_perf_knob_id_build(const enum starpu_perf_knob_scope scope, const int index)
+{
+	STARPU_ASSERT_PERF_KNOB_SCOPE_DEFINED(scope);
+	STARPU_ASSERT(index >= 0);
+	return (index << _STARPU_PERF_KNOBS_ID_SCOPE_BITS) | scope;
+}
+
+
+void _starpu_perf_knob_init(void);
+void _starpu_perf_knob_exit(void);
+
+struct starpu_perf_knob_group *_starpu_perf_knob_group_register(
+	enum starpu_perf_knob_scope scope,
+	void (*set_func)(const struct starpu_perf_knob * const knob, void *context, const struct starpu_perf_knob_value * const value),
+	void (*get_func)(const struct starpu_perf_knob * const knob, void *context,       struct starpu_perf_knob_value * const value));
+void _starpu_perf_knob_group_unregister(struct starpu_perf_knob_group *group);
+
+int _starpu_perf_knob_register(struct starpu_perf_knob_group *group, const char *name, enum starpu_perf_knob_type type, const char *help);
+void _starpu_perf_knob_unregister_all_scopes(void);
+
+/* performance knob registration routines per modules */
+void _starpu__workers_c__register_knobs(void);	/* module: workers.c */
+void _starpu__task_c__register_knobs(void); /* module: task.c */
+
 #endif // __KNOBS_H__

+ 98 - 0
src/core/task.c

@@ -68,6 +68,20 @@ static int __c_peak_ready;
 static int __c_total_executed;
 static int __c_cumul_execution_time;
 
+/* - */
+
+/* per-scheduler knobs */
+static int __s_max_priority_cap_knob;
+static int __s_min_priority_cap_knob;
+
+/* knob variables */
+static int __s_max_priority_cap__value;
+static int __s_min_priority_cap__value;
+
+static struct starpu_perf_knob_group * __kg_starpu_task__per_scheduler;
+
+/* - */
+
 static void global_sample_updater(struct starpu_perf_counter_sample *sample, void *context)
 {
 	STARPU_ASSERT(context == NULL); /* no context for the global updater */
@@ -133,6 +147,83 @@ void _starpu__task_c__register_counters(void)
 	}
 }
 
+/* - */
+
+void sched_knobs__set(const struct starpu_perf_knob * const knob, void *context, const struct starpu_perf_knob_value * const value)
+{
+	const char * const sched_policy_name = *(const char **)context;
+	(void) sched_policy_name;
+	if (knob->id == __s_max_priority_cap_knob)
+	{
+		STARPU_ASSERT(value->val_int32_t <= STARPU_MAX_PRIO);
+		STARPU_ASSERT(value->val_int32_t >= STARPU_MIN_PRIO);
+		STARPU_ASSERT(value->val_int32_t >= __s_min_priority_cap__value);
+		__s_max_priority_cap__value = value->val_int32_t;
+	}
+	else if (knob->id == __s_min_priority_cap_knob)
+	{
+		STARPU_ASSERT(value->val_int32_t <= STARPU_MAX_PRIO);
+		STARPU_ASSERT(value->val_int32_t >= STARPU_MIN_PRIO);
+		STARPU_ASSERT(value->val_int32_t <= __s_max_priority_cap__value);
+		__s_min_priority_cap__value = value->val_int32_t;
+	}
+	else
+	{
+		STARPU_ASSERT(0);
+		abort();
+	}
+}
+
+void sched_knobs__get(const struct starpu_perf_knob * const knob, void *context,       struct starpu_perf_knob_value * const value)
+{
+	const char * const sched_policy_name = *(const char **)context;
+	(void) sched_policy_name;
+	if (knob->id == __s_max_priority_cap_knob)
+	{
+		value->val_int32_t = __s_max_priority_cap__value;
+	}
+	else if (knob->id == __s_min_priority_cap_knob)
+	{
+		value->val_int32_t = __s_min_priority_cap__value;
+	}
+	else
+	{
+		STARPU_ASSERT(0);
+		abort();
+	}
+}
+
+void _starpu__task_c__register_knobs(void)
+{
+#if 0
+	{
+		const enum starpu_perf_knob_scope scope = starpu_perf_knob_scope_global;
+		__kg_starpu_global = _starpu_perf_knob_group_register(scope, global_knobs__set, global_knobs__get);
+	}
+#endif
+
+#if 0
+	{
+		const enum starpu_perf_knob_scope scope = starpu_perf_knob_scope_per_worker;
+		__kg_starpu_worker__per_worker = _starpu_perf_knob_group_register(scope, worker_knobs__set, worker_knobs__get);
+	}
+#endif
+
+	{
+		const enum starpu_perf_knob_scope scope = starpu_perf_knob_scope_per_scheduler;
+		__kg_starpu_task__per_scheduler = _starpu_perf_knob_group_register(scope, sched_knobs__set, sched_knobs__get);
+
+		/* TODO: priority capping knobs actually work globally for now, the sched policy name is ignored */
+		__STARPU_PERF_KNOB_REG("starpu.task", __kg_starpu_task__per_scheduler, s_max_priority_cap_knob, int32, "force task priority to this value or below (priority value)");
+		__s_max_priority_cap__value = STARPU_MAX_PRIO;
+
+		__STARPU_PERF_KNOB_REG("starpu.task", __kg_starpu_task__per_scheduler, s_min_priority_cap_knob, int32, "force task priority to this value or above (priority value)");
+		__s_min_priority_cap__value = STARPU_MIN_PRIO;
+	}
+}
+
+/* - */
+
 /* XXX this should be reinitialized when StarPU is shutdown (or we should make
  * sure that no task remains !) */
 /* TODO we could make this hierarchical to avoid contention ? */
@@ -738,6 +829,13 @@ int starpu_task_submit(struct starpu_task *task)
 	STARPU_ASSERT_MSG(task->magic == _STARPU_TASK_MAGIC, "Tasks must be created with starpu_task_create, or initialized with starpu_task_init.");
 
 	int ret;
+	{
+		/* task knobs */
+		if (task->priority > __s_max_priority_cap__value)
+			task->priority = __s_max_priority_cap__value;
+		if (task->priority < __s_min_priority_cap__value)
+			task->priority = __s_min_priority_cap__value;
+	}
 	unsigned is_sync = task->synchronous;
 	starpu_task_bundle_t bundle = task->bundle;
 	/* internally, StarPU manipulates a struct _starpu_job * which is a wrapper around a

+ 195 - 6
src/core/workers.c

@@ -56,6 +56,121 @@
 #include <windows.h>
 #endif
 
+
+/* global knobs */
+static int __g_calibrate_knob;
+static int __g_enable_catch_signal_knob;
+
+/* per-worker knobs */
+static int __w_bind_to_pu_knob;
+static int __w_enable_worker_knob;
+
+static struct starpu_perf_knob_group * __kg_starpu_global;
+static struct starpu_perf_knob_group * __kg_starpu_worker__per_worker;
+
+static void global_knobs__set(const struct starpu_perf_knob * const knob, void *context, const struct starpu_perf_knob_value * const value)
+{
+	/* context is not used for global knobs */
+	STARPU_ASSERT(context == NULL);
+	(void)context;
+
+	if (knob->id == __g_calibrate_knob)
+	{
+		_starpu_set_calibrate_flag((unsigned)value->val_int32_t);
+	}
+	else if (knob->id == __g_enable_catch_signal_knob)
+	{
+		_starpu_set_catch_signals(!!value->val_int32_t);
+	}
+	else
+	{
+		STARPU_ASSERT(0);
+		abort();
+	}
+}
+static void global_knobs__get(const struct starpu_perf_knob * const knob, void *context,       struct starpu_perf_knob_value * const value)
+{
+	/* context is not used for global knobs */
+	STARPU_ASSERT(context == NULL);
+	(void)context;
+
+	if (knob->id == __g_calibrate_knob)
+	{
+		value->val_int32_t = (int32_t)_starpu_get_calibrate_flag();
+	}
+	else if (knob->id == __g_enable_catch_signal_knob)
+	{
+		value->val_int32_t = _starpu_get_catch_signals();
+	}
+	else
+	{
+		STARPU_ASSERT(0);
+		abort();
+	}
+}
+
+void worker_knobs__set(const struct starpu_perf_knob * const knob, void *context, const struct starpu_perf_knob_value * const value)
+{
+	const unsigned workerid = *(unsigned *)context;
+	struct _starpu_worker * const worker = _starpu_get_worker_struct(workerid);
+	if (knob->id == __w_bind_to_pu_knob)
+	{
+		STARPU_ASSERT(value->val_int32_t >= 0);
+		worker->bindid_requested = value->val_int32_t;
+	}
+	else if (knob->id == __w_enable_worker_knob)
+	{
+		worker->enable_knob = !!value->val_int32_t;
+	}
+	else
+	{
+		STARPU_ASSERT(0);
+		abort();
+	}
+}
+void worker_knobs__get(const struct starpu_perf_knob * const knob, void *context,       struct starpu_perf_knob_value * const value)
+{
+	const unsigned workerid = *(unsigned *)context;
+	struct _starpu_worker * const worker = _starpu_get_worker_struct(workerid);
+	if (knob->id == __w_bind_to_pu_knob)
+	{
+		value->val_int32_t = worker->bindid;
+	}
+	else if (knob->id == __w_enable_worker_knob)
+	{
+		value->val_int32_t = worker->enable_knob;
+	}
+	else
+	{
+		STARPU_ASSERT(0);
+		abort();
+	}
+}
+
+void _starpu__workers_c__register_knobs(void)
+{
+	{
+		const enum starpu_perf_knob_scope scope = starpu_perf_knob_scope_global;
+		__kg_starpu_global = _starpu_perf_knob_group_register(scope, global_knobs__set, global_knobs__get);
+		__STARPU_PERF_KNOB_REG("starpu.global", __kg_starpu_global, g_calibrate_knob, int32, "enable or disable performance models calibration (override STARPU_CALIBRATE env var)");
+		__STARPU_PERF_KNOB_REG("starpu.global", __kg_starpu_global, g_enable_catch_signal_knob, int32, "enable or disable signal catching (override STARPU_CATCH_SIGNALS env var)");
+	}
+
+	{
+		const enum starpu_perf_knob_scope scope = starpu_perf_knob_scope_per_worker;
+		__kg_starpu_worker__per_worker = _starpu_perf_knob_group_register(scope, worker_knobs__set, worker_knobs__get);
+		__STARPU_PERF_KNOB_REG("starpu.worker", __kg_starpu_worker__per_worker, w_bind_to_pu_knob, int32, "bind worker to PU (PU logical number, override StarPU binding env vars)");
+		__STARPU_PERF_KNOB_REG("starpu.worker", __kg_starpu_worker__per_worker, w_enable_worker_knob, int32, "enable assigning task to that worker (1:Enabled | [0:Disabled])");
+	}
+
+#if 0
+	{
+		const enum starpu_perf_knob_scope scope = starpu_perf_knob_scope_per_scheduler;
+		__kg_starpu_worker__per_scheduler = _starpu_perf_knob_group_register(scope, sched_knobs__set, sched_knobs__get);
+	}
+#endif
+}
+
 /* acquire/release semantic for concurrent initialization/de-initialization */
 static starpu_pthread_mutex_t init_mutex = STARPU_PTHREAD_MUTEX_INITIALIZER;
 static starpu_pthread_cond_t init_cond = STARPU_PTHREAD_COND_INITIALIZER;
@@ -77,6 +192,11 @@ int _starpu_worker_parallel_blocks;
 static int *my_argc = 0;
 static char ***my_argv = NULL;
 
+void _starpu__workers_c__register_kobs(void)
+{
+	/* TODO */
+}
+
 /* Initialize value of static argc and argv, called when the process begins
  */
 void _starpu_set_argc_argv(int *argc_param, char ***argv_param)
@@ -325,6 +445,9 @@ static inline int _starpu_can_use_nth_implementation(enum starpu_worker_archtype
 /* must be called with sched_mutex locked to protect state_blocked_in_parallel */
 int starpu_worker_can_execute_task(unsigned workerid, struct starpu_task *task, unsigned nimpl)
 {
+	if (!_starpu_config.workers[workerid].enable_knob)
+		return 0;
+
 	/* if the worker is blocked in a parallel ctx don't submit tasks on it */
 #ifdef STARPU_DEVEL
 #warning FIXME: this is very expensive, while can_execute is supposed to be not very costly so schedulers can call it a lot
@@ -341,6 +464,9 @@ int starpu_worker_can_execute_task(unsigned workerid, struct starpu_task *task,
 /* must be called with sched_mutex locked to protect state_blocked_in_parallel */
 int starpu_worker_can_execute_task_impl(unsigned workerid, struct starpu_task *task, unsigned *impl_mask)
 {
+	if (!_starpu_config.workers[workerid].enable_knob)
+		return 0;
+
 	/* if the worker is blocked in a parallel ctx don't submit tasks on it */
 	if(starpu_worker_is_blocked_in_parallel(workerid))
 		return 0;
@@ -392,6 +518,9 @@ int starpu_worker_can_execute_task_impl(unsigned workerid, struct starpu_task *t
 /* must be called with sched_mutex locked to protect state_blocked */
 int starpu_worker_can_execute_task_first_impl(unsigned workerid, struct starpu_task *task, unsigned *nimpl)
 {
+	if (!_starpu_config.workers[workerid].enable_knob)
+		return 0;
+
 	/* if the worker is blocked in a parallel ctx don't submit tasks on it */
 	if(starpu_worker_is_blocked_in_parallel(workerid))
 		return 0;
@@ -432,6 +561,9 @@ int starpu_worker_can_execute_task_first_impl(unsigned workerid, struct starpu_t
 
 int starpu_combined_worker_can_execute_task(unsigned workerid, struct starpu_task *task, unsigned nimpl)
 {
+	if (!_starpu_config.workers[workerid].enable_knob)
+		return 0;
+
 	/* TODO: check that the task operand sizes will fit on that device */
 
 	struct starpu_codelet *cl = task->cl;
@@ -621,6 +753,8 @@ void _starpu_worker_init(struct _starpu_worker *workerarg, struct _starpu_machin
 	workerarg->state_unblock_in_parallel_ack = 0;
 	workerarg->block_in_parallel_ref_count = 0;
 	_starpu_perf_counter_sample_init(&workerarg->perf_counter_sample, starpu_perf_counter_scope_per_worker);
+	workerarg->enable_knob = 1;
+	workerarg->bindid_requested = -1;
 
 	/* cpu_set/hwloc_cpu_set/hwloc_obj initialized in topology.c */
 }
@@ -1171,6 +1305,7 @@ static void _starpu_build_tree(void)
 #endif
 }
 
+static starpu_pthread_mutex_t sig_handlers_mutex = STARPU_PTHREAD_MUTEX_INITIALIZER;
 static void (*act_sigint)(int);
 static void (*act_sigsegv)(int);
 static void (*act_sigtrap)(int);
@@ -1185,16 +1320,25 @@ void _starpu_handler(int sig)
 #endif
 	if (sig == SIGINT)
 	{
-		signal(SIGINT, act_sigint);
+		void (*sig_act)(int) = act_sigint;
+		if (sig_act == NULL)
+			sig_act = SIG_DFL;
+		signal(SIGINT, sig_act);
 	}
 	if (sig == SIGSEGV)
 	{
-		signal(SIGSEGV, act_sigsegv);
+		void (*sig_act)(int) = act_sigsegv;
+		if (sig_act == NULL)
+			sig_act = SIG_DFL;
+		signal(SIGSEGV, sig_act);
 	}
 #ifdef SIGTRAP
 	if (sig == SIGTRAP)
 	{
-		signal(SIGTRAP, act_sigtrap);
+		void (*sig_act)(int) = act_sigtrap;
+		if (sig_act == NULL)
+			sig_act = SIG_DFL;
+		signal(SIGTRAP, sig_act);
 	}
 #endif
 #ifdef STARPU_VERBOSE
@@ -1207,12 +1351,54 @@ void _starpu_catch_signals(void)
 {
 	if (_starpu_config.conf.catch_signals == 1)
 	{
-		act_sigint  = signal(SIGINT, _starpu_handler);
-		act_sigsegv = signal(SIGSEGV, _starpu_handler);
+		static void (*old_sig_act)(int);
+		old_sig_act  = signal(SIGINT, _starpu_handler);
+		if (old_sig_act != _starpu_handler)
+			act_sigint  = old_sig_act;
+
+		old_sig_act = signal(SIGSEGV, _starpu_handler);
+		if (old_sig_act != _starpu_handler)
+			act_sigsegv  = old_sig_act;
 #ifdef SIGTRAP
-		act_sigtrap = signal(SIGTRAP, _starpu_handler);
+		old_sig_act = signal(SIGTRAP, _starpu_handler);
+		if (old_sig_act != _starpu_handler)
+			act_sigtrap  = old_sig_act;
 #endif
 	}
+	else
+	{
+		if (act_sigint != NULL)
+		{
+			signal(SIGINT, act_sigint);
+			act_sigint = NULL;
+		}
+
+		if (act_sigsegv != NULL)
+		{
+			signal(SIGSEGV, act_sigsegv);
+			act_sigsegv = NULL;
+		}
+#ifdef SIGTRAP
+		if (act_sigtrap != NULL)
+		{
+			signal(SIGTRAP, act_sigtrap);
+			act_sigtrap = NULL;
+		}
+#endif
+	}
+}
+
+void _starpu_set_catch_signals(int do_catch_signal)
+{
+	STARPU_PTHREAD_MUTEX_LOCK(&sig_handlers_mutex);
+	_starpu_config.conf.catch_signals = do_catch_signal;
+	_starpu_catch_signals();
+	STARPU_PTHREAD_MUTEX_UNLOCK(&sig_handlers_mutex);
+}
+
+int _starpu_get_catch_signals(void)
+{
+	return _starpu_config.conf.catch_signals;
 }
 
 int starpu_init(struct starpu_conf *user_conf)
@@ -1463,6 +1649,7 @@ int starpu_initialize(struct starpu_conf *user_conf, int *argc, char ***argv)
 
 	_starpu_initialize_registered_performance_models();
 	_starpu_perf_counter_init();
+	_starpu_perf_knob_init();
 
 #if defined(STARPU_USE_CUDA) || defined(STARPU_SIMGRID)
 	_starpu_cuda_init();
@@ -1775,6 +1962,8 @@ void starpu_shutdown(void)
 #ifdef STARPU_OPENMP
 	_starpu_omp_dummy_shutdown();
 #endif
+	_starpu_perf_knob_exit();
+	_starpu_perf_counter_exit();
 	_starpu_close_debug_logfile();
 
 	_starpu_keys_initialized = 0;

+ 5 - 0
src/core/workers.h

@@ -205,6 +205,8 @@ LIST_TYPE(_starpu_worker,
 	int32_t __w_total_executed__value;
 	double __w_cumul_execution_time__value;
 
+	int enable_knob;
+	int bindid_requested;
 );
 
 struct _starpu_combined_worker
@@ -1201,6 +1203,9 @@ int starpu_wake_worker_relax_light(int workerid);
  */
 void _starpu_worker_refuse_task(struct _starpu_worker *worker, struct starpu_task *task);
 
+void _starpu_set_catch_signals(int do_catch_signal);
+int _starpu_get_catch_signals(void);
+
 /* @}*/
 
 #endif // __WORKERS_H__

+ 14 - 0
src/drivers/driver_common/driver_common.c

@@ -43,6 +43,20 @@ void _starpu_driver_start_job(struct _starpu_worker *worker, struct _starpu_job
 	int workerid = worker->workerid;
 	unsigned calibrate_model = 0;
 
+	if (worker->bindid_requested != -1)
+	{
+		typedef unsigned __attribute__((__may_alias__)) alias_unsigned;
+		typedef int __attribute__((__may_alias__)) alias_int;
+
+		unsigned raw_bindid_requested = starpu_xchg((alias_unsigned *)&worker->bindid_requested, -1);
+		int bindid_requested = *(alias_int *)&raw_bindid_requested;
+
+		if (bindid_requested != -1)
+		{
+			worker->bindid = bindid_requested;
+			_starpu_bind_thread_on_cpu(worker->bindid, worker->workerid, NULL);
+		}
+	}
 	if (cl->model && cl->model->benchmarking)
 		calibrate_model = 1;