Issue 26081: Implement asyncio Future in C to improve performance (original) (raw)

Created on 2016-01-11 17:23 by yselivanov, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (50)

msg257984 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-01-11 17:23

Some info on this: https://github.com/python/asyncio/issues/282#issuecomment-155957235 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.

msg269773 - (view)

Author: Guido van Rossum (gvanrossum) * (Python committer)

Date: 2016-07-03 20:18

We should really try to get this into 3.6.

--Guido (mobile)

msg270037 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-07-09 12:27

I'm working on this. Some bugs are fixed, but doesn't pass tests for now. https://github.com/methane/cpython/pull/5

msg270055 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-07-09 17:05

Passing all tests now. Yury, could you explain what the comment "This isn't a Future class; it's a BaseFuture" means?

Should it be "_futures.Future" or "_futures.BaseFuture"?

msg270068 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-07-10 03:27

Should I send pull request to github.com/python/asyncio? Or should I post patch here?

msg270069 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-07-10 03:31

I'm working on this. Some bugs are fixed, but doesn't pass tests for now.

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.

Yury, could you explain what the comment "This isn't a Future class; it's a BaseFuture" means?

Unfortunately I don't remember :(

Should I send pull request to github.com/python/asyncio? Or should I post patch here?

Please post it here. AFAIK we haven't yet transitioned to the GitHub.

msg270070 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-07-10 04:28

OK. Here is current version.

msg270175 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-07-11 12:49

In my patch, test_asyncio runs against C version Future.

I saw how test_json tests against C version and pure Python version. But test_asyncio is more larger than test_json.

Before working on it, could someone give me idea to run whole test_asyncio with and without C version Future easily?

And, which is master repository of asyncio? github? or hg.python.org? If github, can I send separated pull request changing test_asyncio after this patch is merged?

msg270183 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-07-11 14:32

Before working on it, could someone give me idea to run whole test_asyncio with and without C version Future easily?

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).

And, which is master repository of asyncio? github? or hg.python.org? If github, can I send separated pull request changing test_asyncio after this patch is merged?

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.

msg270221 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-07-12 02:27

Thanks. I'll working on test_asyncio in next few days.

msg270232 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-07-12 08:50

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.

windows_events.py has some classes extends futures.Future. Task extends Future. There are some isinstance(future, futures.Future).

So monkeypatching baseevent.create_future seems not enough. I want a way to completely reload asyncio and test_asyncio packages with and without C future.

msg270924 - (view)

Author: Marco Paolini (mpaolini) *

Date: 2016-07-21 13:43

THe guys developing uvloop say their implementation is already quite fast [1]. Is it worth integrating it?

[1] https://github.com/MagicStack/uvloop

msg270928 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-07-21 14:19

Yes. Most people will use vanilla asyncio anyways.

msg272242 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-08-09 12:57

Yury, could you review this before 3.6a4?

msg272245 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-08-09 13:33

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).

msg272262 - (view)

Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer)

Date: 2016-08-09 19:26

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.

msg272278 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-08-09 23:30

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).

msg272285 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-08-10 00:57

I'd also think about implementing asyncio.Handle in C (with a freelist).

msg272327 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-08-10 11:57

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).

Implementing full behavior of generator seems difficult to me. I'll implement minimum implementation in next patch.

msg272347 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-08-10 17:28

Implementing full behavior of generator seems difficult to me. I'll implement minimum implementation in next patch.

Sure, but you have to implement send() and throw().

msg272498 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-08-12 05:29

Implemented FutureIter

msg273802 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-08-28 02:48

There are only two weeks until 3.6 beta. Yury, could you review this again?

Or should I implement freelist before review? Implementing freelist may be easy, but measuring the effect of freelist from realistic application is not easy.

msg275658 - (view)

Author: Guido van Rossum (gvanrossum) * (Python committer)

Date: 2016-09-10 17:17

The actual _futures module appears missing from your latest patch -- what's up with that?

msg275729 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-09-11 03:48

Oh, I'm sorry. I usually working on git, and convert git diff to hg diff when posting patch.

I've used patch -p1 instead of hg import --no-edit to apply git patch into hg workdir. I wonder if Rietveld accepts git diff format...

msg275734 - (view)

Author: Guido van Rossum (gvanrossum) * (Python committer)

Date: 2016-09-11 04:54

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.._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.._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.._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 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

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)

