Skip to content
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

Annotate Task* objects for Cythonization #4302

Merged
merged 31 commits into from
Dec 7, 2020
Merged

Conversation

jakirkham
Copy link
Member

@jakirkham jakirkham commented Dec 3, 2020

Analogous to PR ( #4290 ) ( #4294 ) except this is annotating TaskState, TaskPrefix, and TaskGroup. Plus all usages thereof. This is a bit longer simply because of how many attributes TaskState has and how frequently it is used. That said, it follows the same pattern as was seen with the other two and so shouldn't be too surprising.

@mrocklin
Copy link
Member

mrocklin commented Dec 3, 2020 via email

@jakirkham
Copy link
Member Author

Both PR ( #4294 ) and this help based on my own local profiling and looking at the call graphs. Though the other thing they do is unlock further optimizations in the Scheduler transition methods. Since all of those methods are just working with these 3 objects ClientState, WorkerState, and TaskState. I think once we get these in, we will want to take another look at line profiling of the transition methods and see what sticks out. There are different ways we can structure the code and make additional annotations so that Cython can optimize it more effectively.

Even just the ClientState and check_idle_saturated changes helped as evidenced by the nightly profile ( quasiben/dask-scheduler-performance#39 ).

Anyways if you have time to review PR ( #4294 ) tomorrow that will be really helpful. This PR is 95% there, but there is one or two more things that it may still need. So will be working on them as well.

Copy link
Member

@mrocklin mrocklin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor comments here. In general all of this looks straightforward.

I'm curious about a couple of the types, but those can wait as well.

Also beware, you're likely to get a few conflicts when we merge in the annotations work #4279

distributed/scheduler.py Outdated Show resolved Hide resolved
distributed/scheduler.py Outdated Show resolved Hide resolved
distributed/scheduler.py Outdated Show resolved Hide resolved
_key: str
_hash: Py_hash_t
_prefix: object
_run_spec: object
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bytes maybe

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. This came from the docstring above. Later in the code it was implied this could be a dict, but might not be. Seems like it is not very clear what it may be so perhaps it is best to leave as object for now.

_prefix: object
_run_spec: object
_priority: tuple
_state: str
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make this into an enum? Would Cython prefer that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cython can't do much with a Python Enum. There are enums in Cython that it does expose to the Python layer optionally. Though these are not available in pure Python mode unfortunately ( cython/cython#3923 ).

For now I think it is ok to just type this as a str as part of our broad effort to type things. We can then revisit specifics once everything is in and we have had an opportunity to profile more closely.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. We check and change state pretty often. I wouldn't be surprised if this has some effect in the future.

Are pure Python enums something that would make sense to request upstream, or is this likely to be hard to achieve with cython?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it may. I think line profiling after this lands makes sense and should hopefully guide us to which of several things we should further optimize. It could be Status or it could be something else.

Seems like a reasonable request to me (though maybe I'm biased as I already made that request 😄). Have no idea if it will be easy or not. Certainly hope it is doable.

In any event, I think at a first pass having str here is fine. Am more interested in broadly typing things first and worrying about more tuning in subsequent PRs after more profiling.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One other thing I forgot to note, Cython does intern any str literals it sees by default as part of module initialization (IOW at import time), which it hangs onto globally (so they don't go out of scope). That way, when we do something like ts._state = "running", Cython will already have interned "running" and assigned the interned str variable to ts._state. Further when we later do something like assert ts._state == "running", this "running" will also refer to the same interned str. As a result comparisons of strs defined with literals should benefit from the intern speedup.

Though there is a caveat. This does not apply for any dynamically generated strs. So "My name is %s" % s will construct a str at runtime that is not interned.

distributed/scheduler.py Outdated Show resolved Hide resolved
@jakirkham
Copy link
Member Author

Since this just includes the WorkerState PR, maybe we should work on that one first? 😉 Unless we’d rather just work with both of them in this PR? 🙂

@mrocklin
Copy link
Member

mrocklin commented Dec 3, 2020

I don't have a preference where the work is done. It'd be nice to merge one and then the other for git history. It was easier for me to comment here just because everything was here.

@jakirkham
Copy link
Member Author

Understood. If we'd like to merge two separate PRs, would suggest to have WorkerState comments in that PR at least. Otherwise I think once that PR gets merged the comments here may wind up being artificially "resolved" since that code is no longer here, which may make hard for us to ensure we have addressed all of them.

That said, if we find it simply more convenient to work here, it may make more sense to close out that PR and do all the work here. It probably gets harder to disambiguate where changes apply if we start pushing both TaskState and WorkerState changes here.

@jakirkham
Copy link
Member Author

Copied the WorkerState comments to PR ( #4294 ). Let's follow up on those over there 🙂

@jakirkham
Copy link
Member Author

Rebased on master to get rid of the WorkerState bits.

_resource_restrictions: dict
_loose_restrictions: bool
_metadata: dict
_annotations: dict
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also included changes related to annotations from PR ( #4279 ). AFAICT this is just a dict, but please let me know if I'm missing something.

@jakirkham
Copy link
Member Author

Do we want to handle Cythonization of TaskPrefix and TaskGroup in this PR? They are pretty small classes and don't see as much use as TaskState. So think it should be straightforward to do. That said, don't want to overload this PR since it already has a lot of changes. WDYT?

Also would be good to get your thoughts on run_spec as the typing seems unclear ( #4302 (comment) ). Am also ok just leaving it as object if we are not sure. We can always follow up later if needed.

Thanks for the feedback thus far 🙂

@mrocklin
Copy link
Member

mrocklin commented Dec 4, 2020

No preference on TaskPrefix/TaskGroup. I think that you probably care more about git history than I do.

It would be good to make run_spec more consistent, but it's rarely accessed, so I don't think that this is a priority.

@jakirkham
Copy link
Member Author

Was more concerned about how that affects the review load for you. No strong feelings on commit history from me.

Ok let's skip run_spec for now and we can revisit after this lands if it becomes important.

@mrocklin
Copy link
Member

mrocklin commented Dec 4, 2020

Review of this stuff is pretty simple. There isn't any tricky logic to go over. I'm generally happy.

@jakirkham
Copy link
Member Author

Found I was having issues getting Cython to use the type definitions correctly when quoted. So am proposing just swapping the ordering of Task* class definitions around to make things easier. ( #4302 )

@jakirkham
Copy link
Member Author

Need to flip the order of TaskPrefix and TaskGroup. Didn't realize the latter has an attribute that holds the former. Fixing with PR ( #4319 ).

Now that we have properties accessible from `TaskState`, drop the
closures we added previously to access the typed values internally. This
should be equivalently performant and cut out a little bit of
boilerplate.
Make sure to assign to the `TaskPrefix` variable, `tp`, first before
assigning to the `dict`. This should avoid the admittedly likely low
overhead of looking up the result in the dictionary when we already have
the value available.
Saves us need to fetch this twice. Also makes the code a bit more
readable. Finally may allow Cython optimizations on the variable later.
Instead of using `None` for `TaskPrefix.duration_average`, set it `-1`.
This works better when typing `TaskPrefix.duration_average` as it can
always be floating point. This also works logically with this value as
it can't actually be negative unless it wasn't defined. Rework the logic
around this variable to ensure it is positive semi-definite.
This allows Cython to perform C-level optimizations on these variables
and usages thereof.
Make sure to assign to the `TaskGroup` variable, `tg`, first before
assigning to the `dict`. This should avoid the admittedly likely low
overhead of looking up the result in the dictionary when we already have
the value available.
This ensures Cython still uses `TaskGroup` to annotate the variable
iterated over. Otherwise it constructs a generator with its own scope
where this is ignored.
@jakirkham jakirkham changed the title [WIP] Annotate Task* objects for Cythonization Annotate Task* objects for Cythonization Dec 7, 2020
@jakirkham jakirkham marked this pull request as ready for review December 7, 2020 18:26
@jakirkham
Copy link
Member Author

Ok this should be good to go. Please let me know if anything else is needed 🙂

@quasiben
Copy link
Member

quasiben commented Dec 7, 2020

I looked over the scheduler and seems like you are following the same techniques you laid out earlier -- _var with type and property decorators for Python access of the variables. Thanks @jakirkham !!

Merging in now

@quasiben quasiben merged commit 0d4a499 into dask:master Dec 7, 2020
@jakirkham jakirkham deleted the cy_ts branch December 7, 2020 20:29
@jakirkham
Copy link
Member Author

Thanks Ben! 😄

If anything else comes up, happy to follow up in a new issue/PR as appropriate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants