In the Q&A session following my EuroPython 2025 presentation about the Microdot web framework, a member of the audience asked me what the performance of MicroPython running on a microcontroller is. This took me a bit by surprise, because while I knew that microcontrollers are slow and underpowered devices, I really had no way to quantify this. I never questioned the supposedly low performance, because it was never a problem for me. My answer to the question was that microcontrollers cannot replace a computer, and that these devices are only useful for small, focused tasks that are not demanding in any way. But after returning from the conference I kept thinking about this question, which piqued my curiosity. So I decided to build a better mental image of the performance these little machines have. In this blog post I want to share some results that compare Python code running on a few microcontroller boards that I have collected through my experiments with hardware, along with my laptop and a Raspberry Pi 4 to help put things into perspective. A note about benchmarks You should know that I'm not a big fan of benchmarks. A few years ago I've written about this in a blog post that I titled Ignore All Web Performance Benchmarks, Including This One. My feelings about benchmarks haven't changed. They are useful to form a general idea, but are often taken too literally, and used as proof that "X" is better than "Y", which is almost always a simplification. Also, benchmarking running code is actually quite difficult, because the speed of execution isn't constant and can change dramatically from one moment to the next, so you can't think of results as absolute or universal. And benchmarks are very easy to skew in one direction or another, as I demonstrated in the blog post referenced above. All this to say that this exercise is driven merely by curiosity and is not intended to prove anything. With that out of the way, let's do some benchmarking! The contenders To evaluate MicroPython performance, I've decided to use a selection of the microcontrollers that I regularly test my Microdot project on. I have also included a Raspberry Pi 4 (not to be confused with the Raspberry Pi Pico line of microcontrollers) and the laptop in which I'm now writing this article, which is a Framework laptop with an Intel Core i5 CPU running Ubuntu. The following table shows the devices that I selected, along with the RAM they have. I'm showing the Raspberry Pi 4 and the Framework laptop in italics, to highlight that they are in a completely different category and only included in the comparison to give a sense of the scale difference. Device RAM ESP8266 64KB Raspberry Pi Pico W 264KB Raspberry Pi Pico 2W 512KB ESP32-S3 512KB Raspberry Pi 4 Model B 8GB Framework Laptop 32GB Just so that the difference is clear, note that the microcontrollers measure RAM in KB (kilobytes, or 1,024 byte units), while the computers use GB (gigabytes, or 1,024 * 1,024 * 1,024 byte units). Looking at the top and bottom rows in the table above, my laptop has 524,288 times more RAM than the ESP8266! As for MicroPython, I've installed version 1.25.0 (the latest official release available at the time I'm writing this) in all the microcontrollers, and also on the laptop. I have also tested the scripts under CPython 3.13 on the laptop, since I had it already installed. I did not think it was a good use of my time to figure out how to compile MicroPython for the Raspberry Pi 4, so on that device I only tested with the already available CPython 3.9, which comes with the version of the Raspberry Pi OS I have installed on this device. The benchmark For my benchmark, I decided to adapt the test scripts I built for my CPython benchmarking article to run on MicroPython. For that article I wrote two CPU-intensive applications, one that calculated Fibonacci numbers, and another that used the bubble sort algorithm to sort an array of numbers. To generate the results I'm about to share, I executed each of the scripts on all the test devices. I ran each script three times on each device, and averaged the durations reported for each run. In the case of the microcontrollers, each run was triggered from the MicroPython REPL right after a soft reboot. fibo.py This first test script calculates the 30th Fibonacci number. Here is the source code for fibo.py: import time if hasattr(time, 'ticks_us'): def t(): return time.ticks_us() / 1000000 else: def t(): return time.time() def fibo(n): if n <= 1: return n else: return fibo(n-1) + fibo(n-2) fibo(30) # warm up s = t() fibo(30) e = t() print(int((e - s) * 1000) / 1000) The script first runs a warm up calculation, and then it does it one more time while keeping track of the clock. Measuring accurate time is one area that works slightly differently in MicroPython compared to CPython. Because I wanted my test programs to work on both Python implementations, you can see that near the top of the script I included a t() function that is defined differently based on the functions that are available in the time module. I had to do this because time.time() in MicroPython returns an integer, so it cannot be used to measure times with a resolution smaller than a second. One interesting aspect of fibo.py is that it uses recursion for its calculations. Recursion is really convenient, but it is also fairly expensive in terms of stack usage. The script failed with a stack overflow on two of my microcontrollers. Here are the results I obtained: Device Interpreter Time (seconds) Framework Laptop MicroPython 1.25.0 0.183 Raspberry Pi Pico 2W MicroPython 1.25.0 16.238 ESP32-S3 MicroPython 1.25.0 30.960 Raspberry Pi Pico W MicroPython 1.25.0 Stack overflow ESP8266 MicroPython 1.25.0 Stack overflow Framework Laptop CPython 3.13 0.078 Raspberry Pi 4 Model B CPython 3.9 0.616 What can we make of this? Well, the most important detail is that you cannot really compare the speed of running code in computers versus microcontrollers, as they are on a completely different scale. Looking at this table you can see that the laptop running the Linux version of MicroPython completed the task in less than 0.2 seconds, while the two microcontrollers that didn't crash took 16 and 31 seconds respectively to accomplish the same work. It's a big difference. It is also interesting to see that the Raspberry Pi 4 running CPython 3.9 was slow, but still orders of magnitude faster than the microcontrollers, and much closer to the laptop speed. Hopefully this makes it clear why the Raspberry Pi is considered to be a computer in spite of its small form factor! Finally, we can compare the two laptop runs, one with MicroPython and the other with CPython. Here CPython performed better, which may suggest that there is some space for optimization in the MicroPython implementation. Below you can see the microcontroller results in chart form: The chart results are interesting as well. For this application the Pico 2W completed the task in half the time compared to the ESP32-S3, which is somewhat surprising. The ESP32 microcontroller has a sort of "high-end" fame (among microcontrollers, of course), so I expected it would be the faster one, or at least to run at a comparable level to the Pico 2W. fibo2.py The recursion in fibo.py is really convenient, but it is also fairly expensive in terms of stack usage. To be able to get measurements on the microcontrollers that failed to run my first script I've decided to create a non-recursive version of it as well. Below you can see fibo2.py: import time if hasattr(time, 'ticks_us'): def t(): return time.ticks_us() / 1000000 else: def t(): return time.time() def fibo(n): if n <= 1: return n else: fibo_n_1 = 1 fibo_n_2 = 0 fibo_n = 1 for i in range(n - 1): fibo_n = fibo_n_1 + fibo_n_2 fibo_n_2 = fibo_n_1 fibo_n_1 = fibo_n return fibo_n fibo(50000) # warm up s = t() fibo(50000) e = t() print(int((e - s) * 1000) / 1000) In fibo.py I calculated the 30th Fibonacci number, because that forced the script to run for a decent amount of time. After removing the recursion, I've found that the 30th Fibonacci was calculated too quickly, even on the slowest microcontroller. To force the script to run longer I had to adjust the number, and believe it or not, I had to raise the number up to the 50,000th Fibonacci to get more decent running times. If you thought recursion is free or nearly free, then think again. Here are the results of running fibo2.py: Device Interpreter Time (seconds) Framework Laptop MicroPython 1.25.0 0.048 ESP32-S3 MicroPython 1.25.0 9.966 Raspberry Pi Pico 2W MicroPython 1.25.0 24.135 Raspberry Pi Pico W MicroPython 1.25.0 31.295 ESP8266 MicroPython 1.25.0 38.348 Framework Laptop CPython 3.13 0.018 Raspberry Pi 4 Model B CPython 3.9 0.079 The chart with the microcontroller runs is below: These results more or less match what I would have initially guessed, with the ESP32-S3 now being faster than the Pico 2W, and the ESP8266 at the tail end. But considering the results of the recursive script, the complete reversal between the ESP32-S3 and the Pico 2W is definitely interesting. bubble.py The final test script creates an array of 2000 random numbers and sorts them with the bubble sort algorithm. Here is the code for bubble.py: import random import time if hasattr(time, 'ticks_us'): def t(): return time.ticks_us() / 1000000 else: def t(): return time.time() def bubble_sort(arr): n = len(arr) for i in range(n): for j in range(0, n-i-1): if arr[j] > arr[j+1]: arr[j], arr[j+1] = arr[j+1], arr[j] return arr random.seed() arr = [random.getrandbits(10) for _ in range(100)] bubble_sort(arr) # warm up arr = [random.getrandbits(10) for _ in range(2000)] s = t() bubble_sort(arr) e = t() print(int((e - s) * 1000) / 1000) Here are the results for this script: Device Interpreter Time (seconds) Framework Laptop MicroPython 1.25.0 0.168 Raspberry Pi Pico 2W MicroPython 1.25.0 15.698 ESP32-S3 MicroPython 1.25.0 19.059 Raspberry Pi Pico W MicroPython 1.25.0 27.846 ESP8266 MicroPython 1.25.0 78.183 Framework Laptop CPython 3.13 0.106 Raspberry Pi 4 Model B CPython 3.9 0.766 And here is the microcontroller chart: This test shows the ESP8266 struggling more than before. This is a really slow device, so this isn't a total surprise, but for some reason this particular script was harder than the others. On the higher end side, this time the Pico 2W and the ESP32-S3 are closer in performance, with the Pico 2W getting a small edge. Conclusion I guess the most important conclusion to make, and one I didn't need the benchmarks to know, is that microcontrollers are really really slow if you compare them against computers. But there is really no comparison to be made, the use cases are completely different and I find microcontrollers unbelievably useful in spite of the slow speed they run code at. Also it is important to mention again that there are no absolute benchmarks. Based on the results I obtained from my test scripts it is impossible to say if the Pico 2W is faster than the ESP32-S3 or if it is the other way around, because it really depends on the script. In the same way I cannot say how slow the ESP8266 is with respect to the others, because it can certainly handle some scripts better than others. If you are looking for a recommendation, I would say that both the Pico 2W and any of the ESP32 variants are fine machines that will work well for any application that needs microcontrollers. And of course if you are going to build something at scale you should definitely find the cheapest device that can run your code, so you shouldn't really discard the ESP8266 or the older Pico W, because maybe they are sufficient. The project that introduced me to microcontrollers was a custom thermostat that connected to the smart heating controller in my home. I built it on the ESP8266 and it was a fantastic hack that helped keep my house warm, but not too warm. It was really amazing that I was able to do this with a total cost of materials for each thermostat that didn't even reach €10. If you end up building something with these devices, keep in mind that with Microdot you can add a Flask or FastAPI style web back end and host it inside the device itself as part of your project!