[b9537e6] | 1 | \chapter{Previous Work}\label{existing} |
---|
| 2 | Scheduling is a topic with a very long history, predating its use in computer science. As such, early work in computed science was inspired from other fields and focused principally on solving scheduling upfront rather that as the system is running. |
---|
| 3 | |
---|
| 4 | \section{Naming Convention} |
---|
| 5 | Scheduling has been studied by various different communities concentrating on different incarnation of the same problems. As a result, their is no real naming convention for scheduling that is respected across these communities. For this document, I will use the term \newterm{task} to refer to the abstract objects being scheduled and the term \newterm{worker} to refer to the objects which will execute these tasks. |
---|
| 6 | |
---|
| 7 | \section{Static Scheduling} |
---|
| 8 | Static schedulers require that programmers explicitly and exhaustively specify dependencies among tasks in order to schedule them. The scheduler then processes this input ahead of time and producess a \newterm{schedule} to which the system can later adhere. An example application for these schedulers |
---|
| 9 | |
---|
| 10 | In general, static schedulers are less relavant to this project since they require input from the programmers that \CFA does not have as part of its concurrency semantic. |
---|
| 11 | \todo{Rate-monotonic scheduling} |
---|
| 12 | |
---|
| 13 | |
---|
| 14 | \section{Dynamic Scheduling} |
---|
| 15 | It may be difficult to fulfill the requirements of static scheduler if dependencies are be conditionnal. In this case, it may be preferable to detect dependencies at runtime. This detection effectively takes the form of halting or suspending a task with unfulfilled dependencies and adding one or more new task(s) to the system. The new task(s) have the responsability of adding the dependent task back in the system once completed. As a consequence, the scheduler may have an incomplete view of the system, seeing only tasks we no pending dependencies. Schedulers that support this detection at runtime are referred to as \newterm{Dynamic Schedulers}. |
---|
| 16 | |
---|
| 17 | \subsection{Explicitly Informed Dynamic Schedulers} |
---|
| 18 | While dynamic schedulers do not have access to an exhaustive list of dependencies for a task, they may require to provide more or less information about each task, including for example: expected duration, required ressources, relative importance, etc. The scheduler can then use this information to direct the scheduling decisions. \cit{Examples of schedulers with more information} Precisely providing this information can be difficult for programmers, especially \emph{predicted} behaviour, and the scheduler may need to support some amount of imprecision in the provided information. For example, specifying that a tasks takes approximately 5 seconds to complete, rather than exactly 5 seconds. User provided information can also become a significant burden depending how the effort to provide the information scales with the number of tasks and there complexity. For example, providing an exhaustive list of files read by 5 tasks is an easier requirement the providing an exhaustive list of memory addresses accessed by 10'000 distinct tasks. |
---|
| 19 | |
---|
| 20 | Since the goal of this thesis is to provide a scheduler as a replacement for \CFA's existing \emph{uninformed} scheduler, Explicitly Informed schedulers are less relevant to this project. Nevertheless, some strategies are worth mentionnding. |
---|
| 21 | |
---|
| 22 | \subsubsection{Prority Scheduling} |
---|
| 23 | A commonly used information that schedulers used to direct the algorithm is priorities. Each Task is given a priority and higher-priority tasks are preferred to lower-priority ones. The simplest priority scheduling algorithm is to simply require that every task have a distinct pre-established priority and always run the available task with the highest priority. Asking programmers to provide an exhaustive set of unique priorities can be prohibitive when the system has a large number of tasks. It can therefore be diserable for schedulers to support tasks with identical priorities and/or automatically setting and adjusting priorites for tasks. |
---|
| 24 | |
---|
| 25 | \subsection{Uninformed and Self-Informed Dynamic Schedulers} |
---|
| 26 | Several scheduling algorithms do not require programmers to provide additionnal information on each task, and instead make scheduling decisions based solely on internal state and/or information implicitly gathered by the scheduler. |
---|
| 27 | |
---|
| 28 | |
---|
| 29 | \subsubsection{Feedback Scheduling} |
---|
| 30 | As mentionned, Schedulers may also gather information about each tasks to direct their decisions. This design effectively moves the scheduler to some extent into the realm of \newterm{Control Theory}\cite{wiki:controltheory}. This gathering does not generally involve programmers and as such does not increase programmer burden the same way explicitly provided information may. However, some feedback schedulers do offer the option to programmers to offer additionnal information on certain tasks, in order to direct scheduling decision. The important distinction being whether or not the scheduler can function without this additionnal information. |
---|
| 31 | |
---|
| 32 | Feedback scheduler |
---|
| 33 | |
---|
| 34 | |
---|
| 35 | \section{Work Stealing} |
---|
| 36 | One of the most popular scheduling algorithm in practice (see~\ref{existing:prod}) is work-stealing. This idea, introduce by \cite{DBLP:conf/fpca/BurtonS81}, effectively has each worker work on its local tasks first, but allows the possibility for other workers to steal local tasks if they run out of tasks. \cite{DBLP:conf/focs/Blumofe94} introduced the more familiar incarnation of this, where each workers has queue of tasks to accomplish and workers without tasks steal tasks from random workers. (The Burton and Sleep algorithm had trees of tasks and stole only among neighbours). Blumofe and Leiserson also prove worst case space and time requirements for well-structured computations. |
---|
| 37 | |
---|
| 38 | Many variations of this algorithm have been proposed over the years\cite{DBLP:journals/ijpp/YangH18}, both optmizations of existing implementations and approaches that account for new metrics. |
---|
| 39 | |
---|
| 40 | \paragraph{Granularity} A significant portion of early Work Stealing research was concentrating on \newterm{Implicit Parellelism}\cite{wiki:implicitpar}. Since the system was responsible to split the work, granularity is a challenge that cannot be left to the programmers (as opposed to \newterm{Explicit Parellelism}\cite{wiki:explicitpar} where the burden can be left to programmers). In general, fine granularity is better for load balancing and coarse granularity reduces communication overhead. The best performance generally means finding a middle ground between the two. Several methods can be employed, but I believe these are less relevant for threads, which are generally explicit and more coarse grained. |
---|
| 41 | |
---|
| 42 | \paragraph{Task Placement} Since modern computers rely heavily on cache hierarchies\cit{Do I need a citation for this}, migrating tasks from one core to another can be . \cite{DBLP:journals/tpds/SquillanteL93} |
---|
| 43 | |
---|
[c04a19e] | 44 | \todo{The survey is not great on this subject} |
---|
[b9537e6] | 45 | |
---|
| 46 | \paragraph{Complex Machine Architecture} Another aspect that has been looked at is how well Work Stealing is applicable to different machine architectures. |
---|
| 47 | |
---|
| 48 | \subsection{Theoretical Results} |
---|
| 49 | There is also a large body of research on the theoretical aspects of work stealing. These evaluate, for example, the cost of migration\cite{DBLP:conf/sigmetrics/SquillanteN91,DBLP:journals/pe/EagerLZ86}, how affinity affects performance\cite{DBLP:journals/tpds/SquillanteL93,DBLP:journals/mst/AcarBB02,DBLP:journals/ipl/SuksompongLS16} and theoretical models for heterogenous systems\cite{DBLP:journals/jpdc/MirchandaneyTS90,DBLP:journals/mst/BenderR02,DBLP:conf/sigmetrics/GastG10}. \cite{DBLP:journals/jacm/BlellochGM99} examine the space bounds of Work Stealing and \cite{DBLP:journals/siamcomp/BerenbrinkFG03} show that for underloaded systems, the scheduler will complete computations in finite time, \ie is \newterm{stable}. Others show that Work-Stealing is applicable to various scheduling contexts\cite{DBLP:journals/mst/AroraBP01,DBLP:journals/anor/TchiboukdjianGT13,DBLP:conf/isaac/TchiboukdjianGTRB10,DBLP:conf/ppopp/AgrawalLS10,DBLP:conf/spaa/AgrawalFLSSU14}. \cite{DBLP:conf/ipps/ColeR13} also studied how Randomized Work Stealing affects false sharing among tasks. |
---|
| 50 | |
---|
| 51 | However, as \cite{DBLP:journals/ijpp/YangH18} highlights, it is worth mentionning that this theoretical research has mainly focused on ``fully-strict'' computations, \ie workloads that can be fully represented with a Direct Acyclic Graph. It is unclear how well these distributions represent workloads in real world scenarios. |
---|
| 52 | |
---|
| 53 | \section{Preemption} |
---|
| 54 | One last aspect of scheduling worth mentionning is preemption since many schedulers rely on it for some of their guarantees. Preemption is the idea of interrupting tasks that have been running for too long, effectively injecting suspend points in the applications. There are multiple techniques to achieve this but they all aim to have the effect of guaranteeing that suspend points in a task are never further apart than some fixed duration. While this helps schedulers guarantee that no tasks will unfairly monopolize a worker, preemption can effectively added to any scheduler. Therefore, the only interesting aspect of preemption for the design of scheduling is whether or not to require it. |
---|
| 55 | |
---|
| 56 | \section{Schedulers in Production}\label{existing:prod} |
---|
| 57 | This section will show a quick overview of several schedulers which are generally available a the time of writing. While these schedulers don't necessarily represent to most recent advances in scheduling, they are what is generally accessible to programmers. As such, I believe that these schedulers are at least as relevant as those presented in published work. I chose both schedulers that operating in kernel space and in user space, as both can offer relevant insight for this project. However, I did not list any schedulers aimed for real-time applications, as these have constraints that are much stricter than what is needed for this project. |
---|
| 58 | |
---|
| 59 | \subsection{Operating System Schedulers} |
---|
| 60 | Operating System Schedulers tend to be fairly complex schedulers, they generally support some amount of real-time, aim to balance interactive and non-interactive tasks and support for multiple users sharing hardware without requiring these users to cooperate. Here are more details on a few schedulers used in the common operating systems: Linux, FreeBsd, Microsoft Windows and Apple's OS X. The information is less complete for operating systems behind closed source. |
---|
| 61 | |
---|
| 62 | \paragraph{Linux's CFS} |
---|
| 63 | The default scheduler used by Linux (the Completely Fair Scheduler)\cite{MAN:linux/cfs,MAN:linux/cfs2} is a feedback scheduler based on CPU time. For each processor, it constructs a Red-Black tree of tasks waiting to run, ordering them by amount of CPU time spent. The scheduler schedules the task that has spent the least CPU time. It also supports the concept of \newterm{Nice values}, which are effectively multiplicative factors on the CPU time spent. The ordering of tasks is also impacted by a group based notion of fairness, where tasks belonging to groups having spent less CPU time are preferred to tasks beloning to groups having spent more CPU time. Linux achieves load-balancing by regularly monitoring the system state\cite{MAN:linux/cfs/balancing} and using some heuristic on the load (currently CPU time spent in the last millisecond plus decayed version of the previous time slots\cite{MAN:linux/cfs/pelt}.). |
---|
| 64 | |
---|
| 65 | \cite{DBLP:conf/eurosys/LoziLFGQF16} shows that Linux's CFS also does work-stealing to balance the workload of each processors, but the paper argues this aspect can be improved significantly. The issues highlighted sem to stem from Linux's need to support fairness across tasks \emph{and} across users\footnote{Enforcing fairness across users means, for example, that given two users: one with a single task and the other with one thousand tasks, the user with a single task does not receive one one thousandth of the CPU time.}, increasing the complexity. |
---|
| 66 | |
---|
| 67 | Linux also offers a FIFO scheduler, a real-time schedulerwhich runs the highest-priority task, and a round-robin scheduler, which is an extension of the fifo-scheduler that adds fixed time slices. \cite{MAN:linux/sched} |
---|
| 68 | |
---|
| 69 | \paragraph{FreeBSD} |
---|
| 70 | The ULE scheduler used in FreeBSD\cite{DBLP:conf/bsdcon/Roberson03} is a feedback scheduler similar to Linux's CFS. It uses different data structures and heuristics but also schedules according to some combination of CPU time spent and niceness values. It also periodically balances the load of the system(according to a different heuristic), but uses a simpler Work Stealing approach. |
---|
| 71 | |
---|
| 72 | \paragraph{Windows(OS)} |
---|
| 73 | Microsoft's Operating System's Scheduler\cite{MAN:windows/scheduler} is a feedback scheduler with priorities. It supports 32 levels of priorities, some of which are reserved for real-time and prviliged applications. It schedules tasks based on the highest priorities (lowest number) and how much cpu time each tasks have used. The scheduler may also temporarily adjust priorities after certain effects like the completion of I/O requests. |
---|
| 74 | |
---|
| 75 | \todo{load balancing} |
---|
| 76 | |
---|
| 77 | \paragraph{Apple OS X} |
---|
| 78 | Apple programming documentation describes its kernel scheduler as follows: |
---|
| 79 | \begin{displayquote} |
---|
| 80 | OS X is based on Mach and BSD. [...]. It contains an advanced scheduler based on the CMU Mach 3 scheduler. [...] Mach scheduling is based on a system of run queues at various priorities. |
---|
| 81 | |
---|
| 82 | \begin{flushright} |
---|
| 83 | -- Kernel Programming Guide \cite{MAN:apple/scheduler} |
---|
| 84 | \end{flushright} |
---|
| 85 | \end{displayquote} |
---|
| 86 | |
---|
| 87 | \todo{load balancing} |
---|
| 88 | |
---|
| 89 | \subsection{User-Level Schedulers} |
---|
| 90 | By comparison, user level schedulers tend to be simpler, gathering fewer metrics and avoid complex notions of fairness. Part of the simplicity is due to the fact that all tasks have the same user, and therefore cooperation is both feasible and probable. |
---|
| 91 | \paragraph{Go} |
---|
| 92 | Go's scheduler uses a Randomized Work Stealing algorithm that has a global runqueue(\emph{GRQ}) and each processor(\emph{P}) has both a fixed-size runqueue(\emph{LRQ}) and a high-priority next ``chair'' holding a single element.\cite{GITHUB:go,YTUBE:go} Preemption is present, but only at function call boundaries. |
---|
| 93 | |
---|
| 94 | The algorithm is as follows : |
---|
| 95 | \begin{enumerate} |
---|
| 96 | \item Once out of 61 times, directly pick 1 element from the \emph{GRQ}. |
---|
| 97 | \item If there is an item in the ``chair'' pick it. |
---|
| 98 | \item Else pick an item from the \emph{LRQ}. |
---|
| 99 | \item If it was empty steal (len(\emph{GRQ}) / \#of\emph{P}) + 1 items (max 256) from the \emph{GRQ}. |
---|
| 100 | \item If it was empty steal \emph{half} the \emph{LRQ} of another \emph{P} chosen randomly. |
---|
| 101 | \end{enumerate} |
---|
| 102 | |
---|
| 103 | \paragraph{Erlang} |
---|
| 104 | Erlang is a functionnal language that supports concurrency in the form of processes, threads that share no data. It seems to be some kind of Round-Robin Scheduler. It currently uses some mix of Work Sharing and Work Stealing to achieve load balancing\cite{:erlang}, where underloaded workers steal from other workers, but overloaded workers also push work to other workers. This migration logic seems to be directed by monitoring logic that evaluates the load a few times per seconds. |
---|
| 105 | |
---|
| 106 | \paragraph{Intel\textregistered ~Threading Building Blocks} |
---|
| 107 | \newterm{Thread Building Blocks}(TBB) is Intel's task parellelism\cite{wiki:taskparallel} framework. It runs tasks or \newterm{jobs}, schedulable objects that must always run to completion, on a pool of worker threads. TBB's scheduler is a variation of Randomized Work Stealing that also supports higher-priority graph-like dependencies\cite{MAN:tbb/scheduler}. It schedules tasks as follows (where \textit{t} is the last task completed): |
---|
| 108 | \begin{displayquote} |
---|
| 109 | \begin{enumerate} |
---|
| 110 | \item The task returned by \textit{t}\texttt{.execute()} |
---|
| 111 | \item The successor of t if \textit{t} was its last completed predecessor. |
---|
| 112 | \item A task popped from the end of the thread’s own deque. |
---|
| 113 | \item A task with affinity for the thread. |
---|
| 114 | \item A task popped from approximately the beginning of the shared queue. |
---|
| 115 | \item A task popped from the beginning of another randomly chosen thread’s deque. |
---|
| 116 | \end{enumerate} |
---|
| 117 | |
---|
| 118 | \begin{flushright} |
---|
| 119 | -- Intel\textregistered ~TBB documentation \cite{MAN:tbb/scheduler} |
---|
| 120 | \end{flushright} |
---|
| 121 | \end{displayquote} |
---|
| 122 | |
---|
| 123 | \paragraph{Quasar/Project Loom} |
---|
| 124 | Java has two projects that are attempting to introduce lightweight threading into java in the form of Fibers, Quasar\cite{MAN:quasar} and Project Loom\cite{MAN:project-loom}\footnote{It is unclear to me if these are distinct projects or not}. Both projects seem to be based on the \texttt{ForkJoinPool} in Java which appears to be a simple incarnation of Randomized Work Stealing\cite{MAN:java/fork-join}. |
---|
| 125 | |
---|
| 126 | \paragraph{Grand Central Dispatch} |
---|
| 127 | This is an API produce by Apple\cit{Official GCD source} that offers task parellelism\cite{wiki:taskparallel}. Its distinctive aspect is that it uses multiple ``Dispatch Queues'', some of which are created by programmers. These queues each have their own local ordering guarantees, \eg tasks on queue $A$ are executed in \emph{FIFO} order. |
---|
| 128 | |
---|
| 129 | \todo{load balancing and scheduling} |
---|
| 130 | |
---|
| 131 | % http://web.archive.org/web/20090920043909/http://images.apple.com/macosx/technology/docs/GrandCentral_TB_brief_20090903.pdf |
---|
| 132 | |
---|
| 133 | In terms of semantics, the Dispatch Queues seem to be very similar in semantics to Intel\textregistered ~TBB \texttt{execute()} and predecessor semantics. Where it would be possible to convert from one to the other. |
---|
| 134 | |
---|
| 135 | \paragraph{LibFibre} |
---|
| 136 | LibFibre\cite{DBLP:journals/pomacs/KarstenB20} is a light-weight user-level threading framework developt at the University of Waterloo. Similarly to Go, it uses a variation of Work Stealing with a global queue that is higher priority than stealing. Unlock Go it does not have the high-priority next ``chair'' and does not use Randomized Work Stealing. |
---|
| 137 | |
---|