-
-
Notifications
You must be signed in to change notification settings - Fork 29.9k
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
Implement asyncio Future in C to improve performance #70269
Comments
Some info on this: python/asyncio#282 (comment) Long story short, Future implemented in C can speedup some asyncio code up to 25%. I'm attaching a patch with a WIP implementation. There are some failing assertions deep in GC, which I need to track down. 'Future.remove_done_callback' still needs to be properly implemented. |
We should really try to get this into 3.6. --Guido (mobile) |
I'm working on this. Some bugs are fixed, but doesn't pass tests for now. |
Passing all tests now. Should it be "_futures.Future" or "_futures.BaseFuture"? |
Should I send pull request to github.com/python/asyncio? |
Thanks a lot! I couldn't find time to finish this myself. I can definitely help you and review the patch once it's ready.
Unfortunately I don't remember :(
Please post it here. AFAIK we haven't yet transitioned to the GitHub. |
OK. Here is current version. |
In my patch, test_asyncio runs against C version Future. I saw how test_json tests against C version and pure Python version. Before working on it, could someone give me idea to run whole test_asyncio And, which is master repository of asyncio? github? or hg.python.org? |
asyncio uses loop.create_future() to create sockets. I'd suggest you to create two base test classes: one that monkeypatches loop.create_future to return pure python Future in its setUp method; an another, that makes create_future to return a C version of the Future. The derive some unittests from those base classes (which will effectively double the number of tests).
The master repo for asyncio is github, but since the C version won't be a part of asyncio (it will be checked in only in CPython source tree), I think it's fine to continue the work here, on bugs.python.org. |
Thanks. I'll working on test_asyncio in next few days. |
windows_events.py has some classes extends futures.Future. So monkeypatching |
THe guys developing uvloop say their implementation is already quite fast [1]. Is it worth integrating it? |
Yes. Most people will use vanilla asyncio anyways. |
Yury, could you review this before 3.6a4? |
Left a couple of comments; the important one -- Future.__await__ (and Future.__iter__) should always return a *new* instance of a generator-like object (tied to the Future object). |
See also a discussion on Python-Dev about rewriting contextlib.contextmanager in C: https://mail.python.org/pipermail/python-dev/2016-August/thread.html#145786 . What parts of Future are performance critical? Maybe it is worth to implement in C only the most critical code. |
Basically everything. Contrary to @contextmanager, Futures are the building blocks of asyncio, so instantiation + awaiting on them + setting results must be fast. To cover instantiation, I want to add a freelist for Futures, so this basically requires them to be implemented in C (and it's not a lot of C code actually). |
I'd also think about implementing asyncio.Handle in C (with a freelist). |
Implementing full behavior of generator seems difficult to me. |
Sure, but you have to implement send() and throw(). |
Implemented FutureIter |
There are only two weeks until 3.6 beta. Or should I implement freelist before review? |
The actual _futures module appears missing from your latest patch -- what's up with that? |
Oh, I'm sorry. I've used |
Thanks! I can't review the whole thing, but I patched it in and tried running the asyncio/examples/crawl.py example, like so: $ ~/src/cpython36/python.exe examples/crawl.py xkcd.com -q
Exception RuntimeError('yield was used instead of yield from in task <Task pending coro=<Crawler.fetch() running at examples/crawl.py:778>> with <Future pending cb=[_chain_future.<locals>._call_check_cancel() at /Users/guido/src/cpython36/Lib/asyncio/futures.py:472]>',) for ('xkcd.com', 80)
ERROR:asyncio:Task exception was never retrieved
future: <Task finished coro=<Crawler.fetch() done, defined at examples/crawl.py:769> exception=RuntimeError('yield was used instead of yield from in task <Task pending coro=<Crawler.fetch() running at examples/crawl.py:778>> with <Future pending cb=[_chain_future.<locals>._call_check_cancel() at /Users/guido/src/cpython36/Lib/asyncio/futures.py:472]>',)>
Traceback (most recent call last):
File "/Users/guido/src/cpython36/Lib/asyncio/tasks.py", line 241, in _step
result = coro.throw(exc)
File "examples/crawl.py", line 778, in fetch
yield from fetcher.fetch() # Fetcher gonna fetch.
File "examples/crawl.py", line 507, in fetch
yield from self.request.connect()
File "examples/crawl.py", line 315, in connect
self.port, self.ssl)
File "examples/crawl.py", line 143, in get_connection
ipaddrs = yield from self.loop.getaddrinfo(host, port)
RuntimeError: yield was used instead of yield from in task <Task pending coro=<Crawler.fetch() running at examples/crawl.py:778>> with <Future pending cb=[_chain_future.<locals>._call_check_cancel() at /Users/guido/src/cpython36/Lib/asyncio/futures.py:472]>
*** Report ***
http://xkcd.com no response object
Finished 0 urls in 0.041 secs (max_tasks=100) (0.000 urls/sec/task)
Todo: 0
Busy: 1
Done: 0
Date: Sat Sep 10 21:50:08 2016 local time
Traceback (most recent call last):
File "examples/crawl.py", line 864, in <module>
main()
File "examples/crawl.py", line 852, in main
loop.run_until_complete(crawler.crawl()) # Crawler gonna crawl.
File "/Users/guido/src/cpython36/Lib/asyncio/base_events.py", line 438, in run_until_complete
return future.result()
File "/Users/guido/src/cpython36/Lib/asyncio/tasks.py", line 241, in _step
result = coro.throw(exc)
File "examples/crawl.py", line 766, in crawl
yield from self.termination.wait()
File "/Users/guido/src/cpython36/Lib/asyncio/locks.py", line 326, in wait
yield from fut
RuntimeError: yield was used instead of yield from in task <Task pending coro=<Crawler.crawl() running at examples/crawl.py:766> cb=[_run_until_complete_cb() at /Users/guido/src/cpython36/Lib/asyncio/base_events.py:164]> with <Future pending> Without your diff, that works, and the output includes this line: Finished 1786 urls in 7.105 secs (max_tasks=100) (2.514 urls/sec/task) |
Sorry, again. fixed. |
Yury: What do you think of the code? How solid is it? (The issue I found was due to my own very recent changes to _blocking.) Ned: Is it better to do this in 3.6b1 or to wait for 3.6b2? |
The code looks fine, I can fix the remaining nits myself. I've left a couple of comments in review.
TBH it would be way more convenient if we could push this into b2. I can push this on Tuesday without rushing things, and we'll have plenty of time to watch buildbots etc. |
Yeah, let's do this in 3.6b2. |
This change touches a lot of files and affect both the unix* and Windows build processes so, yeah, I think it's too risky to go in to b1. Let's get it in as soon as possible after b1. |
INADA, would you be able to address my last review comments? Also, I'm wondering what if we could implement __del__ and __repr__ in C too, so that we could drop BaseFuture class? |
I'm working on fixing points you commented. Wait a minute. Implementing __del__ and __repr__ in C is bit hard task to me. On Thu, Sep 15, 2016 at 7:37 AM, Yury Selivanov <report@bugs.python.org> wrote:
|
NP. I'll take a look myself after you upload the next iteration of the patch... |
This is the patch. |
The most recent patch segfaults... Will try to debug. |
INADA, would you be able to take a look? |
FutureIter_throw is wrong, maybe. |
fixed |
fastfuture3-wip.patch is work in progress implementation of Known TODOs:
I hope I have enough time to finish in next week, but I'm not sure. |
Fixed overriding Future._repr_info(). (FYI, fastfuture2.patch passes tests by mix-in __del__ and __repr__) |
Now I understand tp_dealloc, tp_finalize and subtype_dealloc. |
I quickly looked over the patch and I think it's good. If anything we still have time to hunt down any bugs or even revert this before 3.6 final. INADA, feel free to commit it before Monday to 3.6 and default branches. |
I've committed the patch with trivial fixes (adding curly braces to if statements). https://hg.python.org/cpython/rev/678424183b38 (3.6) I fixed NEWS entry already. |
I close this issue for now. |
Thank you, INADA! Next task -- optimize asyncio.Task in C in 3.7. Another 10-15% performance improvement. |
I mean another optimization possibility. |
How about changing module name? |
Yes, I think it's a good idea. |
This patch introduced multiple refleaks in test_asyncgen. |
New changeset 345904bd0456 by Yury Selivanov in branch '3.6': New changeset b977775aa07d by Yury Selivanov in branch 'default': |
Misc/NEWS
so that it is managed by towncrier #552Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: