Is Micropython inherently multithreaded?

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
Post Reply
hwiguna
Posts: 7
Joined: Sat Aug 17, 2019 8:07 pm

Is Micropython inherently multithreaded?

Post by hwiguna » Fri Mar 26, 2021 3:42 am

After much hair pulling, I was able to get my Mandelbrot calculation run on two threads (core0 as usual, plus core1 via _thread.start_new_thread().

However, it does not run to be twice as fast or anywhere near that.
Is MicroPython inherently already multithreaded and I'm just inefficiently trying to do what it already does?
Thx!

stijn
Posts: 735
Joined: Thu Apr 24, 2014 9:13 am

Re: Is Micropython inherently multithreaded?

Post by stijn » Fri Mar 26, 2021 7:21 am

No MicroPython is singlethreaded by default. As in, the interpreter won't automatically distribute calculations over multiple threads (assuming that is what you are asking).
it does not run to be twice as fast or anywhere near that
That is not surprising. Expecting x threads to lead to execution time being divided by x is incorrect. There are so many other things which need to be considered, and writing multithreaded code correctly is also pretty hard, that most 'naive' implementation will not speed things up.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Is Micropython inherently multithreaded?

Post by pythoncoder » Fri Mar 26, 2021 8:19 am

My understanding is that this is generally correct but is incorrect on the Pico.

This platform supports a single thread which runs on the other core, with its own exclusive interpreter. However running a heavy duty calculation on the other core won't achieve anything if the main program just spins its wheels waiting for a result. To speed things up you need to do half the calculation on one core and the other half on the other. Shouldn't be too hard with Mandelbrot.
Peter Hinch
Index to my micropython libraries.

hwiguna
Posts: 7
Joined: Sat Aug 17, 2019 8:07 pm

Re: Is Micropython inherently multithreaded?

Post by hwiguna » Fri Mar 26, 2021 12:34 pm

Thank you for the replies everyone!
If you're interested, here is the dual core version:
https://github.com/hwiguna/HariFun_202_ ... ualCore.py

It works, but it is only marginally faster than the single core version:
https://github.com/hwiguna/HariFun_202_ ... rotQuik.py

I tried to distribute the mandelbrot calculation equally between the two cores by going though the X axis in steps of 2.
All Y for X is calculated by the thread, and all Y for X+1 is calculated by main thread.

Functions of interest are:
Loop() where I call DrawMandelbrotX() and then wait for user input.
DrawMandelbrotX() where I spin a thread to compute all Y pixels for column X, and also compute all Y pixels for column X+1 then loop to X+2
mandelbrotThreadX() is basically the same calculation as in DrawMandelbrotX() but stores the pixel results in an array of booleans.
When done I set a global variable resultsReady to True.

Back in DrawMandelbrotX() I wait for resultsReady to become true, then plot the results pixel array on the actual screen.

Do you see how we could improve this code for better multi core performance? Thanks!!!

Code: Select all

def mandelbrotThreadX(x):
    global MAX_ITER
    global WIDTH, HEIGHT, realStart, realEnd, imStart, imEnd
    global results, resultsReady
    #print("Thread Begin x=",x)
    xx = realStart + (x / WIDTH) * (realEnd - realStart)
#     for y in range(HEIGHT):
#         results[y]=False
    for y in range(HEIGHT):
        yy = imStart + (y / HEIGHT) * (imEnd - imStart)
        c = complex(xx, yy) # Convert pixel coordinate to complex number
        m = mandelbrot(c)   # Compute the number of iterations
        color = 1 - int(m/MAX_ITER)
        results[y] = color>0
    resultsReady = True
    #print("Thread Done x", x)
    _thread.exit() # when done, commit suicide so we could be re-incarnated for next X.

def DrawMandelbrotX():
    global oled, brotFB, cursorFB, isHiRez, nextRefresh, MAX_ITER
    global results, resultsReady
    print("DRAWING:", realStart, realEnd, imStart, imEnd)
    stopWatch = time.ticks_ms()
    RE_START = realStart
    RE_END = realEnd
    IM_START = imStart
    IM_END = imEnd

    brotFB.fill(0)
    
    for x in range(0, WIDTH,2): # We're drawing two columns at a time. One by the thread, the other by main.
        resultsReady=False # Will be set by thread to True when it's done computing column.
        _thread.start_new_thread(mandelbrotThreadX,(x,))
        
        x1 = x+1
        #print("Main begin x1=",x1)
        xx = RE_START + (x1 / WIDTH) * (RE_END - RE_START)
        for y in range(0, HEIGHT, 1):
            yy = IM_START + (y / HEIGHT) * (IM_END - IM_START)
            c = complex(xx, yy) # Convert pixel coordinate to complex number
            m = mandelbrot(c)   # Compute the number of iterations
            color = 1 - int(m/MAX_ITER)
            brotFB.pixel(x1,y, 1 if color>0 else 0) # Plot the point
        #print("Main End x1=",x1)
                   
        #stopwatchStart = time.ticks_ms()
        while not resultsReady:
            pass
        #print("waited ", time.ticks_ms()-stopwatchStart, "ms")
        
        # Plot the X column computed by the thread
        for y in range(HEIGHT):
            brotFB.pixel(x,y, 1 if results[y] else 0)

        if x % 6 == 0: # No need to refresh everytime we go through X loop.
            oled.blit(brotFB,0,0)
            oled.show()

    oled.blit(brotFB,0,0)
    oled.show()

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Is Micropython inherently multithreaded?

Post by pythoncoder » Sat Mar 27, 2021 10:09 am

Optimisation is an involved process. A first step is to read this doc and this one to learn about MicroPython specific optimisations. There are a lot of ways to improve performance.

After fixing design issues a key part of the process is determining where the time is being used up, and concentrating your efforts on optimising that part of the code. Timings should be measured with

Code: Select all

from time import ticks_us, ticks_diff  # Not by subtraction
# code omitted
start = ticks_us()
# Section of code to be measured
print(ticks_diff(ticks_us(), start))
I haven't used threading on the Pico, but it is possible that the overhead in starting and stopping a thread is significant. It may be more efficient to run the thread for the entire duration of computing half of the set, rather than repeatedly starting and stopping it. But that's a guess: you'll need to measure it.
Peter Hinch
Index to my micropython libraries.

hwiguna
Posts: 7
Joined: Sat Aug 17, 2019 8:07 pm

Re: Is Micropython inherently multithreaded?

Post by hwiguna » Sat Mar 27, 2021 12:51 pm

pythoncoder wrote:
Sat Mar 27, 2021 10:09 am
Optimisation is an involved process. A first step is to read this doc and this one to learn about MicroPython specific optimisations. There are a lot of ways to improve performance.
Peter, I've just scanned those two resources. They're excellent!
I will digest them in detail, apply, and time their performance.
Thank you!!!

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Is Micropython inherently multithreaded?

Post by pythoncoder » Sun Mar 28, 2021 9:20 am

A point to consider is that the Pico does not support hardware floating point. It will therefore be substantially slower than platforms such as the Pyboards. I believe there is an integer algorithm for Mandelbrot. There is, or used to be, a PC program called fractint.

Don't ask me how this is done. To this "bear of little brain" the whole concept seems rooted in the notion of complex numbers of widely ranging magnitudes.
Peter Hinch
Index to my micropython libraries.

jbeale
Posts: 2
Joined: Tue Mar 16, 2021 8:31 pm

Re: Is Micropython inherently multithreaded?

Post by jbeale » Mon Mar 29, 2021 2:55 am

pythoncoder wrote:
Sun Mar 28, 2021 9:20 am
A point to consider is that the Pico does not support hardware floating point. It will therefore be substantially slower than platforms such as the Pyboards. I believe there is an integer algorithm for Mandelbrot. There is, or used to be, a PC program called fractint.
It's cool how some things from long ago turn out to still be around:
News: 28 November 2020 - Released DOSBox/Fractint package to allow running on Windows
https://www.fractint.org/

Post Reply