Changeset 7ce70e2
- Timestamp:
- Jul 5, 2023, 1:07:22 PM (17 months ago)
- Branches:
- master
- Children:
- 9235192c, f6afd84
- Parents:
- 218685e
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/theses/colby_parsons_MMAth/text/actors.tex
r218685e r7ce70e2 480 480 481 481 Each executor thread iterates over its own message queues until it finds one with messages. 482 At this point, the executor thread atomically \gls{gulp}s the queue, meaning it moves the contents of message queue to a local queue of the executor thread using a single atomic instruction.482 At this point, the executor thread atomically \gls{gulp}s the queue, meaning it moves the contents of message queue to a local queue of the executor thread. 483 483 An example of the queue gulping operation is shown in the right side of Figure \ref{f:gulp}, where a executor threads gulps queue 0 and begins to process it locally. 484 484 This step allows an executor thread to process the local queue without any atomics until the next gulp. … … 563 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. 564 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. 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.565 Hence, only one victim is affected and there is a reasonable delay between stealing events as the thief will scan its ready queue looking for its own work before potentially stealing again. 566 566 This lazy examination by the thief has a low perturbation cost for victims, while still finding work in a moderately loaded system. 567 567 In all work-stealing algorithms, there is the pathological case where there is too little work and too many workers; … … 572 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. 573 573 The complexity in the implementation is that victim gulping does not take the mailbox queue; 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.574 rather it atomically transfers the mailbox nodes to another queue leaving the mailbox empty, as discussed in Section~\ref{s:executor}. 575 575 Hence, the original mailbox is always available for new message deliveries. 576 However, this transfer logically subdivides the mailbox , and during this period, the mailbox cannot be stolen;576 However, this transfer logically subdivides the mailbox into two separate queues, and during this period, the mailbox cannot be stolen; 577 577 otherwise there are two threads simultaneously running messages on actors in the two parts of the mailbox queue. 578 578 To solve this problem, an atomic gulp also marks the mailbox queue as subdivided, making it ineligible for stealing. … … 592 592 work_queue ** worker_queues; $\C{// secondary array of work queues to allow for swapping}\CRT$ 593 593 \end{cfa} 594 A send inserts a request at the end of a@mailboxes@.594 A send inserts a request at the end of one of @mailboxes@. 595 595 A steal swaps two pointers in @worker_queues@. 596 596 Conceptually, @worker_queues@ represents the ownership relation between mailboxes and workers. 597 597 Given $M$ workers and $N$ mailboxes, each worker owns a contiguous $M$/$N$ block of pointers in @worker_queues@. 598 When a worker gulps, it dereferences one of the pointers in its section of @worker_queues@ and then gulps from the queueit points at.598 When a worker gulps, it dereferences one of the pointers in its section of @worker_queues@ and then gulps the queue from the mailbox it points at. 599 599 To transfer ownership of a mailbox from one worker to another, a pointer from each of the workers' ranges are swapped. 600 600 This structure provides near-complete separation of stealing and gulping/send operations. 601 As such, operations can happen on the queuesindependent of stealing, which avoids almost all contention between thief threads and victim threads.601 As such, operations can happen on @mailboxes@ independent of stealing, which avoids almost all contention between thief threads and victim threads. 602 602 603 603 \begin{figure} … … 616 616 617 617 \item 618 scan the victim's $M$/$N$ range of @worker_queues@ and non-atomically checks each mail queueto see if it is eligible and non-empty.619 If a match is found, the thief attempts to steal the queueby 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.618 scan the victim's $M$/$N$ range of @worker_queues@ and non-atomically checks each mailbox to see if it is eligible and non-empty. 619 If a match is found, the thief attempts to steal the mailbox 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 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 621 This swap can fail if another thief steals the queue, which is discussed further in Section~\ref{s:swap}. … … 624 624 625 625 \item 626 stops searching after a successful queue steal, a failed queue steal, or all queues in the victim's range are examined.626 stops searching after a successful mailbox steal, a failed mailbox steal, or all mailboxes in the victim's range are examined. 627 627 The thief then resumes normal execution and ceases being a thief. 628 628 Hence, it iterates over its own worker queues because new messages may have arrived during stealing, including ones in the potentially stolen queue. … … 631 631 632 632 \subsection{Stealing Problem} 633 634 Each queue access involving a thief is protected using spinlock @mutex_lock@ and contention among thieves should be low. 635 However, to achieve the goal of almost zero contention for the victim, it is necessary to remove this lock around the only critical section involving the victim. 633 Each queue access (send or gulp) involving any worker (thief or victim) is protected using spinlock @mutex_lock@. 634 However, to achieve the goal of almost zero contention for the victim, it is necessary that the thief does not acquire any queue spinlocks in the stealing protocol. 636 635 The victim critical-section is gulping a queue, which involves two steps: 637 636 \begin{cfa} 638 637 temp = worker_queues[x]; 639 638 // preemption and steal 640 transfer( temp->owned_queuem, temp->c_queue ); // @being_processed@ set in transfer with mutual exclusion641 \end{cfa} 642 where @transfer@ moves the top point from @c_queue@ to @owned_queuem@ and sets the top pointer of @c_queue@ to @0p@ (null) to partitionthe mailbox.639 transfer( local_queue, temp->c_queue ); // @being_processed@ set in transfer with mutual exclusion 640 \end{cfa} 641 where @transfer@ gulps the work from @c_queue@ to the victim's @local_queue@ and leaves @c_queue@ empty, partitioning the mailbox. 643 642 Specifically, 644 643 \begin{enumerate}[topsep=5pt,itemsep=3pt,parsep=0pt] 645 644 \item 646 The victim must dereference its current queuepointer from @worker_queues@.647 \item 648 The victim calls @transfer@ to gulp the queue.645 The victim must dereference its current mailbox pointer from @worker_queues@. 646 \item 647 The victim calls @transfer@ to gulp from the mailbox. 649 648 \end{enumerate} 650 If the victim is preempted after the dereference, a thief can steal the queuepointer before the victim calls @transfer@.651 The thief then races ahead, transitions back to a victim, searches its mailbox queues, finds the stolen non-empty queue, and gulps it.652 The original victim now restarts and gulps from the stolen queue pointed to by its dereference, even though the thief put an empty mailbox queue in place for the victim.653 At this point, the victim and thief are possibly processing the same messages, which violates mutual exclusion and the message-ordering guarantee.654 Preventing this race requires either a lock acquire or an atomic operation on the victim's fast-path to guarantee the victim's gulp retrieves the empty mailbox queue installed by the thief.649 If the victim is preempted after the dereference, a thief can steal the mailbox pointer before the victim calls @transfer@. 650 The thief then races ahead, transitions back to a victim, searches its mailboxes, finds the stolen non-empty mailbox, and gulps its queue. 651 The original victim now continues and gulps from the stolen mailbox pointed to by its dereference, even though the thief has logically subdivided this mailbox by gulping it. 652 At this point, the mailbox has been subdivided a second time, and the victim and thief are possibly processing messages sent to the same actor, which violates mutual exclusion and the message-ordering guarantee. 653 Preventing this race requires either a lock acquire or an atomic operation on the victim's fast-path to guarantee the victim's mailbox dereferenced pointer is not stale. 655 654 However, any form of locking here creates contention between thief and victim. 656 655 … … 667 666 % If any behaviours from a queue are run by two workers at a time it violates both mutual exclusion and the actor ordering guarantees. 668 667 To resolve the race, each mailbox header stores a @being_processed@ flag that is atomically set when a queue is transferred. 669 The flag indicates that a queue is being processed by a worker and is reset once the local processing is finished. 670 If a worker, either victim or thief turned victim, attempts to gulp a queue and finds that the @being_processed@ flag is set, it does not gulp the queue and moves on to the next queue in its range. 668 The flag indicates that a mailbox has been gulped (logically subdivided) by a worker and the gulped queue is being processed locally. 669 The @being_processed@ flag is reset once the local processing is finished. 670 If a worker, either victim or thief turned victim, attempts to gulp from a mailbox and find the @being_processed@ flag set, it does not gulp and moves on to the next mailbox in its range. 671 671 This resolves the race no matter the winner. 672 If the thief wins the race, it steals and gulps the queue, and the victim sees the flag set and skips gulping the queue.673 If the victim wins the race, it gulps the queue, and the thief sees the flag set and skips gulping the queue.674 675 There is a final pathologicalcase where the race occurs and is resolved with \emph{both} gulps occurring.672 If the thief wins the race, it steals the mailbox and gulps, and the victim sees the flag set and skips gulping from the mailbox. 673 If the victim wins the race, it gulps from the mailbox, and the thief sees the flag set and does not gulp from the mailbox. 674 675 There is a final case where the race occurs and is resolved with \emph{both} gulps occurring. 676 676 Here, the winner of the race finishes processing the queue and resets the @being_processed@ flag. 677 Then the loser unblocks and completes its gulp by transferring a \emph{stolen queue} and atomically setting the @being_processed@ flag. 678 The loser is now processing messages from the winner's queue, which is safe because the winner ignores this queue as @being_processed@ is set. 679 The loser never attempts to process this stolen queue again because its next queue cycle sees the swapped queue from the thief (which may or may not be empty at this point). 680 This race is now the only source of contention between victim and thieve as they both try to acquire a lock on the same queue during a transfer. 681 In theory, this scenario can cascade across multiple workers all performing this sequence across multiple queues. 677 Then the loser unblocks and completes its gulp from the same mailbox and atomically sets the @being_processed@ flag. 678 The loser is now processing messages from a temporarily shared mailbox, which is safe because the winner will ignore this mailbox if it attempts another gulp, since @being_processed@ is set. 679 The victim never attempts to gulp from the stolen mailbox again because its next cycle sees the swapped mailbox from the thief (which may or may not be empty at this point). 680 This race is now the only source of contention between victim and thief as they both try to acquire a lock on the same queue during a transfer. 682 681 However, the window for this race is extremely small, making this contention rare. 683 Furthermore, the chance of a cascading scenario across multiple workers is even rarer. 682 In theory, if this race occurs multiple times consecutively \ie a thief steals, dereferences stolen mailbox pointer, is interrupted and stolen from, etc., this scenario can cascade across multiple workers all attempting to gulp from one mailbox. 683 The @being_processed@ flag ensures correctness even in this case, and the chance of a cascading scenario across multiple workers is even rarer. 684 684 % The rarity is why this mechanism is zero-victim-cost in practice but not in theory. 685 685 By collecting statistics on failed gulps due to the @being_processed@ flag, this contention occurs $\approx$0.05\% of the time when a gulp occurs (1 in every 2000 gulps).
Note: See TracChangeset
for help on using the changeset viewer.