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!
Is Micropython inherently multithreaded?
Re: Is Micropython inherently multithreaded?
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).
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.it does not run to be twice as fast or anywhere near that
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: Is Micropython inherently multithreaded?
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.
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.
Index to my micropython libraries.
Re: Is Micropython inherently multithreaded?
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!!!
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()
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: Is Micropython inherently multithreaded?
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
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.
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))
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: Is Micropython inherently multithreaded?
Peter, I've just scanned those two resources. They're excellent!pythoncoder wrote: ↑Sat Mar 27, 2021 10:09 amOptimisation 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.
I will digest them in detail, apply, and time their performance.
Thank you!!!
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: Is Micropython inherently multithreaded?
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.
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.
Index to my micropython libraries.
Re: Is Micropython inherently multithreaded?
It's cool how some things from long ago turn out to still be around:pythoncoder wrote: ↑Sun Mar 28, 2021 9:20 amA 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.
News: 28 November 2020 - Released DOSBox/Fractint package to allow running on Windows
https://www.fractint.org/