optimizing uasyncio performance
Posted: Tue Dec 04, 2018 7:06 pm
I'm hoping a uasyncio expert can comment on performance challenges I'm facing. I'm not a uasyncio expert and suspect that I am using the library incorrectly.
Background
I am using the usayncio library in a uPy project to manage operation of sensors, a display, and an I2S microphone. It was all coming together until I starting seeing performance issues with the I2S microphone - gaps were appearing in the audio sample stream.
The I2S microphone code runs in a coro loop that looks like this:
a) read 2048 bytes of audio samples from the microphone using I2S
b) call await asyncio.sleep(0) to yield
c) prune out unused bytes, 2048->512 bytes
d) call await asyncio.sleep(0) to yield
e) write 512 bytes to WAV file on a SD Card
f) call await asyncio.sleep(0) to yield
g) loop back to a)
The ambition is a gap-less audio stream recorded to a WAV file. It works, except when I implement with uasyncio ...
Here is the file being discussed:
https://github.com/MikeTeachman/micropy ... etsense.py
in this repo:
https://github.com/MikeTeachman/micropy ... reet-sense
Performance challenges with uasyncio
1. calls to await asyncio.sleep(0) in this application consistency take > 6ms to run, sometimes 10ms -- this adds delays into the microphone read-write loop, putting constraints on the sample rate that can be used.
https://github.com/MikeTeachman/micropy ... se.py#L283
2. frequent garbage collection
Environment:
- I am using the ESP32 LoBo build, with customization to support I2S
- board is Lolin D32 Pro (4MB PSRAM)
Here is a testcase illustrating both issues. You will see the call to await asyncio.sleep(0) taking > 3ms to return and rapid "consumption" of the uPy heap.
Testcase
testcase output:
Does anyone have suggestions to improve the performance?
thanks!
Background
I am using the usayncio library in a uPy project to manage operation of sensors, a display, and an I2S microphone. It was all coming together until I starting seeing performance issues with the I2S microphone - gaps were appearing in the audio sample stream.
The I2S microphone code runs in a coro loop that looks like this:
a) read 2048 bytes of audio samples from the microphone using I2S
b) call await asyncio.sleep(0) to yield
c) prune out unused bytes, 2048->512 bytes
d) call await asyncio.sleep(0) to yield
e) write 512 bytes to WAV file on a SD Card
f) call await asyncio.sleep(0) to yield
g) loop back to a)
The ambition is a gap-less audio stream recorded to a WAV file. It works, except when I implement with uasyncio ...
Here is the file being discussed:
https://github.com/MikeTeachman/micropy ... etsense.py
in this repo:
https://github.com/MikeTeachman/micropy ... reet-sense
Performance challenges with uasyncio
1. calls to await asyncio.sleep(0) in this application consistency take > 6ms to run, sometimes 10ms -- this adds delays into the microphone read-write loop, putting constraints on the sample rate that can be used.
https://github.com/MikeTeachman/micropy ... se.py#L283
2. frequent garbage collection
Environment:
- I am using the ESP32 LoBo build, with customization to support I2S
- board is Lolin D32 Pro (4MB PSRAM)
Here is a testcase illustrating both issues. You will see the call to await asyncio.sleep(0) taking > 3ms to return and rapid "consumption" of the uPy heap.
Testcase
Code: Select all
import utime
import uasyncio as asyncio
import asyn
import gc
async def idle():
while True:
print(" -- idle --")
await asyncio.sleep(1)
utime.sleep_us(1) # workaround for watchdog trigger issue in LoBo port
print(gc.mem_free())
async def task1(b):
while True:
print(" -- task1 --")
await b
await asyncio.sleep(2)
async def task2(b):
t0 = 0
t1 = 0
while True:
print(" -- task2 --")
t0 = utime.ticks_us()
await asyncio.sleep(0)
t1 = utime.ticks_us()
print("asyncio.sleep(0) = {} us".format(t1-t0))
await b
await asyncio.sleep(3)
loop = asyncio.get_event_loop()
barrier = asyn.Barrier(2)
loop.create_task(idle())
loop.create_task(task1(barrier))
loop.create_task(task2(barrier))
loop.run_forever()
Code: Select all
-- idle --
-- task1 --
-- task2 --
asyncio.sleep(0) = 2464 us
472800
-- idle --
472400
-- idle --
-- task1 --
-- task2 --
asyncio.sleep(0) = 3149 us
417184
-- idle --
416784
-- idle --
-- task1 --
414976
-- idle --
-- task2 --
asyncio.sleep(0) = 3083 us
361264
-- idle --
360864
-- idle --
-- task1 --
357808
-- idle --
-- task2 --
asyncio.sleep(0) = 3087 us
305248
-- idle --
304848
-- idle --
-- task1 --
300448
-- idle --
-- task2 --
asyncio.sleep(0) = 3052 us
249040
-- idle --
248640
-- idle --
-- task1 --
242992
-- idle --
-- task2 --
asyncio.sleep(0) = 3112 us
193312
-- idle --
192912
-- idle --
-- task1 --
185824
-- idle --
-- task2 --
asyncio.sleep(0) = 3099 us
137104
-- idle --
136704
-- idle --
-- task1 --
128368
-- idle --
-- task2 --
asyncio.sleep(0) = 3223 us
81088
-- idle --
80688
-- idle --
-- task1 --
71008
-- idle --
-- task2 --
asyncio.sleep(0) = 3168 us
25168
-- idle --
24768
-- idle --
-- task1 --
13744
-- idle --
-- task2 --
asyncio.sleep(0) = 3056 us
455360
-- idle --
454960
-- idle --
-- task1 --
thanks!