Speed up the coroutine-function check in watcher creation - #178
Closed
Korijn wants to merge 1 commit into
Closed
Conversation
Watcher.__init__ calls inspect.iscoroutinefunction twice (once for the watched function, once for the callback), and each call walks the generic inspect machinery: unwrapping functools.partials, method checks and function-like checks. That is wasted work for plain functions and methods, which are by far the common case. Add a fast-path helper that answers directly from the callable's code flags and only falls back to inspect for callables without a __code__ attribute (e.g. functools.partial) or ones explicitly marked with inspect.markcoroutinefunction. A parity test checks the helper against inspect.iscoroutinefunction for every kind of callable that can be watched. This makes watcher creation ~14% faster (5957 -> 5116 ns/op on the watcher_creation benchmark setup), recovering part of the overhead that was accepted with #177. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T4cFbKSCxjGabtJbB9e5H2
Collaborator
Author
Benchmarks failure diagnosis: gate noise, not a regressionThe failed Benchmarks job is not caused by this PR. Evidence:
So the Suggestions (happy to PR any of these)
No changes planned to this PR itself — the code is unaffected by the failure. Generated by Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #177, recovering part of the watcher-creation overhead that was accepted there.
What
Watcher.__init__callsinspect.iscoroutinefunctiontwice — once for the watched function and once for the callback. Each call goes through the generic inspect machinery (functools.partialunwrapping, method and function-like checks), which profiling showed accounts for ~500 ns per watcher creation. For plain functions and methods — by far the common case — the answer is available directly from the callable's code flags.This adds a fast-path
iscoroutinefunctionhelper inobserv.watcherthat checks__code__.co_flags & CO_COROUTINEdirectly and only falls back toinspect.iscoroutinefunctionfor callables without a__code__attribute (e.g.functools.partial, custom callables) or ones explicitly marked withinspect.markcoroutinefunction(Python ≥ 3.12). The helper is also used inweak(), so bound-method watchers benefit as well.Correctness
A new parity test asserts the helper agrees with
inspect.iscoroutinefunctionfor every kind of callable that can be watched: plain sync/async functions, lambdas, bound sync/async methods, callable instances, partials wrapping sync/async functions, builtins, and (on 3.12+)markcoroutinefunction-decorated functions.Result
Watcher creation goes from 5957 → 5116 ns/op (~14% faster) on the
test_watcher_creationbenchmark setup, measured back-to-back against master on the same machine.Full test suite passes (210 tests incl. the Qt group), ruff check/format clean.
Note: this narrows, but does not fully close, the
test_watcher_creationgap accepted in #177 — the Benchmarks gate may still flag that one benchmark against the pre-#177 baseline's spirit, though this PR's own baseline is post-#177 master, against which it is a strict improvement.🤖 Generated with Claude Code
https://claude.ai/code/session_01T4cFbKSCxjGabtJbB9e5H2
Generated by Claude Code