Changeset 3397eed


Ignore:
Timestamp:
Jul 4, 2023, 4:28:22 PM (14 months ago)
Author:
Peter A. Buhr <pabuhr@…>
Branches:
master
Children:
4c2e561
Parents:
f519bd8
Message:

more proofreading of actor chapter

File:
1 edited

Legend:

Unmodified
Added
Removed
  • doc/theses/colby_parsons_MMAth/text/actors.tex

    rf519bd8 r3397eed  
    538538% Through these behaviours, a worker inserts messages onto its own and other worker ready-queues.
    539539To steal, a thief takes work from a victim's ready queue, so work stealing always results in a potential increase in contention on ready queues between the victim gulping from a queue and the thief stealing the queue.
    540 This contention can slows down the victim's throughput.
     540This contention can reduce the victim's throughput.
    541541Note, the data structure used for the ready queue is not discussed since the largest cost is the mutual exclusion and its duration for safely performing the queue operations.
    542542
    543543The stealing mechanism in this work differs from most work-stealing actor-systems because of the message-centric (inverted) actor-system.
    544 Actor systems, such as Akka~\cite{Akka} and CAF~\cite{CAF} using actor-centric systems, steal by dequeuing from a non-empty actor ready-queue and enqueue\-ing to an empty ready-queue.
     544Actor systems, such as Akka~\cite{Akka} and CAF~\cite{CAF} using actor-centric systems, steal by dequeuing an actor from a non-empty actor ready-queue and enqueue\-ing to an empty ready-queue.
    545545% As an example, in CAF, the sharded actor ready queue is a set of double-ended queues (dequeues).
    546546In \CFA, the actor work-stealing implementation is unique because of the message-centric system.
    547547With this approach, it is impractical to steal actors because an actor's messages are distributed in temporal order along the message queue.
    548 To ensure sequential actor execution and \gls{fifo} message delivery in a message-centric system,  stealing requires finding and removing \emph{all} of an actor's messages, and inserting them consecutively in another message queue.
     548To ensure sequential actor execution and \gls{fifo} message delivery in a message-centric system, stealing requires finding and removing \emph{all} of an actor's messages, and inserting them consecutively in another message queue.
    549549This operation is $O(N)$ with a non-trivial constant.
    550550The only way for work stealing to become practical is to shard each worker's message queue, which also reduces contention, and to steal queues to eliminate queue searching.
    551551
    552 \begin{figure}
    553 \begin{center}
    554 \input{diagrams/steal.tikz}
    555 \end{center}
    556 \caption{Queue Stealing Mechanism}
    557 \label{f:steal}
    558 \end{figure}
    559 
    560 Given queue stealing, the goal of the presented work stealing implementation is to have an essentially zero-contention-cost stealing mechanism.
     552Given queue stealing, the goal of the presented stealing implementation is to have an essentially zero-contention-cost stealing mechanism.
    561553Achieving this goal requires work stealing to have minimal (practically no) effect on the performance of the victim.
    562554The implication is that thieves cannot contend with a victim, and that a victim should perform no stealing related work unless it becomes a thief.
    563555In theory, this goal is not achievable, but practical results show the goal is virtually achieved.
    564556
    565 One important lesson learned while working on \uC actors~\cite{} and through discussions with fellow student Thierry Delisle, who examined work-stealing for user-threads in his Ph.D., is \emph{not} to aggressively steal.
     557One important lesson learned while working on \uC actors~\cite{} and through discussions with fellow student Thierry Delisle, who examined work-stealing for user-threads in his Ph.D.~\cite{Delisle22}, is \emph{not} to aggressively steal.
    566558With reasonable workloads, being a thief should be a temporary state, \ie eventually work appears on the thief's ready-queues and it returns to normal operation.
    567559Furthermore, the act of \emph{looking} to find work is invasive (Heisenberg uncertainty principle), possibly disrupting multiple victims.
     
    569561Note, the cost of stealing is not crucial for the thief because it does not have anything else to do except poll or block.
    570562
    571 As such a lazy-stealing approach is used; a thief select a victim, scan its queues once, and return immediately if a queue is stolen.
     563The outline for lazy-stealing by a thief is: select a victim, scan its queues once, and return immediately if a queue is stolen.
    572564The thief then returns to normal operation and conducts a regular scan over its own queues looking for work, where stolen work is placed at the end of the scan.
    573565Hence, only one victim is affected and there is a reasonable delay between stealing events by scanning all the thief's ready queue looking for its own work.
     
    580572Stealing a queue is done wait-free (\ie no busy waiting) with a few atomic instructions that only create contention with other stealing workers, not the victim.
    581573The complexity in the implementation is that victim gulping does not take the mailbox queue;
    582 rather it atomically transfers the mailbox queue to another list leaving the mailbox empty.
    583 Hence, the original list is available for new message deliveries.
     574rather it atomically transfers the mailbox nodes to another list leaving the mailbox empty, \ie it unlinks the queue nodes from the top pointer leaving the original list empty.
     575Hence, the original mailbox is always available for new message deliveries.
    584576However, this transfer logically subdivides the mailbox, and during this period, the mailbox cannot be stolen;
    585577otherwise there are two threads simultaneously running messages on actors in the two parts of the mailbox queue.
     
    587579Hence, a thief checks if a queue is eligible and non-empty before attempting an atomic steal of a queue.
    588580
    589 Internally, the mailbox queues are accessed through two arrays of size $N$, that are shared among all workers.
    590 There is an array of mailbox queue objects @mailboxes@, and an array of pointers to the mailbox objects, @worker_queues@:
    591 \begin{cfa}
    592 work_queue * mailboxes;                   // master array of work request queues
    593 work_queue ** worker_queues;                // secondary array of work queues to allow for swapping
    594 \end{cfa}
    595 A diagram of the queue architecture and stealing mechanism is shown in Figure~\ref{f:steal}.
    596 
    597 When a send happens, it inserts a request directly into one of the @mailboxes@, but when a steal happens, two pointers in @worker_queues@ are swapped.
     581Figure~\ref{f:steal} shows the queue architecture and stealing mechanism.
     582Internally, the mailbox queues are accessed through two arrays of size $N$, which are shared among all workers.
     583There is an array of mailbox queues, @mailboxes@, and an array of pointers to the mailboxes, @worker_queues@:
     584\begin{cfa}
     585struct work_queue {
     586        spinlock_t mutex_lock;                          $\C[2.75in]{// atomicity for queue operations}$
     587        copy_queue * owned_queue;                       $\C{// copy queue}$
     588        copy_queue * c_queue;                           $\C{// current queue}$
     589        volatile bool being_processed;          $\C{// flag to prevent concurrent processing}$
     590};
     591work_queue * mailboxes;                                 $\C{// master array of work request queues}$
     592work_queue ** worker_queues;                    $\C{// secondary array of work queues to allow for swapping}\CRT$
     593\end{cfa}
     594A send inserts a request at the end of a @mailboxes@.
     595A steal swaps two pointers in @worker_queues@.
    598596Conceptually, @worker_queues@ represents the ownership relation between mailboxes and workers.
    599597Given $M$ workers and $N$ mailboxes, each worker owns a contiguous $M$/$N$ block of pointers in @worker_queues@.
     
    603601As such, operations can happen on the queues independent of stealing, which avoids almost all contention between thief threads and victim threads.
    604602
     603\begin{figure}
     604\begin{center}
     605\input{diagrams/steal.tikz}
     606\end{center}
     607\caption{Queue Stealing Mechanism}
     608\label{f:steal}
     609\end{figure}
     610
    605611To steal a queue, a thief does the following:
    606612\begin{enumerate}[topsep=5pt,itemsep=3pt,parsep=0pt]
    607613\item
    608 chooses a victim. Victim selection heuristics are independent of the stealing mechanism and will be discussed in Section~\ref{s:victimSelect}.
     614chooses a victim.
     615Victim selection heuristics are independent of the stealing mechanism and discussed in Section~\ref{s:victimSelect}.
    609616
    610617\item
    611618scan the victim's $M$/$N$ range of @worker_queues@ and non-atomically checks each mail queue to see if it is eligible and non-empty.
    612 If one such mail queue is found, the thief attempts to steal the queue by swapping two pointers in @worker_queues@.
    613 The swap races to interchange the non-empty mail-queue pointer from the victim's @worker_queues@ range with an empty mail-queue pointer in the thief's @worker_queues@ range.
    614 This swap can fail if another thief steals the queue, which will be discussed further in Section~\ref{s:swap}.
    615 Note, a thief never exceeds its $M$/$N$ worker range because it is always exchanging queues with other workers.
    616 If no such mail queue is found, no swap is attempted.
    617 
    618 \item
    619 stops searching after a successful queue steal, a failed queue steal or all queues in the victim's range are examined.
    620 The thief then resumes normal execution and ceases being a thief; it returns to its own queues and iterates over them, because new messages may have arrived during stealing, including ones in the potentially stolen queue.
     619If a match is found, the thief attempts to steal the queue by swapping the appropriate victim worker-queue pointer with an empty thief's pointer, where the pointers come from the victim's and thief's ranges, respectively.
     620% The swap races to interchange the appropriate victim's mail-queue pointer with an empty mail-queue pointer in the thief's @worker_queues@ range.
     621This swap can fail if another thief steals the queue, which is discussed further in Section~\ref{s:swap}.
     622% Note, a thief never exceeds its $M$/$N$ worker range because it is always exchanging queues with other workers.
     623If no appropriate victim mailbox is found, no swap is attempted.
     624
     625\item
     626stops searching after a successful queue steal, a failed queue steal, or all queues in the victim's range are examined.
     627The thief then resumes normal execution and ceases being a thief.
     628Hence, it iterates over its own worker queues because new messages may have arrived during stealing, including ones in the potentially stolen queue.
    621629Stealing is only repeated after the worker completes two consecutive iterations over its owned queues without finding work.
    622630\end{enumerate}
    623631
    624 This approach largely eliminates contention among thieves and victims except for the rare moment when a thief steals a queue and the victim and thief concurrently attempt to gulp the same queue.
    625 When a victim operates on a queue, it first copies the queue pointer from @worker_queues@ to a local variable.
    626 It then uses that local variable for all queue operations until it moves to the next index of its range of the queue array.
    627 This ensures that any swaps do not interrupt gulping operations, however this introduces a correctness issue.
    628 There is a window for a race condition between the victim and a thief.
    629 Once a victim copies the queue pointer from @worker_queues@, a thief could steal that pointer and both may try to gulp from the same queue.
    630 These two gulps cannot be allowed to happen concurrently.
    631 If any behaviours from a queue are run by two workers at a time it violates both mutual exclusion and the actor ordering guarantees.
    632 To avoid concurrent gulps, each queue stores a @being_processed@ flag that is atomically set to @true@ when a queue is gulped.
     632As mentioned, there is a race between a victim gulping and a thief stealing because gulping partitions a mailbox queue making it ineligible for stealing.
     633Furthermore, after a thief steals, there is moment when victim gulps but the queue no longer
     634% This algorithm largely eliminates contention among thieves and victims except for the rare moment when a victim/thief concurrently attempt to gulp/steal the same queue.
     635% Restating, when a victim operates on a queue, it first copies the queue pointer from @worker_queues@ to a local variable (gulp).
     636% It then uses that local variable for all queue operations until it moves to the next index of its range.
     637% This approach ensures any swaps do not interrupt gulping operations, however this introduces a correctness issue.
     638% There is a window for a race condition between the victim and a thief.
     639% Once a victim copies the queue pointer from @worker_queues@, a thief could steal that pointer and both may try to gulp from the same queue.
     640% These two gulps cannot be allowed to happen concurrently.
     641% If any behaviours from a queue are run by two workers at a time it violates both mutual exclusion and the actor ordering guarantees.
     642To avoid this race, each mailbox header stores a @being_processed@ flag that is atomically set to @true@ when a queue is gulped.
    633643The flag indicates that a queue is being processed by a worker and is set back to @false@ once the local processing is finished.
    634644If a worker, either victim or thief, attempts to gulp a queue and finds that the @being_processed@ flag is @true@, it does not gulp the queue and moves on to the next queue in its range.
Note: See TracChangeset for help on using the changeset viewer.