Pi Pico - nearly empty mainloop at 30 kHz only?

CircuitPython on hardware including Adafruit's boards, and CircuitPython libraries using Blinka on host computers.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
Greg0815
 
Posts: 2
Joined: Sat May 27, 2023 10:19 am

Pi Pico - nearly empty mainloop at 30 kHz only?

Post by Greg0815 »

Hello,
i come from 8bit Atmel world with C and also did some Audio/Midi stuff with Teensy and Arduino. Now i started with a Raspberry Pico and CircuitPy hooking up midi, neopixel, a button and some LEDs from the offical examples and tutorials. Everything working as expected but ... there must be something i did not understand right yet so please point me in the right direction.

My problem: the mainloop looks extremely slow to me. No delays, time-lib is not even imported. With midi (from official example) the mainloop runs at 3 kHz. Without all the midi-stuff and just toggling one LED it's at 30 kHz. Example:

Code: Select all

import board
import digitalio

led_3 = digitalio.DigitalInOut(board.GP19)
led_3.direction = digitalio.Direction.OUTPUT

while True:               
    if led_3.value == 1:
        led_3.value = 0
    else:
        led_3.value = 1 
Hm, 30 kHz is what my old Atmega8 (8MhZ crystal) was running at WITH lots of midi-parsing, ADC-reading, 4051-switching plus buttons, LEDs and logic.

Is the CircuitPy framework eating up all the performance from the 133MHz board? Maybe my Raspberry has an issue?
thank you for bringing some light to my old brain
Gregor

User avatar
slight
 
Posts: 120
Joined: Wed Sep 12, 2012 2:23 am

Re: Pi Pico - nearly empty mainloop at 30 kHz only?

Post by slight »

Hi Gregor,

how did you measure the timing?

as CircuitPython is an interpreted language - and the interpretation is done on this little micro-controller it is *normal* that the resulting speed is *slow*.
for the most beginner use-cases it is fast enough... ;-)

i currently have no osci on hand - so i can not tell how fast things move in real -
i just run this speedtest code (orig. from another project therefore way more complicated then needed...):

Code: Select all

# SPDX-FileCopyrightText: 2023 s-light.eu Stefan Krüger
# SPDX-License-Identifier: MIT

import time
import board

import digitalio

led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT



def speed_test(fn, msg, count=1000):
    print("{} speed test running..".format(msg))
    start = time.monotonic()
    for i in range(count):
        fn()
    end = time.monotonic()
    duration = (end - start) / count
    duration_ms = duration * 1000
    result = {
        "msg": msg,
        "duration_ms": duration_ms,
    }
    print(
        "'{}'  needs {:10.3f}ms/call".format(
            result["msg"],
            result["duration_ms"],
        )
    )
    return result


def print_results(speed_tests):
    msg_max_length = max(len(x["msg"]) for x in speed_tests)
    # print(msg_max_length)
    for test in speed_tests:
        msg_template = (
            "'{:<" + "{}".format(msg_max_length + 5) + "}'  needs {:10.3f}ms/call"
        )
        print(
            msg_template.format(
                test["msg"],
                test["duration_ms"],
            )
        )


print("\n" * 20)
speed_tests = []


if hasattr(board, "DISPLAY"):
    import displayio
    print("deactivate display..")
    board.DISPLAY.auto_refresh = False
    board.DISPLAY.root_group = displayio.Group()



def toggle_led():
    led.value = not led.value

speed_tests.append(
    speed_test(
        toggle_led,
        msg="toggle_led",
        count=100000,
    )
)






if hasattr(board, "DISPLAY"):
    print("activate display..")
    board.DISPLAY.auto_refresh = True
    board.DISPLAY.root_group = displayio.CIRCUITPYTHON_TERMINAL





print("\n" * 20)
time.sleep(1)

print_results(speed_tests)
time.sleep(1)

print("done...")

it results in

Code: Select all

0;🐍Wi-Fi: off | Done | 8.0.5soft reboot

Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.
main.py output:
0;🐍Wi-Fi: off | main.py | 8.0.5

