StarPU proposes a platform for constructing Scheduling Contexts, for deleting and modifying them dynamically. A parallel kernel, can thus be isolated into a scheduling context and interferences between several parallel kernels are avoided. If the user knows exactly how many workers each scheduling context needs, he can assign them to the contexts at their creation time or modify them during the execution of the program.

The Scheduling Context Hypervisor Plugin is available for the users who do not dispose of a regular parallelism, who cannot not know in advance the exact size of the context and need to resize the contexts according to the behavior of the parallel kernel. The Hypervisor receives information from StarPU concerning the execution of the tasks, the efficiency of the resources, etc. and it decides accordingly when and how the contexts can be resized. Basic strategies of resizing scheduling contexts already exist but a platform for implementing additional custom ones is available.

Managing the hypervisor
=======================

There is a single hypervisor that is in charge of resizing contexts and the resizing strategy is chosen at the initialization of the hypervisor. A single resize can be done at a time.

 -- Function: struct starpu_performance_counters* sched_ctx_hypervisor_init (struct starpu_sched_ctx_hypervisor_policy * POLICY)
     Initializes the hypervisor to use the strategy provided as parameter and creates the performance counters (see Performance Counters). These performance counters represent actually some callbacks that will be used by the contexts to notify the information needed by the hypervisor.

Note: The Hypervisor is actually a worker that takes this role once certain conditions trigger the resizing process (there is no additional thread assigned to the hypervisor).

 -- Function: void sched_ctx_hypervisor_shutdown (void)
     The hypervisor and all information is freed. There is no synchronization between this function and starpu_shutdown. Thus, this should be done after starpu_shutdown(), because the performance counters will still need allocated callback functions.

Registering Scheduling Contexts to the hypervisor
=================================================

Scheduling Contexts that have to be resized by the hypervisor must be first registered to the hypervisor. Whenever we want to exclude contexts from the resizing process we have to unregister them from the hypervisor.

 -- Function: void sched_ctx_hypervisor_register_ctx (unsigned SCHED_CTX, double TOTAL_FLOPS)
     Register the context to the hypervisor, and indicate the number of flops the context will execute (needed for Gflops rate based strategy see Resizing strategies or any other custom strategy needing it, for the others we can pass 0.0)

 -- Function: void sched_ctx_hypervisor_unregister_ctx (unsigned SCHED_CTX)
     Unregister the context from the hypervisor

The user's input in the resizing process
========================================

The user can totally forbid the resizing of a certain context or can then change his mind and allow it (in this case the resizing is managed by the hypervisor, that can forbid it or allow it)

 -- Function: void sched_ctx_hypervisor_stop_resize (unsigned SCHED_CTX)
     Forbid resizing of a context

 -- Function: void sched_ctx_hypervisor_start_resize (unsigned SCHED_CTX)
     Allow resizing of a context

The user can then provide information to the hypervisor concerning the conditions of resizing.

 -- Function: void sched_ctx_hypervisor_ioctl (unsigned SCHED_CTX, ...)
     Inputs conditions to the context sched_ctx with the following arguments.  The argument list must be zero-terminated.

      -- Macro: HYPERVISOR_MAX_IDLE
          This macro is used when calling sched_ctx_hypervisor_ioctl and must be followed by 3 arguments: an array of int for the workerids to apply the condition, an int to indicate the size of the array, and a double value indicating the maximum idle time allowed for a worker before the resizing process should be triggered

      -- Macro: HYPERVISOR_PRIORITY
          This macro is used when calling sched_ctx_hypervisor_ioctl and must be followed by 3 arguments: an array of int for the workerids to apply the condition, an int to indicate the size of the array, and an int value indicating the priority of the workers previously mentioned. The workers with the smallest priority are moved the first.

      -- Macro: HYPERVISOR_MIN_WORKERS
          This macro is used when calling sched_ctx_hypervisor_ioctl and must be followed by 1 argument(int) indicating the minimum number of workers a context should have, underneath this limit the context cannot execute.

      -- Macro: HYPERVISOR_MAX_WORKERS
          This macro is used when calling sched_ctx_hypervisor_ioctl and must be followed by 1 argument(int) indicating the maximum number of workers a context should have, above this limit the context would not be able to scale

      -- Macro: HYPERVISOR_GRANULARITY
          This macro is used when calling sched_ctx_hypervisor_ioctl and must be followed by 1 argument(int) indicating the granularity of the resizing process (the number of workers should be moved from the context once it is resized) This parameter is ignore for the Gflops rate based strategy see Resizing strategies, the number of workers that have to be moved is calculated by the strategy.

      -- Macro: HYPERVISOR_FIXED_WORKERS
          This macro is used when calling sched_ctx_hypervisor_ioctl and must be followed by 2 arguments: an array of int for the workerids to apply the condition and an int to indicate the size of the array. These workers are not allowed to be moved from the context.

      -- Macro: HYPERVISOR_MIN_TASKS
          This macro is used when calling sched_ctx_hypervisor_ioctl and must be followed by 1 argument (int) that indicated the minimum number of tasks that have to be executed before the context could be resized. This parameter is ignored for the Application Driven strategy see Resizing strategies where the user indicates exactly when the resize should be done.

      -- Macro: HYPERVISOR_NEW_WORKERS_MAX_IDLE
          This macro is used when calling sched_ctx_hypervisor_ioctl and must be followed by 1 argument, a double value indicating the maximum idle time allowed for workers that have just been moved from other contexts in the current context.

      -- Macro: HYPERVISOR_TIME_TO_APPLY
          This macro is used when calling sched_ctx_hypervisor_ioctl and must be followed by 1 argument (int) indicating the tag an executed task should have such that this configuration should be taken into account.

