1+ // 2019/07/26 - modified by Chun-Xun Lin
2+ // - Combine explore_task & wait_for_task
3+ // - Remove CAS operations
4+ // - Update _num_thieves after pre_wait
5+ // - TODO: Check will underutilization happen?
6+ // - TODO: Find out uppper bound (does cycle exist?)
7+ // - TODO: Does performance drop due to the busy looping after pre_wait?
8+ //
19// 2019/07/25 - modified by Tsung-Wei Huang & Chun-Xun Lin
210// - fixed the potential underutilization
311// - use CAS in both last thief & active worker to make the notification less aggressive
@@ -348,8 +356,6 @@ inline void Executor::_explore_task(unsigned thief, std::optional<Node*>& t) {
348356 const size_t F = (_workers.size () + 1 ) << 1 ;
349357 const size_t Y = 100 ;
350358
351- steal_loop:
352-
353359 size_t f = 0 ;
354360 size_t y = 0 ;
355361
@@ -388,25 +394,6 @@ inline void Executor::_explore_task(unsigned thief, std::optional<Node*>& t) {
388394 }*/
389395 }
390396
391- // We need to ensure at least one thieve if there is an active worker
392- if (auto N = --_num_thieves; N == 0 ) {
393- if (t != std::nullopt ) {
394- _notifier.notify (false );
395- }
396- else if (_num_actives > 0 ) {
397- // watch dog
398- // To prevent race with another thief inside the wait_for_task
399- // ++_num_thieves;
400- size_t zero {0 };
401- size_t one {1 };
402- if (_num_thieves.compare_exchange_strong (zero, one, std::memory_order_seq_cst, std::memory_order_relaxed)) {
403- goto steal_loop;
404- }
405- else {
406- assert (zero <= _workers.size ());
407- }
408- }
409- }
410397}
411398
412399// Procedure: _exploit_task
@@ -439,16 +426,19 @@ inline void Executor::_exploit_task(unsigned i, std::optional<Node*>& t) {
439426// Function: _wait_for_tasks
440427inline bool Executor::_wait_for_tasks (unsigned me, std::optional<Node*>& t) {
441428
442- ++_num_thieves;
443- assert (_num_thieves <= _workers.size ());
444429
445430begin_steal:
446431
432+ ++_num_thieves;
433+ assert (_num_thieves <= _workers.size ());
434+
447435 if (_explore_task (me, t); t) {
436+ if (auto N = _num_thieves.fetch_sub (1 ); N == 1 ) {
437+ _notifier.notify (false );
438+ }
448439 return true ;
449440 }
450441
451-
452442 assert (!t);
453443
454444 _notifier.prepare_wait (&_waiters[me]);
@@ -457,6 +447,9 @@ inline bool Executor::_wait_for_tasks(unsigned me, std::optional<Node*>& t) {
457447 if (!_queue.empty ()) {
458448 _notifier.cancel_wait (&_waiters[me]);
459449 t = _queue.steal ();
450+ if (auto N = _num_thieves.fetch_sub (1 ); t && N == 1 ) {
451+ _notifier.notify (false );
452+ }
460453 // t = (vtm == me) ? _queue.steal() : _workers[vtm].queue.steal();
461454 return true ;
462455 }
@@ -474,17 +467,23 @@ inline bool Executor::_wait_for_tasks(unsigned me, std::optional<Node*>& t) {
474467 if (_done) {
475468 _notifier.cancel_wait (&_waiters[me]);
476469 _notifier.notify (true );
470+ --_num_thieves;
477471 return false ;
478472 }
479473
480- if (_num_actives && _num_thieves.load (std::memory_order_relaxed) == 0 ) {
481- size_t zero {0 };
482- size_t one {1 };
483- if (_num_thieves.compare_exchange_strong (zero, one, std::memory_order_seq_cst, std::memory_order_relaxed)) {
484- _notifier.cancel_wait (&_waiters[me]);
485- goto begin_steal;
486- }
474+ if (_num_thieves.fetch_sub (1 ) == 1 && _num_actives) {
475+ _notifier.cancel_wait (&_waiters[me]);
476+ goto begin_steal;
487477 }
478+
479+ // if(_num_actives && _num_thieves.load(std::memory_order_relaxed) == 0) {
480+ // size_t zero {0};
481+ // size_t one {1};
482+ // if(_num_thieves.compare_exchange_strong(zero, one, std::memory_order_seq_cst, std::memory_order_relaxed)) {
483+ // _notifier.cancel_wait(&_waiters[me]);
484+ // goto begin_steal;
485+ // }
486+ // }
488487
489488 // Now I really need to relinguish my self to others
490489 _notifier.commit_wait (&_waiters[me]);
0 commit comments