msg275740 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-09-11 07:51

Sorry, again. fixed. Now this passes ./python -m test.test_asyncio

msg275903 - (view)

Author: Guido van Rossum (gvanrossum) * (Python committer)

Date: 2016-09-12 00:54

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?

msg275904 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-09-12 01:07

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.)

The code looks fine, I can fix the remaining nits myself. I've left a couple of comments in review.

Ned: Is it better to do this in 3.6b1 or to wait for 3.6b2?

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.

msg275905 - (view)

Author: Guido van Rossum (gvanrossum) * (Python committer)

Date: 2016-09-12 01:08

Yeah, let's do this in 3.6b2.

msg275906 - (view)

Author: Ned Deily (ned.deily) * (Python committer)

Date: 2016-09-12 01:11

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.

msg276494 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-09-14 22:37

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?

msg276504 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-09-15 03:14

I'm working on fixing points you commented. Wait a minute.

Implementing del and repr in C is bit hard task to me. I can't do it in this week. (maybe I can't do it in this month too.)

On Thu, Sep 15, 2016 at 7:37 AM, Yury Selivanov <report@bugs.python.org> wrote:

Yury Selivanov added the comment:

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?



Python tracker <report@bugs.python.org> <http://bugs.python.org/issue26081>


msg276505 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-09-15 03:15

Implementing del and repr in C is bit hard task to me. I can't do it in this week. (maybe I can't do it in this month too.)

NP. I'll take a look myself after you upload the next iteration of the patch...

msg276513 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-09-15 04:15

This is the patch. And git branch is here https://github.com/methane/cpython/pull/5

msg278204 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-10-06 19:07

The most recent patch segfaults... Will try to debug.

msg278206 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-10-06 19:14

INADA, would you be able to take a look?

msg278224 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-10-07 03:17

FutureIter_throw is wrong, maybe. Removing FutureIter_send and FutureIter_throw from FutureIter_methods solves the segv and test passed.

msg278227 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-10-07 03:54

fixed

msg278243 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-10-07 14:07

fastfuture3-wip.patch is work in progress implementation of implementing repr and del in C. I post it to avoid duplicated works.

Known TODOs:

I hope I have enough time to finish in next week, but I'm not sure.

msg278294 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-10-08 12:29

Fixed overriding Future._repr_info(). But I failed to implement overridable Future.del in C yet.

(FYI, fastfuture2.patch passes tests by mix-in del and repr)

msg278325 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-10-08 20:36

Now I understand tp_dealloc, tp_finalize and subtype_dealloc. Attached patch passes tests.

msg278328 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-10-09 01:00

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.

msg278348 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-10-09 05:56

I've committed the patch with trivial fixes (adding curly braces to if statements). And I'm sorry, I committed with wrong issue number.

https://hg.python.org/cpython/rev/678424183b38 (3.6) https://hg.python.org/cpython/rev/f8815001a390 (default)

I fixed NEWS entry already.

msg278357 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-10-09 12:46

I close this issue for now. Further improvements can be new issue.

msg278365 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-10-09 16:00

Thank you, INADA! Next task -- optimize asyncio.Task in C in 3.7. Another 10-15% performance improvement.

msg278366 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-10-09 16:02

I mean another optimization possibility.

msg278368 - (view)

Author: Inada Naoki (methane) * (Python committer)

Date: 2016-10-09 16:16

How about changing module name? _asyncio_speedup for example.

msg278370 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-10-09 16:19

Yes, I think it's a good idea.

msg280359 - (view)

Author: Yury Selivanov (yselivanov) * (Python committer)

Date: 2016-11-08 22:24

This patch introduced multiple refleaks in test_asyncgen.

msg280363 - (view)

Author: Roundup Robot (python-dev) (Python triager)

Date: 2016-11-09 00:05

New changeset 345904bd0456 by Yury Selivanov in branch '3.6': Issue #26081: Fix refleak in _asyncio.Future.iter().throw. https://hg.python.org/cpython/rev/345904bd0456