Resizing strategies
===================

The plugin proposes several strategies for resizing the scheduling context.

The Application driven strategy uses the user's input concerning the moment when he wants to resize the contexts. Thus, the users tags the task that should trigger the resizing process. We can set directly the corresponding field in the starpu_task data structure is hypervisor_tag or use the macro STARPU_HYPERVISOR_TAG in starpu_insert_task function.

     task.hypervisor_tag = 2;

or

     {starpu_insert_task(&codelet,
     		    ...,
     		    STARPU_HYPERVISOR_TAG, 2,
                         0);
     }

The the user has to indicate that when a task with the specified tag is executed the contexts should resize.

     sched_ctx_hypervisor_resize(sched_ctx, 2);

The user can use the same tag to change the resizing configuration of the contexts if he considers it necessary.

     sched_ctx_hypervisor_ioctl(sched_ctx,
                               HYPERVISOR_MIN_WORKERS, 6,
                               HYPERVISOR_MAX_WORKERS, 12,
                               HYPERVISOR_TIME_TO_APPLY, 2,
                               NULL);

The Idleness based strategy resizes the scheduling contexts every time one of their workers stays idle for a period longer than the one imposed by the user (see The user's input in the resizing process)

     int workerids[3] = {1, 3, 10};
     int workerids2[9] = {0, 2, 4, 5, 6, 7, 8, 9, 11};
     sched_ctx_hypervisor_ioctl(sched_ctx_id,
                                HYPERVISOR_MAX_IDLE, workerids, 3, 10000.0,
     			   HYPERVISOR_MAX_IDLE, workerids2, 9, 50000.0,
                                NULL);

The Gflops rate based strategy resizes the scheduling contexts such that they all finish at the same time. The velocity of each of them is considered and once one of them is significantly slower the resizing process is triggered. In order to do these computations the user has to input the total number of instructions needed to be executed by the parallel kernels and the number of instruction to be executed by each task. The number of flops to be executed by a context are passed as parameter when they are registered to the hypervisor, (sched_ctx_hypervisor_register_ctx(sched_ctx_id, flops)) and the one to be executed by each task are passed when the task is submitted. The corresponding field in the starpu_task data structure is flops and the corresponding macro in starpu_insert_task function is STARPU_FLOPS. When the task is executed the resizing process is triggered.

     task.flops = 100;

or

     starpu_insert_task(&codelet,
     		    ...,
     		    STARPU_FLOPS, 100,
                         0);

Performance Counters
====================

The Scheduling Context Hypervisor Plugin provides a series of performance counters to StarPU. By incrementing them, StarPU can help the hypervisor in the resizing decision making process.

 -- Data Type: struct starpu_performance_counters

     void (*notify_idle_cycle)(unsigned sched_ctx_id, int worker, double idle_time)
          Informs the hypervisor for how long a worker has been idle in the specified context

     void (*notify_idle_end)(unsigned sched_ctx_id, int worker)
          Informs the hypervisor that after a period of idle, the worker has just executed a task in the specified context. The idle counter it though reset.

     void (*notify_pushed_task)(unsigned sched_ctx_id, int worker)
          Notifies the hypervisor a task has been scheduled on the queue of the worker corresponding to the specified context

     void (*notify_poped_task)(unsigned sched_ctx_id, int worker, double flops)
          Informs the hypervisor a task executing a specified number of instructions has been poped from the worker

     void (*notify_post_exec_hook)(unsigned sched_ctx_id, int taskid)
          Notifies the hypervisor a task has just been executed

TODO maybe they should be hidden to the user

Defining a new hypervisor policy
================================

Hypervisor Policy API
---------------------

While Scheduling Context Hypervisor Plugin comes with a variety of resizing policies (see Resizing strategies), it may sometimes be desirable to implement custom policies to address specific problems.  The API described below allows users to write their own resizing policy.

 -- Data Type: struct starpu_sched_ctx_hypervisor_policy
     This structure contains all the methods that implement a hypervisor resizing policy.

     const char* name
          Indicates the name of the policy, if there is not a custom policy, the policy corresponding to this name will be used by the hypervisor

     unsigned custom
          Indicates whether the policy is custom or not

     void (*handle_idle_cycle)(unsigned sched_ctx_id, int worker)
          It is called whenever the indicated worker executes another idle cycle in sched_ctx

     void (*handle_pushed_task)(unsigned sched_ctx_id, int worker)
          It is called whenever a task is pushed on the worker's queue corresponding to the context sched_ctx

     void (*handle_poped_task)(unsigned sched_ctx_id, int worker)
          It is called whenever a task is poped from the worker's queue corresponding to the context sched_ctx

     void (*handle_idle_end)(unsigned sched_ctx_id, int worker)
          It is called whenever a task is executed on the indicated worker and context after a long period of idle time

     void (*handle_post_exec_hook)(unsigned sched_ctx_id, struct starpu_htbl32_node* resize_requests, int task_tag)
          It is called whenever a tag task has just been executed. The table of resize requests is provided as well as the tag

The Hypervisor provides also a structure with configuration information of each context, which can be used to construct new resize strategies.

 -- Data Type: struct starpu_sched_ctx_hypervisor_policy_config
     This structure contains all configuration information of a context

     int min_nworkers
          Indicates the minimum number of workers needed by the context

     int max_nworkers
          Indicates the maximum number of workers needed by the context

     int granularity
          Indicates the workers granularity of the context

     int priority[STARPU_NMAXWORKERS]
          Indicates the priority of each worker in the context

     double max_idle[STARPU_NMAXWORKERS]
          Indicates the maximum idle time accepted before a resize is triggered

     int fixed_workers[STARPU_NMAXWORKERS]
          Indicates which workers can be moved and which ones are fixed

     double new_workers_max_idle
          Indicates the maximum idle time accepted before a resize is triggered for the workers that just arrived in the new context

Additionally, the hypervisor provides a structure with information obtained from StarPU by means of the performance counters

 -- Data Type: struct starpu_sched_ctx_hypervisor_wrapper
     This structure is a wrapper of the contexts available in StarPU and contains all information about a context obtained by incrementing the performance counters

     unsigned sched_ctx
          The context wrapped

     struct starpu_sched_ctx_hypervisor_policy_config *config
          The corresponding resize configuration

     double current_idle_time[STARPU_NMAXWORKERS]
          The idle time counter of each worker of the context

     int pushed_tasks[STARPU_NMAXWORKERS]
          The number of pushed tasks of each worker of the context

     int poped_tasks[STARPU_NMAXWORKERS]
          The number of poped tasks of each worker of the context

     double total_flops
          The total number of flops to execute by the context

     double total_elapsed_flops[STARPU_NMAXWORKERS]
          The number of flops executed by each workers of the context

     double elapsed_flops[STARPU_NMAXWORKERS]
          The number of flops executed by each worker of the context from last resize

     double remaining_flops
          The number of flops that still have to be executed by the workers in the context

     double start_time
          The time when he started executed

     struct starpu_sched_ctx_hypervisor_resize_ack resize_ack
          The structure confirming the last resize finished and a new one can be done

 -- Data Type: struct starpu_sched_ctx_hypervisor_resize_ack
     This structures checks if the workers moved to another context are actually taken into account in that context

     int receiver_sched_ctx
          The context receiving the new workers

     int *moved_workers
          The workers moved to the receiver context

     int nmoved_workers
          The number of workers moved

     int *acked_workers
          If the value corresponding to a worker is 1, this one is taken into account in the new context if 0 not yet

The following functions can be used in the resizing strategies.

 -- Function: void sched_ctx_hypervisor_move_workers (unsigned SENDER_SCHED_CTX, unsigned RECEIVER_SCHED_CTX, int *WORKERS_TO_MOVE, unsigned NWORKERS_TO_MOVE, unsigned NOW);
     Moves workers from one context to another

 -- Function: struct starpu_sched_ctx_hypervisor_policy_config * sched_ctx_hypervisor_get_config (unsigned SCHED_CTX);
     Returns the configuration structure of a context

 -- Function: int * sched_ctx_hypervisor_get_sched_ctxs ();
     Gets the contexts managed by the hypervisor

 -- Function: int sched_ctx_hypervisor_get_nsched_ctxs ();
     Gets the number of contexts managed by the hypervisor

 -- Function: struct starpu_sched_ctx_hypervisor_wrapper * sched_ctx_hypervisor_get_wrapper (unsigned SCHED_CTX);
     Returns the wrapper corresponding the context sched_ctx

 -- Function: double sched_ctx_hypervisor_get_elapsed_flops_per_sched_ctx (struct starpu_sched_ctx_hypervisor_wrapper * SC_W);
     Returns the flops of a context elapsed from the last resize

 -- Function: char * sched_ctx_hypervisor_get_policy ();
     Returns the name of the resizing policy the hypervisor uses

Hypervisor example
------------------

     struct starpu_sched_ctx_hypervisor_policy dummy_policy =
     {
            .handle_poped_task = dummy_handle_poped_task,
            .handle_pushed_task = dummy_handle_pushed_task,
            .handle_idle_cycle = dummy_handle_idle_cycle,
            .handle_idle_end = dummy_handle_idle_end,
            .handle_post_exec_hook = dummy_handle_post_exec_hook,
            .custom = 1,
            .name = ``dummy''
     };