-
-
Notifications
You must be signed in to change notification settings - Fork 719
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
Dynamic annotations #5207
base: main
Are you sure you want to change the base?
Dynamic annotations #5207
Conversation
5247116
to
c56bc9b
Compare
c56bc9b
to
dae7d59
Compare
distributed/scheduler.py
Outdated
worker=None, | ||
key: str = None, | ||
annotations: Mapping = None, | ||
resource_restrictions: Mapping = None, |
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 recommend making restrictions a separate route. I don't think that there is any reason for annotations and restrictions to be intertwined.
However, I do think that it would be useful to support a variety of different kinds of restrictions. You should probably roll this into the Scheduler.set_restrictions
method. I think that it has a worker=
keyword today as a hint that host=
and resource=
should come later.
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.
Good point.
A related question, what is the different between regular handlers and stream handlers?
Can I use self.batched_stream.send
for a "op": "set_restrictions"
message when set_restrictions
is in Scheduler.handlers
?
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.
Stream handlers are used with BatchedSend objects. They are strictly fire and forget. They're also very cheap to send. We accumulate several small messages in a list and then send off that list periodically (with a very short period)
The handlers are what receive the await rpc.foo(...)
commands. They wait for and collect a response.
If you need a response then use await rpc.foo(...)
. If you want to send lots of little small messages without really affecting overhead then try to use a stream handler. You can certainly put a method in both stream handlers and handlers, although if the receiving end needs a response then this might require some cleverness.
Does that provide enough information?
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.
Yes, thanks!
distributed/scheduler.py
Outdated
key: str = None, | ||
annotations: Mapping = None, | ||
resource_restrictions: Mapping = None, | ||
annotate_dependents=True, |
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 curious about the motivation behind this keyword. Can you expand here?
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 am considering if an annotator function should be able to enable/disable annotations of task dependents. Alternativevly, we can always apply annotations to task dependents.
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 agree that it's possibly in scope. I'm curious if there are active use cases for it today that you have in mind. If so, I would be curious to hear them (for example, maybe you need the results of gpu tasks are likely to also be gpu tasks). If not, then I would recommend waiting on this until we know more.
My gut reaction is not to do this until we have a motivating use case.
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.
for example, maybe you need the results of gpu tasks are likely to also be gpu tasks
Yes, to implement automatic use of a GPU ThreadPoolExecutor (#5084). If the initial array or dataframe creation is annotated with the GPU executor, all the following operations are as well.
This will also be very helpful when mixing data types. By tracking the GPU use of each individual partition, we can utilize GPU and CPU workers simultaneously.
519ce1c
to
8ad9cee
Compare
449ca4b
to
7494179
Compare
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.
Some small comments. In general this looks fine to me though.
} | ||
res1, res2 = c.get(dsk, ["g1", "g2"], sync=False, asynchronous=True) | ||
assert "Executor1" in await res1 | ||
assert "Executor2" in await res2 |
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.
The fact that you had to use get/compute here is intersting. There are interesting challenges with doing this with futures / dependencies that might change. I still think that this is a useful feature to have, but it seems like this approach might not be comprehensive if we want to solve things across update_graph calls. Agree or disagree?
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.
Agree! I hadn't thought about this issue before today. As you say, it is still useful but I think we should put this PR on hold for a bit and see if we can come up with a better approach.
|
||
# Separate annotation updates into two cases: | ||
a1 = {} # when the task's dependents should also be updated | ||
a2 = {} # when only the task itself should be updated |
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.
Do you have thoughts on better names here?
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.
Heh, yeah I can do that :)
As part of the discussion of Heterogeneous Computing Design, this PR implements dynamic annotations inspired by @mrocklin's option 4 in #4656.
The idea is that a user can specify a function that updates the annotations and restrictions of a task after its execution:
"annotate-task"
message.The following in an example of an dynamic annotation function that sets the worker executor to
"gpu"
when the task output is a CUDA device object. Together with a default GPU executor (#5084), this will make sure that all tasks preceding the first CUDA task will use the GPU executor.Notice, this will not annotate the first use of a GPU task, only its dependent tasks are annotated automatically. The user can of cause still annotate tasks manually.
black distributed
/flake8 distributed
/isort distributed