New changeset b977775aa07d by Yury Selivanov in branch 'default': Merge 3.6 (issue #26081) https://hg.python.org/cpython/rev/b977775aa07d

History

Date

User

Action

Args

2022-04-11 14:58:26

admin

set

github: 70269

2017-03-31 16:36:12

dstufft

set

pull_requests: + <pull%5Frequest881>

2016-11-09 00:06:18

yselivanov

set

priority: release blocker -> normal
status: open -> closed
resolution: fixed

2016-11-09 00:05:50

python-dev

set

nosy: + python-dev
messages: +

2016-11-08 22:24:00

yselivanov

set

priority: normal -> release blocker
status: closed -> open
resolution: fixed -> (no value)
messages: +

2016-10-09 16:19:55

yselivanov

set

messages: +

2016-10-09 16:16:30

methane

set

messages: +

2016-10-09 16:02:07

yselivanov

set

messages: +

2016-10-09 16:00:43

yselivanov

set

messages: +

2016-10-09 13:00:45

berker.peksag

set

stage: patch review -> resolved
versions: + Python 3.7

2016-10-09 12:46:52

methane

set

status: open -> closed
resolution: fixed
messages: +

2016-10-09 05:56:58

methane

set

messages: +

2016-10-09 01:00:01

yselivanov

set

messages: +

2016-10-08 20:36:24

methane

set

files: + fastfuture4.patch

messages: +

2016-10-08 12:29:44

methane

set

files: + fastfuture3.patch

messages: +

2016-10-07 14:07:17

methane

set

files: + fastfuture3-wip.patch

messages: +

2016-10-07 03:54:52

methane

set

files: + fastfuture2.patch

messages: +

2016-10-07 03:17:43

methane

set

messages: +

2016-10-06 19:14:48

yselivanov

set

messages: +

2016-10-06 19:07:15

yselivanov

set

messages: +

2016-09-16 16:44:51

RemiCardona

set

nosy: + RemiCardona

2016-09-15 04:16:03

methane

set

files: + fastfuture.patch

messages: +

2016-09-15 03:15:40

yselivanov

set

messages: +

2016-09-15 03:14:32

methane

set

messages: +

2016-09-14 22:37:51

yselivanov

set

messages: +

2016-09-12 01:11:18

ned.deily

set

messages: +

2016-09-12 01:08:24

gvanrossum

set

messages: +

2016-09-12 01:07:37

yselivanov

set

messages: +

2016-09-12 00:54:25

gvanrossum

set

nosy: + ned.deily
messages: +

2016-09-11 07:51:14

methane

set

files: + fastfuture.patch

messages: +

2016-09-11 04:54:14

gvanrossum

set

messages: +

2016-09-11 03:48:33

methane

set

files: + fastfuture.patch

messages: +

2016-09-10 17:17:12

gvanrossum

set

messages: +

2016-09-10 15:01:27

methane

set

files: + fastfuture.patch

2016-08-28 02:48:35

methane

set

messages: +

2016-08-12 05:29:41

methane

set

files: + futures.patch

messages: +

2016-08-10 17:28:33

yselivanov

set

messages: +

2016-08-10 11:57:39

methane

set

messages: +

2016-08-10 00:57:17

yselivanov

set

messages: +

2016-08-09 23:30:50

yselivanov

set

messages: +

2016-08-09 19:26:15

serhiy.storchaka

set

nosy: + serhiy.storchaka
messages: +

2016-08-09 13:33:18

yselivanov

set

messages: +

2016-08-09 13:16:47

berker.peksag

set

stage: needs patch -> patch review
versions: - Python 3.5

2016-08-09 12:57:51

methane

set

messages: +

2016-07-21 14:19:20

yselivanov

set

messages: +

2016-07-21 13:43:26

mpaolini

set

nosy: + mpaolini
messages: +

2016-07-12 08:50:03

methane

set

messages: +

2016-07-12 02:27:39

methane

set

messages: +

2016-07-11 14:32:51

yselivanov

set

messages: +

2016-07-11 12:49:25

methane

set

messages: +

2016-07-10 04:28:30

methane

set

files: + futures.patch

messages: +

2016-07-10 03:31:49

yselivanov

set

messages: +

2016-07-10 03:27:06

methane

set

messages: +

2016-07-09 17:05:42

methane

set

messages: +

2016-07-09 12:27:41

methane

set

nosy: + methane
messages: +

2016-07-03 20🔞20

gvanrossum

set

messages: +

2016-07-03 19:43:45

giampaolo.rodola

set

nosy: + giampaolo.rodola

2016-01-11 17:23:42

yselivanov

create