multi-threading question

All ESP32 boards running MicroPython.
Target audience: MicroPython users with an ESP32 board.
User avatar
jimmo
Posts: 2262
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: multi-threading question

Post by jimmo » Mon Feb 22, 2021 3:03 am

bertel wrote:
Sun Feb 21, 2021 5:08 pm
I don’t have any experience with threading in micropython, but according to the docs, _thread in micropython works just like in vanilla Python. There, threading uses the same core. To use multiple cores, we need to use multiPROCESSING. Most likely, the Swiss guy was tricked into believing threading uses multiple cores
Is "the Swiss guy" the linked video? I haven't watched it yet.

But either way:
- On Pico we most definitely use both cores on the RP2040. The _thread module allows you to create at most one additional thread, and then you have the main thread on the first core, and the new thread on the second core. They run completely independently, and because they're 1:1 with cores, there's no scheduler or pre-emptive multitasking required. Additionally, as pythoncoder points out, there's no GIL on the Pico, so they really do run concurrently.
- On ESP32, all MicroPython threads (of which there can be as many as you like) are pinned to the same core, and we rely on FreeRTOS to pre-emptively schedule them. They run with the GIL.

bertel
Posts: 27
Joined: Tue Feb 09, 2021 3:55 pm
Location: Tokyo

Re: multi-threading question

Post by bertel » Mon Feb 22, 2021 6:46 am

@Pythoncoder, both multiTHREADING and multiPROCESSING work very well with standard Python. The threading module and the multiprocessing module come standard with Python. They are different animals.

With the threading module (on which the Micropython _thread module is based) all threads use the same core as the main Python program in timeshare fashion. Of course, as you add multiple compute-intensive Python-threads, the matter may bog down, because all the threads share one and the same core. If the threads mostly service I/O-bound tasks, which involve a lot of waiting for something to happen, Python threads work fine.

If we want to use the power of multiple cores in true parallelism, we need to use Python’s multiPROCESSING module. Until we run out of cores, multiprocessing handles multiple compute-bound tasks with aplomb. The price of it is setup time. Each process takes a while to initiate. I don’t think true multiprocessing exists for the ESP32.
Last edited by bertel on Mon Feb 22, 2021 8:09 am, edited 1 time in total.

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

Re: multi-threading question

Post by pythoncoder » Mon Feb 22, 2021 7:00 am

Perhaps I didn't express myself well, but none of that contradicts my post. I didn't mention the multiprocessing module because MicroPython has no equivalent.

Threading does solve the problem of blocking I/O, both in CPython and MicroPython. However, in MicroPython at least, it is much more efficient to use uasyncio to achieve this if a nonblocking I/O device driver can be used. Common devices like sockets and UARTs are supported.

The GIL remains a much discussed issue limiting threading performance under both Python variants. Cooperative multitasking sidesteps this by only having a single process.
Peter Hinch

bertel
Posts: 27
Joined: Tue Feb 09, 2021 3:55 pm
Location: Tokyo

Re: multi-threading question

Post by bertel » Mon Feb 22, 2021 7:24 am

@Jimmo: Yes, the Swiss Guy is in the video. As for _threading using multiple cores on the Pico: Very confusing. It has been drummed into Python coders that with Python, multithreading is not what one would expect, and that Python multithreading uses the same core, and that for multiple cores, one needs to use multiprocessing. And now suddenly on the Pico, _threading does what multiprocessing does elsewhere. Maybe it should have been called multiprocessing.

User avatar
jimmo
Posts: 2262
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: multi-threading question

Post by jimmo » Wed Feb 24, 2021 12:33 am

bertel wrote:
Mon Feb 22, 2021 7:24 am
It has been drummed into Python coders that with Python, multithreading is not what one would expect, and that Python multithreading uses the same core, and that for multiple cores, one needs to use multiprocessing.
bertel wrote:
Mon Feb 22, 2021 6:46 am
With the threading module (on which the Micropython _thread module is based) all threads use the same core as the main Python program in timeshare fashion.
I don't think this is _quite_ accurate. Python threads aren't pinned to the same core, rather they are free to run on any core. The issue is that they all contend for the same mutex before doing anything useful (the GIL). You would hope that the practical result is that they end up on the same core to prevent that mutex bouncing across cores, but I don't see anything in CPython (e.g. thread_pthread.h) that enforces that.

So the reason that MicroPython on the Pico is special isn't that _thread behaves differently, rather that it runs without a GIL. If you turned off the GIL on the ESP32 port (and disabled core pinning) then it would behave the same way.

You're right that "multiprocessing" would be clearer, but the issue is that Python's multiprocessing module provides a vastly different API and way of working with concurrency (i.e. it's modelled on processes with separate address spaces and VM state).

I agree that it's confusing, but what we provide on the Pico is truly multithreading.

bertel
Posts: 27
Joined: Tue Feb 09, 2021 3:55 pm
Location: Tokyo

Re: multi-threading question

Post by bertel » Wed Feb 24, 2021 4:40 am

Live ad learn, and unlearn ...
jimmo wrote:
Wed Feb 24, 2021 12:33 am
So the reason that MicroPython on the Pico is special isn't that _thread behaves differently, rather that it runs without a GIL. If you turned off the GIL on the ESP32 port (and disabled core pinning) then it would behave the same way.
Interesting. Could you provide some insight in how to do that?

User avatar
jimmo
Posts: 2262
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: multi-threading question

Post by jimmo » Thu Feb 25, 2021 12:01 am

bertel wrote:
Wed Feb 24, 2021 4:40 am
Interesting. Could you provide some insight in how to do that?
The GIL is enabled/disabled via MICROPY_PY_THREAD_GIL in mpconfigport.h (see rp2/mpconfigport.h). It defaults to true if threading is enabled.

Core pinning on ESP32 is a bit of a complicated topic because there are a bunch of complicated ways in which things don't work (I'm sort of hand waving over the detail here because I'm not that familiar and haven't done any testing for a while).

But you can see the thread creation code in esp32/mpthreadport.c where it uses MP_TASK_COREID with xTaskCreatePinnedToCore

TBH running MicroPython on the Pico without the GIL is a bit scary... it would be very easy to end up with bad things happening, so your code has to be very careful around its use of locks and shared variables (i.e. definitely no shared lists or dictionaries without locks). I don't know much about the RP2040's multi-core implementation (e.g. things like cache coherency, etc) so not sure what the implications are there either.

bertel
Posts: 27
Joined: Tue Feb 09, 2021 3:55 pm
Location: Tokyo

Re: multi-threading question

Post by bertel » Thu Feb 25, 2021 4:16 am

Thank you, @Jimmo

Post Reply