resumable* dequeue(Worker* self) { // we wait for new jobs by polling our external queue: first, we // assume an active work load on the machine and perform aggresive // polling, then we relax our polling a bit and wait 50 us between // dequeue attempts, finally we assume pretty much nothing is going // on and poll every 10 ms; this strategy strives to minimize the // downside of "busy waiting", which still performs much better than a // "signalizing" implementation based on mutexes and conition variables auto& strategies = d(self).strategies; resumable* job = nullptr; for (auto& strat : strategies) { for (size_t i = 0; i < strat.attempts; i += strat.step_size) { job = d(self).queue.take_head(); if (job) return job; // try to steal every X poll attempts if ((i % strat.steal_interval) == 0) { job = try_steal(self); if (job) return job; } if (strat.sleep_duration.count() > 0) std::this_thread::sleep_for(strat.sleep_duration); } } // unreachable, because the last strategy loops // until a job has been dequeued return nullptr; }
static gboolean dequeue_or_steal (ThreadPool *tp, gpointer *data, MonoWSQ *local_wsq) { if (mono_runtime_is_shutting_down ()) return FALSE; mono_cq_dequeue (tp->queue, (MonoObject **) data); if (!tp->is_io && !*data) try_steal (local_wsq, data, FALSE); return (*data != NULL); }
resumable* dequeue(Worker* self) { // we wait for new jobs by polling our external queue: first, we // assume an active work load on the machine and perform aggresive // polling, then we relax our polling a bit and wait 50 us between // dequeue attempts, finally we assume pretty much nothing is going // on and poll every 10 ms; this strategy strives to minimize the // downside of "busy waiting", which still performs much better than a // "signalizing" implementation based on mutexes and conition variables struct poll_strategy { size_t attempts; size_t step_size; size_t steal_interval; std::chrono::microseconds sleep_duration; }; constexpr poll_strategy strategies[3] = { // aggressive polling (100x) without sleep interval {100, 1, 10, std::chrono::microseconds{0}}, // moderate polling (500x) with 50 us sleep interval {500, 1, 5, std::chrono::microseconds{50}}, // relaxed polling (infinite attempts) with 10 ms sleep interval {101, 0, 1, std::chrono::microseconds{10000}} }; resumable* job = nullptr; for (auto& strat : strategies) { for (size_t i = 0; i < strat.attempts; i += strat.step_size) { job = d(self).queue.take_head(); if (job) { return job; } // try to steal every X poll attempts if ((i % strat.steal_interval) == 0) { job = try_steal(self); if (job) { return job; } } std::this_thread::sleep_for(strat.sleep_duration); } } // unreachable, because the last strategy loops // until a job has been dequeued return nullptr; }