-
-
Notifications
You must be signed in to change notification settings - Fork 720
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Revert idle classification when worker-saturation is set #7278
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -1504,8 +1504,12 @@ class SchedulerState: | |||||||||||||||||||||||||||||||||||||||||||||||
#: Workers that are currently in running state | ||||||||||||||||||||||||||||||||||||||||||||||||
running: set[WorkerState] | ||||||||||||||||||||||||||||||||||||||||||||||||
#: Workers that are currently in running state and not fully utilized | ||||||||||||||||||||||||||||||||||||||||||||||||
#: Definition based on occupancy | ||||||||||||||||||||||||||||||||||||||||||||||||
#: (actually a SortedDict, but the sortedcontainers package isn't annotated) | ||||||||||||||||||||||||||||||||||||||||||||||||
idle: dict[str, WorkerState] | ||||||||||||||||||||||||||||||||||||||||||||||||
#: Similar to `idle` | ||||||||||||||||||||||||||||||||||||||||||||||||
#: Definition based on assigned tasks | ||||||||||||||||||||||||||||||||||||||||||||||||
idle_task_count: set[WorkerState] | ||||||||||||||||||||||||||||||||||||||||||||||||
#: Workers that are fully utilized. May include non-running workers. | ||||||||||||||||||||||||||||||||||||||||||||||||
saturated: set[WorkerState] | ||||||||||||||||||||||||||||||||||||||||||||||||
total_nthreads: int | ||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -1612,6 +1616,7 @@ def __init__( | |||||||||||||||||||||||||||||||||||||||||||||||
self.extensions = {} | ||||||||||||||||||||||||||||||||||||||||||||||||
self.host_info = host_info | ||||||||||||||||||||||||||||||||||||||||||||||||
self.idle = SortedDict() | ||||||||||||||||||||||||||||||||||||||||||||||||
self.idle_task_count = set() | ||||||||||||||||||||||||||||||||||||||||||||||||
self.n_tasks = 0 | ||||||||||||||||||||||||||||||||||||||||||||||||
self.resources = resources | ||||||||||||||||||||||||||||||||||||||||||||||||
self.saturated = set() | ||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -2146,13 +2151,16 @@ def decide_worker_rootish_queuing_enabled(self) -> WorkerState | None: | |||||||||||||||||||||||||||||||||||||||||||||||
# (and actually pass in the task). | ||||||||||||||||||||||||||||||||||||||||||||||||
assert not math.isinf(self.WORKER_SATURATION) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
if not self.idle: | ||||||||||||||||||||||||||||||||||||||||||||||||
if not self.idle_task_count: | ||||||||||||||||||||||||||||||||||||||||||||||||
# All workers busy? Task gets/stays queued. | ||||||||||||||||||||||||||||||||||||||||||||||||
return None | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
# Just pick the least busy worker. | ||||||||||||||||||||||||||||||||||||||||||||||||
# NOTE: this will lead to worst-case scheduling with regards to co-assignment. | ||||||||||||||||||||||||||||||||||||||||||||||||
ws = min(self.idle.values(), key=lambda ws: len(ws.processing) / ws.nthreads) | ||||||||||||||||||||||||||||||||||||||||||||||||
ws = min( | ||||||||||||||||||||||||||||||||||||||||||||||||
self.idle_task_count, | ||||||||||||||||||||||||||||||||||||||||||||||||
key=lambda ws: len(ws.processing) / ws.nthreads, | ||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||
if self.validate: | ||||||||||||||||||||||||||||||||||||||||||||||||
assert not _worker_full(ws, self.WORKER_SATURATION), ( | ||||||||||||||||||||||||||||||||||||||||||||||||
ws, | ||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -2791,7 +2799,7 @@ def transition_waiting_queued(self, key, stimulus_id): | |||||||||||||||||||||||||||||||||||||||||||||||
worker_msgs: dict = {} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
if self.validate: | ||||||||||||||||||||||||||||||||||||||||||||||||
assert not self.idle, (ts, self.idle) | ||||||||||||||||||||||||||||||||||||||||||||||||
assert not self.idle_task_count, (ts, self.idle_task_count) | ||||||||||||||||||||||||||||||||||||||||||||||||
_validate_ready(self, ts) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
ts.state = "queued" | ||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -3062,25 +3070,23 @@ def check_idle_saturated(self, ws: WorkerState, occ: float = -1.0): | |||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
idle = self.idle | ||||||||||||||||||||||||||||||||||||||||||||||||
saturated = self.saturated | ||||||||||||||||||||||||||||||||||||||||||||||||
if ( | ||||||||||||||||||||||||||||||||||||||||||||||||
self.is_unoccupied(ws, occ, p) | ||||||||||||||||||||||||||||||||||||||||||||||||
if math.isinf(self.WORKER_SATURATION) | ||||||||||||||||||||||||||||||||||||||||||||||||
else not _worker_full(ws, self.WORKER_SATURATION) | ||||||||||||||||||||||||||||||||||||||||||||||||
): | ||||||||||||||||||||||||||||||||||||||||||||||||
saturated.discard(ws) | ||||||||||||||||||||||||||||||||||||||||||||||||
if self.is_unoccupied(ws, occ, p): | ||||||||||||||||||||||||||||||||||||||||||||||||
if ws.status == Status.running: | ||||||||||||||||||||||||||||||||||||||||||||||||
idle[ws.address] = ws | ||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+3073
to
3076
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This is more consistent with previous behavior. Notice that before, if a worker was occupied, but not saturated, it wouldn't be removed from the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||||||||||||||||||||||||
saturated.discard(ws) | ||||||||||||||||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||||||||||||||||
idle.pop(ws.address, None) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
nc = ws.nthreads | ||||||||||||||||||||||||||||||||||||||||||||||||
if p > nc: | ||||||||||||||||||||||||||||||||||||||||||||||||
pending = occ * (p - nc) / (p * nc) | ||||||||||||||||||||||||||||||||||||||||||||||||
if 0.4 < pending > 1.9 * (self.total_occupancy / self.total_nthreads): | ||||||||||||||||||||||||||||||||||||||||||||||||
saturated.add(ws) | ||||||||||||||||||||||||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
saturated.discard(ws) | ||||||||||||||||||||||||||||||||||||||||||||||||
if not _worker_full(ws, self.WORKER_SATURATION): | ||||||||||||||||||||||||||||||||||||||||||||||||
if ws.status == Status.running: | ||||||||||||||||||||||||||||||||||||||||||||||||
self.idle_task_count.add(ws) | ||||||||||||||||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||||||||||||||||
self.idle_task_count.discard(ws) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
def is_unoccupied( | ||||||||||||||||||||||||||||||||||||||||||||||||
self, ws: WorkerState, occupancy: float, nprocessing: int | ||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -4746,6 +4752,7 @@ async def remove_worker( | |||||||||||||||||||||||||||||||||||||||||||||||
del self.stream_comms[address] | ||||||||||||||||||||||||||||||||||||||||||||||||
del self.aliases[ws.name] | ||||||||||||||||||||||||||||||||||||||||||||||||
self.idle.pop(ws.address, None) | ||||||||||||||||||||||||||||||||||||||||||||||||
self.idle_task_count.discard(ws) | ||||||||||||||||||||||||||||||||||||||||||||||||
self.saturated.discard(ws) | ||||||||||||||||||||||||||||||||||||||||||||||||
del self.workers[address] | ||||||||||||||||||||||||||||||||||||||||||||||||
ws.status = Status.closed | ||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -5333,6 +5340,7 @@ def handle_worker_status_change( | |||||||||||||||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||||||||||||||||
self.running.discard(ws) | ||||||||||||||||||||||||||||||||||||||||||||||||
self.idle.pop(ws.address, None) | ||||||||||||||||||||||||||||||||||||||||||||||||
self.idle_task_count.discard(ws) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
async def handle_request_refresh_who_has( | ||||||||||||||||||||||||||||||||||||||||||||||||
self, keys: Iterable[str], worker: str, stimulus_id: str | ||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm quite concerned that we're now calling
is_unoccupied
every time, even when queuing is enabled. This significantly slows down the scheduler: #7256. The urgency of fixing that was diminished by queuing being on by default and getting to skip that slow code path.I'm not sure that a known and large scheduler performance degradation is worth avoiding hypothetical small changes to work-stealing behavior due to the changed definition of
idle
when queuing is on.If we can fix #7256 before a release, then I'm happy with this change, otherwise I'd be concerned by this tradeoff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After running some benchmarks, it looks like occupancy might not have as much of an effect on end-to-end runtime as I'd expected: #7256 (comment). So I'm happy with this if we want to go with it.
For
performance reasonsand practicality though, I'd like to consider #7280 as another solution to #7085.Edit: that uses occupancy too, so there's a similar performance cost. I think doing both PRs would be a good idea.