Skip to content

Ideas on optimizing uasyncio #2622

Closed
Closed
@pfalcon

Description

@pfalcon

With #2219 landed and uasyncio updated to it (in a branch), discuss further ideas for improvement (many would require changes to uPy for "optimizations"), so ticket is here:

  • A thought: millisecond-perfect tasks scheduling requires working with times on microsecond level, and in a smart way. E.g. there's no warranty that after sleep_ms(20) ticks_ms() will be 20 late (can be 21, on funny systems can be 19).
  • The aim should be achieving memalloc-free running of non-recursive coroutines. The current culprit is actually packing task entries into a queue, which requires creating a tuple. Thus, uasyncio queue should be optimized, and now that RFC: Optimized heap queue implementation for uasyncio #2219 has landed, the next obvious is step is to revert it and redo as an adhoc queue. That all was expected long ago, but seeing's believing.
  • yield from's aka await's from cosubroutines still will pose a problem, each such cocall will cause allocation. Here, we should try to exploit paradigmatic duality of closures and objects - they both approach storing internal state from different sides. And indeed, yield from works not with just coros, but with objects, on which it will call __next__(). However, we still don't want to allocate object either, what I have in mind is having and async method of an object with a special call semantics, where it's passed its object and can yield (unlike __next__??). This is not achievable in Python, but we'll definitely need ability to code coroutines in C. So, the idea is to have:
line = await stream.readline()

to have following call semantics:

StreamClass.readline__next__(stream)

I.e. there should be special methods in an object, await'ing which just calls them as a normal method, but allowing it to indicate that it yields in addition to just returning a value. The problem comes with handling arguments, e.g.

line = await stream.readline(512)

That either will require special compiler treatment to transform that to:

StreamClass.readline__next__(stream, 512)

Or, given that coroutine semantics is strictly sequential, i.e. it's an error to call .readline() coroutine on the same object while another call is active, i.e. there's nor reenterability problem, we can do it like:

stream.last_args = (512,)
StreamClass.readline__next__(stream)

This can be completely implemented on object level, i.e. __call__ on special comethod stores passed args and returns self, then __next__ on it executes it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    rfcRequest for Comment

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions