This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author v2m
Recipients Mark.Shannon, brandtbucher, carljm, corona10, dino.viehland, gvanrossum, itamaro, v2m
Date 2022-03-22.16:50:02
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1647967802.82.0.244366912836.issue46965@roundup.psfhosted.org>
In-reply-to
Content
- introducing dedicated opcodes for each kind of awaited call is definitely an option. In fact first implementation used it however as Dino has mentioned it was more of a logistical issue (there were several spots that produced .pyc files so compiler needed to be up to date across all of them).
- there was some perf win that was coming from rewriting `gather` in C however main reason for us to do it was the ability to be await-aware which we made only available in C (also returning completed waithandle is not exposed to Python either) to reduce the scope. Providing ability to follow await-aware protocol (read indicator that call is awaited + return completed waithandle for eagerly completed calls) in pure Python is definitely possible
- to provide some context why it was beneficial: typical implementation of endpoint in IG is an async function that in turn calls into numerous other async functions to generate an output. 
  - `gather` is used all over the place in case if there are no sequential dependency between calls
  - amount of unique pieces of data that are ultimately fetched by async calls is not very big, i.e. the same fragment of information can be requested by different async calls which makes memoization a very attractive strategy to reduce I/O and heavyweight computations.
  - memoized pieces of data is represented effectively by completed futures and it is very common to have `gather` accept either memoized value or coroutine object that will be completed synchronously by awaiting memoized value.

Before making gather await-aware if always have to follow the standard process and convert awaitables into tasks that are queued into the event loop for execution. In our workload task creation/queueing were adding a noticeable overhead. With await-aware gather we can execute coroutine objects eagerly and if they were not suspended - bypass task creation entirely.  
```
import asyncio
import time

async def step(i):
    if i == 0:
        return
    await asyncio.gather(*[step(i - 1) for _ in range(6)])

async def main():
    t0 = time.perf_counter()
    await step(6)
    t1 = time.perf_counter()
    print(f"{t1 - t0} s")

N = 0
def create_task(loop, coro):
    global N
    N += 1
    return asyncio.Task(coro, loop=loop)

loop = asyncio.get_event_loop()
loop.set_task_factory(create_task)
loop.run_until_complete(main())
print(f"{N} tasks created")

# Cinder
# 0.028410961851477623 s
# 1 tasks created

# Python 3.8
# 1.2157012447714806 s
# 55987 tasks created
```
History
Date User Action Args
2022-03-22 16:50:02v2msetrecipients: + v2m, gvanrossum, carljm, dino.viehland, Mark.Shannon, corona10, brandtbucher, itamaro
2022-03-22 16:50:02v2msetmessageid: <1647967802.82.0.244366912836.issue46965@roundup.psfhosted.org>
2022-03-22 16:50:02v2mlinkissue46965 messages
2022-03-22 16:50:02v2mcreate