deactivate display..
toggle_led speed test running..
'toggle_led'  needs      0.010ms/call
activate display..

'toggle_led     '  needs      0.010ms/call
done...
0;🐍Wi-Fi: off | Done | 8.0.5
Code done running.

Press any key to enter the REPL. Use CTRL-D to reload.
on my Adafruit ESP32-S3 Reverse TFT Feather
(ESP32-S3 Dual Core 240MHz Tensilica processor)
0.01ms/call würde bei mir wohl so 100 MHz ergeben...
das erscheint mir schnell ;-)

User avatar
tannewt
 
Posts: 3304
Joined: Thu Oct 06, 2016 8:48 pm

Re: Pi Pico - nearly empty mainloop at 30 kHz only?

Post by tannewt »

I clocked the RP2040 at ~90khz with this blinky code.

Code: Select all

import digitalio
import board

led = digitalio.DigitalInOut(board.LED)
led.switch_to_output()

while True:
    led.value = True
    led.value = False
The programming paradigm is pretty different due to this overhead. (The benefit is the faster iteration time.) Usually you'll want to rely on CP to do timing sensitive computation. If you find another piece of code that is too slow, then you can add a c module to do it.

User avatar
slight
 
Posts: 120
Joined: Wed Sep 12, 2012 2:23 am

Re: Pi Pico - nearly empty mainloop at 30 kHz only?

Post by slight »

with my speedtest setup i get

Code: Select all

'toggle_led      '  needs     0.0096ms/call
'toggle_led2     '  needs     0.0092ms/call
so i think at this point the overhead of calling a function is big..

retesting with this code
(eliminated the function call)

Code: Select all

# SPDX-FileCopyrightText: 2023 s-light.eu stefan krüger
# SPDX-License-Identifier: MIT

import time
import board

import digitalio

led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT


msg_template = "'{:<10}'  needs {:10.4f}ms/call"
count = 100000


if hasattr(board, "DISPLAY"):
    import displayio

    print("deactivate display..")
    board.DISPLAY.auto_refresh = False
    board.DISPLAY.root_group = displayio.Group()


start = time.monotonic()
for i in range(count):
    led.value = not led.value
end = time.monotonic()
duration = (end - start) / count
duration_ms = duration * 1000
print(msg_template.format("led_blink1", duration_ms))


start = time.monotonic()
for i in range(count):
    led.value = True
    led.value = False
end = time.monotonic()
duration = (end - start) / count
duration_ms = duration * 1000
print(msg_template.format("led_blink2", duration_ms))


if hasattr(board, "DISPLAY"):
    print("activate display..")
    board.DISPLAY.auto_refresh = True
    board.DISPLAY.root_group = displayio.CIRCUITPYTHON_TERMINAL


print("done...")

results in

Code: Select all

'led_blink1'  needs     0.0062ms/call
'led_blink2'  needs     0.0058ms/call
so it seems the function call needs about
0.0096ms/call - 0.0062ms/call = ~3,4 us

User avatar
Greg0815
 
Posts: 2
Joined: Sat May 27, 2023 10:19 am

Re: Pi Pico - nearly empty mainloop at 30 kHz only?

Post by Greg0815 »

Thank you very much for the replies. For speed measuring i simply put an oscilloscope probe on the led pin. I was a bit confused about the speed first while playing around with the midi library. Many incoming data trunks were not forwarded to the serial plotter, it looked like they were simply 'ignored'. I first thought the whole "Pico + CircuitPy for Midi" platform is to 'slow'. The reason was: i used a Yamaha DX7II keyboard which permanently spits out Active sensing messages. Somehow it corrupted the midi data parsing. With another keyboard (not sending active sensing) it worked.
I did not know about the option to implement C-parts in CircuitPy, interesting. I realize i still have a lot to learn :-)

Locked
Please be positive and constructive with your questions and comments.

Return to “Adafruit CircuitPython”