Skip to content

asyncio: slight optimizations for run_until_complete and sleep_ms #17699

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions extmod/asyncio/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from time import ticks_ms as ticks, ticks_diff, ticks_add
import sys, select
from select import POLLIN, POLLOUT

# Import TaskQueue and Task, preferring built-in C code over Python code
try:
Expand Down Expand Up @@ -54,7 +55,8 @@ def __next__(self):
# Use a SingletonGenerator to do it without allocating on the heap
def sleep_ms(t, sgen=SingletonGenerator()):
assert sgen.state is None
sgen.state = ticks_add(ticks(), max(0, t))
now = ticks()
sgen.state = ticks_add(now, t) if t > 0 else now
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this give a measurable speed improvement? Is it worth it for the cost in code size?

I measure this change here as +5 bytes to the bytecode. The most taken path will be when ticks_add() needs to be called, which goes from 12 opcodes previously to now 16 opcodes. It's usually the opcode overhead that's slow, rather than the actual call (eg out to max, which should be quick with two small int args). So I would guess that this change actually makes things a little slower.

return sgen


Expand All @@ -66,6 +68,8 @@ def sleep(t):
################################################################################
# Queue and poller for stream IO

nPOLLIN = ~POLLIN
nPOLLOUT = ~POLLOUT

class IOQueue:
def __init__(self):
Expand All @@ -77,13 +81,13 @@ def _enqueue(self, s, idx):
entry = [None, None, s]
entry[idx] = cur_task
self.map[id(s)] = entry
self.poller.register(s, select.POLLIN if idx == 0 else select.POLLOUT)
self.poller.register(s, POLLIN if idx == 0 else POLLOUT)
else:
sm = self.map[id(s)]
assert sm[idx] is None
assert sm[1 - idx] is not None
sm[idx] = cur_task
self.poller.modify(s, select.POLLIN | select.POLLOUT)
self.poller.modify(s, POLLIN | POLLOUT)
# Link task to this IOQueue so it can be removed if needed
cur_task.data = self

Expand Down Expand Up @@ -114,20 +118,20 @@ def wait_io_event(self, dt):
for s, ev in self.poller.ipoll(dt):
sm = self.map[id(s)]
# print('poll', s, sm, ev)
if ev & ~select.POLLOUT and sm[0] is not None:
if ev & nPOLLOUT and sm[0] is not None:
# POLLIN or error
_task_queue.push(sm[0])
sm[0] = None
if ev & ~select.POLLIN and sm[1] is not None:
if ev & nPOLLIN and sm[1] is not None:
# POLLOUT or error
_task_queue.push(sm[1])
sm[1] = None
if sm[0] is None and sm[1] is None:
self._dequeue(s)
elif sm[0] is None:
self.poller.modify(s, select.POLLOUT)
self.poller.modify(s, POLLOUT)
else:
self.poller.modify(s, select.POLLIN)
self.poller.modify(s, POLLIN)


################################################################################
Expand All @@ -153,24 +157,29 @@ def run_until_complete(main_task=None):
global cur_task
excs_all = (CancelledError, Exception) # To prevent heap allocation in loop
excs_stop = (CancelledError, StopIteration) # To prevent heap allocation in loop
queue_peek = _task_queue.peek
queue_pop = _task_queue.pop
wait_io_event = _io_queue.wait_io_event
while True:
# Wait until the head of _task_queue is ready to run
dt = 1
while dt > 0:
while dt:
dt = -1
t = _task_queue.peek()
t = queue_peek()
if t:
# A task waiting on _task_queue; "ph_key" is time to schedule task at
dt = max(0, ticks_diff(t.ph_key, ticks()))
dt = ticks_diff(t.ph_key, ticks())
if dt < 0:
dt = 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, does this change here actually make things faster?

elif not _io_queue.map:
# No tasks can be woken so finished running
cur_task = None
return
# print('(poll {})'.format(dt), len(_io_queue.map))
_io_queue.wait_io_event(dt)
wait_io_event(dt)

# Get next task to run and continue it
t = _task_queue.pop()
t = queue_pop()
cur_task = t
try:
# Continue running the coroutine, it's responsible for rescheduling itself
Expand Down
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy