Tinche / incant

https://incant.threeofwands.com
Apache License 2.0
65 stars 3 forks source link

avoid roundtrip to event loop in ainvoke #1

Closed adriangb closed 2 years ago

adriangb commented 2 years ago

since there are no awaits in this function other than the return, you can just return the await able objet directly and let the caller await that

Tinche commented 2 years ago

Interesting. Is this approach more efficient? I find the added clarity of the method being async better if not.

adriangb commented 2 years ago

Is this approach more efficient?

In theory, yes. Here's a quick benchmark:

import asyncio
import timeit
from typing import Awaitable, Callable

async def user_func() -> None:
    return None

def def_func() -> Awaitable[None]:
    return user_func()

async def async_def_func() -> None:
    return await user_func()

async def test(func: Callable[[], Awaitable[None]]) -> float:
    start = timeit.default_timer()
    for _ in range(100_000):
        await func()
    return (timeit.default_timer() - start) / 100_000

print(asyncio.run(test(def_func)))  # 2e-7
print(asyncio.run(test(async_def_func)))  # 3e-7

I find the added clarity of the method being async better if not.

I agree. And I'm realizing this is a public facing function, so it would be reasonable to keep it as is just for that (if so, please just close this PR). On the other hand, because of the annotations, user's IDEs should treat the two as equivalent. It's only if a user looks at the source that they might be confused.

Nonetheless, I think it's an interesting tidbit to keep in mind.