Changeset 3397eed for doc/theses/colby_parsons_MMAth/text/actors.tex
- Timestamp:
- Jul 4, 2023, 4:28:22 PM (17 months ago)
- Branches:
- master
- Children:
- 4c2e561
- Parents:
- f519bd8
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/theses/colby_parsons_MMAth/text/actors.tex
rf519bd8 r3397eed 538 538 % Through these behaviours, a worker inserts messages onto its own and other worker ready-queues. 539 539 To 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 downthe victim's throughput.540 This contention can reduce the victim's throughput. 541 541 Note, 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. 542 542 543 543 The 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.544 Actor 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. 545 545 % As an example, in CAF, the sharded actor ready queue is a set of double-ended queues (dequeues). 546 546 In \CFA, the actor work-stealing implementation is unique because of the message-centric system. 547 547 With 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, 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. 549 549 This operation is $O(N)$ with a non-trivial constant. 550 550 The 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. 551 551 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. 552 Given queue stealing, the goal of the presented stealing implementation is to have an essentially zero-contention-cost stealing mechanism. 561 553 Achieving this goal requires work stealing to have minimal (practically no) effect on the performance of the victim. 562 554 The implication is that thieves cannot contend with a victim, and that a victim should perform no stealing related work unless it becomes a thief. 563 555 In theory, this goal is not achievable, but practical results show the goal is virtually achieved. 564 556 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.557 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.~\cite{Delisle22}, is \emph{not} to aggressively steal. 566 558 With 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. 567 559 Furthermore, the act of \emph{looking} to find work is invasive (Heisenberg uncertainty principle), possibly disrupting multiple victims. … … 569 561 Note, the cost of stealing is not crucial for the thief because it does not have anything else to do except poll or block. 570 562 571 As such a lazy-stealing approach is used; a thiefselect a victim, scan its queues once, and return immediately if a queue is stolen.563 The outline for lazy-stealing by a thief is: select a victim, scan its queues once, and return immediately if a queue is stolen. 572 564 The 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. 573 565 Hence, 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. … … 580 572 Stealing 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. 581 573 The 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 mailboxempty.583 Hence, the original list is available for new message deliveries.574 rather 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. 575 Hence, the original mailbox is always available for new message deliveries. 584 576 However, this transfer logically subdivides the mailbox, and during this period, the mailbox cannot be stolen; 585 577 otherwise there are two threads simultaneously running messages on actors in the two parts of the mailbox queue. … … 587 579 Hence, a thief checks if a queue is eligible and non-empty before attempting an atomic steal of a queue. 588 580 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. 581 Figure~\ref{f:steal} shows the queue architecture and stealing mechanism. 582 Internally, the mailbox queues are accessed through two arrays of size $N$, which are shared among all workers. 583 There is an array of mailbox queues, @mailboxes@, and an array of pointers to the mailboxes, @worker_queues@: 584 \begin{cfa} 585 struct 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 }; 591 work_queue * mailboxes; $\C{// master array of work request queues}$ 592 work_queue ** worker_queues; $\C{// secondary array of work queues to allow for swapping}\CRT$ 593 \end{cfa} 594 A send inserts a request at the end of a @mailboxes@. 595 A steal swaps two pointers in @worker_queues@. 598 596 Conceptually, @worker_queues@ represents the ownership relation between mailboxes and workers. 599 597 Given $M$ workers and $N$ mailboxes, each worker owns a contiguous $M$/$N$ block of pointers in @worker_queues@. … … 603 601 As such, operations can happen on the queues independent of stealing, which avoids almost all contention between thief threads and victim threads. 604 602 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 605 611 To steal a queue, a thief does the following: 606 612 \begin{enumerate}[topsep=5pt,itemsep=3pt,parsep=0pt] 607 613 \item 608 chooses a victim. Victim selection heuristics are independent of the stealing mechanism and will be discussed in Section~\ref{s:victimSelect}. 614 chooses a victim. 615 Victim selection heuristics are independent of the stealing mechanism and discussed in Section~\ref{s:victimSelect}. 609 616 610 617 \item 611 618 scan 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. 619 If 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. 621 This 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. 623 If no appropriate victim mailbox is found, no swap is attempted. 624 625 \item 626 stops searching after a successful queue steal, a failed queue steal, or all queues in the victim's range are examined. 627 The thief then resumes normal execution and ceases being a thief. 628 Hence, it iterates over its own worker queues because new messages may have arrived during stealing, including ones in the potentially stolen queue. 621 629 Stealing is only repeated after the worker completes two consecutive iterations over its owned queues without finding work. 622 630 \end{enumerate} 623 631 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. 632 As mentioned, there is a race between a victim gulping and a thief stealing because gulping partitions a mailbox queue making it ineligible for stealing. 633 Furthermore, 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. 642 To avoid this race, each mailbox header stores a @being_processed@ flag that is atomically set to @true@ when a queue is gulped. 633 643 The flag indicates that a queue is being processed by a worker and is set back to @false@ once the local processing is finished. 634 644 If 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.