← index #5622Issue #3005
Off-topic · high · value 0.522
QUERY · ISSUE

`with` statement in loop takes cyclically increasing time

openby dhalbertopened 2020-02-08updated 2020-02-10
py-core
import pyb

class WithTest:
    def __init__(self):
        pass
    def __enter__(self):
        pass
    def __exit__(self, *exc):
        pass

x1 = pyb.Pin(pyb.Pin.board.X1, pyb.Pin.OUT_PP)

# CYCLICALLY INCREASING INTERVALS
wt = WithTest()
while True:
    with wt as wtx:
        x1.value(not x1.value())

#OK: CONSTANT INTERVALS
while True:
    with WithTest() as wt:
        x1.value(not x1.value())

The first loop in this test program produces a cyclically increasing square wave. I tested on a PyBoard v1.1 with v1.94 and the also the latest build pybv11-20200208-v1.12-154-gce40abcf2.dfu

The period of the waveform increases from about 38 microseconds to about 240 microseconds. Then there's a gap (gc?) and it starts over.

If you comment out the first loop and use the second (which constructs the with object over and over), it runs at a constant 180 microseconds or so.

The fact that it's a with statement is important; The as is not necessary. We first discovered this while doing I2C transactions in CircuitPython which were taking too long. It took a while to figure out that it was not an I2C issue, but was solely due to the with.

I don't see why the with should inherently cause this behavior, so I think there's something going on in the VM that's slowing things down on each iteration, probably allocating storage which finally gets gc'd. But why should we see an increasing interval? It's like something is walking down an increasingly longer list.

This is easiest to see on an oscilloscope:
https://youtu.be/tGB2uVwig_4 (unlisted; use the link)

Screen shots from Saleae:
first loop (taken at gap: starts fast after a gap and slows down):
Selection_129

second loop (constant before and after gap):
Selection_130

Also filed as https://github.com/adafruit/circuitpython/issues/2602.

CANDIDATE · ISSUE

uasyncio stream reader/writer: unexpected behaviour with UART

closedby peterhinchopened 2017-04-06updated 2017-04-07

Testing done on Pyboard V1.1. The sender coroutine iterates every 2 seconds but no output appears on pin X1:

import uasyncio as asyncio
from pyb import UART
uart = UART(4, 9600)

async def sender():
    swriter = asyncio.StreamWriter(uart, {})
    while True:
        swriter.awrite('Hello uart\n')
        await asyncio.sleep(2)
        print('wrote')

loop = asyncio.get_event_loop()
loop.create_task(sender())
loop.run_forever()

The next script produces output on X1 as expected until a loopback is applied (link X1 and X2). At this point the UART sends data continuously. The data is read correctly, but the call to

await asyncio.sleep(2) no longer behaves as expected.

import uasyncio as asyncio
from pyb import UART
uart = UART(4, 9600)

async def sender():
    while True:
        uart.write('Hello uart\n')
        await asyncio.sleep(2)
        print('Wrote')

async def receiver():
    sreader = asyncio.StreamReader(uart)
    while True:
        res = await sreader.readline()
        print('Recieved', res)

loop = asyncio.get_event_loop()
loop.create_task(sender())
loop.create_task(receiver())
loop.run_forever()

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied