|
| 1 | +// 2019/04/19 - modified by Tsung-Wei Huang |
| 2 | +// - added delay yielding strategy |
| 3 | +// - added mailbox strategy to balance the wakeup calls |
| 4 | +// - TODO: need to add mailbox strategy to batch |
| 5 | +// - TODO: need a notify_n to wake up n threads |
| 6 | +// - TODO: can we skip the --mailbox in emplace? |
| 7 | +// |
1 | 8 | // 2019/04/11 - modified by Tsung-Wei Huang
|
2 | 9 | // - renamed to executor
|
3 | 10 | //
|
@@ -320,6 +327,7 @@ class WorkStealingExecutor {
|
320 | 327 |
|
321 | 328 | struct Worker {
|
322 | 329 | std::minstd_rand rdgen { std::random_device{}() };
|
| 330 | + std::atomic<size_t> mailbox {0}; |
323 | 331 | std::optional<Closure> cache;
|
324 | 332 | WorkStealingQueue<Closure> queue;
|
325 | 333 | };
|
@@ -437,16 +445,6 @@ WorkStealingExecutor<Closure>::WorkStealingExecutor(unsigned N) :
|
437 | 445 | _workers {N},
|
438 | 446 | _waiters {N},
|
439 | 447 | _notifier {_waiters} {
|
440 |
| - |
441 |
| - //for(unsigned i = 1; i <= N; i++) { |
442 |
| - // unsigned a = i; |
443 |
| - // unsigned b = N; |
444 |
| - // // If GCD(a, b) == 1, then a and b are coprimes. |
445 |
| - // if(std::gcd(a, b) == 1) { |
446 |
| - // _coprimes.push_back(i); |
447 |
| - // } |
448 |
| - //} |
449 |
| - |
450 | 448 | _spawn(N);
|
451 | 449 | }
|
452 | 450 |
|
@@ -533,17 +531,10 @@ size_t WorkStealingExecutor<Closure>::num_workers() const {
|
533 | 531 | return _workers.size();
|
534 | 532 | }
|
535 | 533 |
|
536 |
| -// Function: _non_empty_queue |
| 534 | +// Function: _find_victim |
537 | 535 | template <typename Closure>
|
538 | 536 | unsigned WorkStealingExecutor<Closure>::_find_victim(unsigned thief) {
|
539 | 537 |
|
540 |
| - //assert(_workers[thief].queue.empty()); |
541 |
| - |
542 |
| - //auto &pt = _per_thread(); |
543 |
| - //auto rnd = _randomize(pt.seed); |
544 |
| - //auto inc = _coprimes[_fast_modulo(rnd, _coprimes.size())]; |
545 |
| - //auto vtm = _fast_modulo(rnd, _workers.size()); |
546 |
| - |
547 | 538 | unsigned l = 0;
|
548 | 539 | unsigned r = _workers.size() - 1;
|
549 | 540 | unsigned vtm = std::uniform_int_distribution<unsigned>{l, r}(
|
@@ -587,11 +578,21 @@ void WorkStealingExecutor<Closure>::_explore_task(
|
587 | 578 |
|
588 | 579 | if(auto vtm = _find_victim(thief); vtm != _workers.size()) {
|
589 | 580 | t = (vtm == thief) ? _queue.steal() : _workers[vtm].queue.steal();
|
| 581 | + // successful thief |
590 | 582 | if(t) {
|
591 | 583 | return;
|
592 | 584 | }
|
| 585 | + // wasteful thief |
| 586 | + else if(vtm != thief) { |
| 587 | + size_t C = _workers[vtm].mailbox.load(std::memory_order_acquire); |
| 588 | + if(C && _workers[vtm].mailbox.compare_exchange_strong(C, C-1, |
| 589 | + std::memory_order_seq_cst, |
| 590 | + std::memory_order_relaxed)) { |
| 591 | + _notifier.notify(false); |
| 592 | + } |
| 593 | + } |
593 | 594 | }
|
594 |
| - |
| 595 | + |
595 | 596 | if(num_failures++ > max_failures) {
|
596 | 597 | num_failures = 0;
|
597 | 598 | if(std::this_thread::yield(); num_yields++ > max_yields) {
|
@@ -659,7 +660,22 @@ bool WorkStealingExecutor<Closure>::_wait_for_tasks(
|
659 | 660 | _notifier.notify(true);
|
660 | 661 | return false;
|
661 | 662 | }
|
| 663 | + |
| 664 | + // After we update the idler count, we need to check the mailbox |
| 665 | + // again to ensure there is no new wakeup requests. |
| 666 | + for(unsigned w = 0; w<_workers.size(); ++w) { |
| 667 | + if(w == me) { |
| 668 | + _workers[w].mailbox.store(0, std::memory_order_relaxed); |
| 669 | + continue; |
| 670 | + } |
| 671 | + else if(_workers[w].mailbox != 0) { |
| 672 | + _notifier.cancel_wait(&_waiters[me]); |
| 673 | + --_num_idlers; |
| 674 | + return true; |
| 675 | + } |
| 676 | + } |
662 | 677 |
|
| 678 | + // Now I really need to relinguish my self to others |
663 | 679 | _notifier.commit_wait(&_waiters[me]);
|
664 | 680 | --_num_idlers;
|
665 | 681 |
|
@@ -687,6 +703,16 @@ void WorkStealingExecutor<Closure>::emplace(ArgsT&&... args){
|
687 | 703 | }
|
688 | 704 | else {
|
689 | 705 | _workers[pt.worker_id].queue.push(Closure{std::forward<ArgsT>(args)...});
|
| 706 | + |
| 707 | + // We only do the wake-up when this thread is the only worker! |
| 708 | + // Notice that incrementing the mailbox should come before if-statement. |
| 709 | + ++_workers[pt.worker_id].mailbox; |
| 710 | + if(_num_idlers != num_workers() - 1) { |
| 711 | + return; |
| 712 | + } |
| 713 | + else { |
| 714 | + _workers[pt.worker_id].mailbox--; |
| 715 | + } |
690 | 716 | }
|
691 | 717 | }
|
692 | 718 | // other threads
|
@@ -723,7 +749,8 @@ void WorkStealingExecutor<Closure>::batch(std::vector<Closure>& tasks) {
|
723 | 749 | if(!_workers[pt.worker_id].cache) {
|
724 | 750 | _workers[pt.worker_id].cache = std::move(tasks[i++]);
|
725 | 751 | }
|
726 |
| - |
| 752 | + |
| 753 | + // TODO: need to implement the mailbox strategy as well |
727 | 754 | for(; i<tasks.size(); ++i) {
|
728 | 755 | _workers[pt.worker_id].queue.push(std::move(tasks[i]));
|
729 | 756 | _notifier.notify(false);
|
|
0 commit comments