{"id":17711,"date":"2025-01-16T22:31:50","date_gmt":"2025-01-17T03:31:50","guid":{"rendered":"https:\/\/scruss.com\/blog\/?p=17711"},"modified":"2025-01-21T23:17:00","modified_gmt":"2025-01-22T04:17:00","slug":"parallel-micropython-benchmarking","status":"publish","type":"post","link":"https:\/\/scruss.com\/blog\/2025\/01\/16\/parallel-micropython-benchmarking\/","title":{"rendered":"Parallel MicroPython Benchmarking"},"content":{"rendered":"\n<figure class=\"wp-block-video\"><video height=\"720\" style=\"aspect-ratio: 1280 \/ 720;\" width=\"1280\" controls loop muted src=\"https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2025\/01\/pico-mpy-benchmark.mp4\"><\/video><figcaption class=\"wp-element-caption\">On the left, a Raspberry Pi Pico 2W. On the right, a Raspberry Pi Pico. Each is connected to its own small OLED screen. When a button is pressed, both boards calculate and display the Mandelbrot set, along with its completion time. Needless to say, the Pico 2 W is quite a bit quicker.<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"992\" height=\"560\" src=\"https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2025\/01\/xPXL_20250117_003642928.jpg\" alt=\"two small OLED screens side by side on a breadboard. They're the type that are surplus from pulse oximeter machines, so the top 16 pixels are yellow, and the rest of the rows are blue.\n\nThe left screen displays: &quot;micropython 1.25.0.preview RP2350 150 MHz 128*64; 120&quot;, while the screen on the right shows &quot;micropython 1.24.1 RP2040 125 MHz 128*64; 120&quot;\" class=\"wp-image-17713\" srcset=\"https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2025\/01\/xPXL_20250117_003642928.jpg 992w, https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2025\/01\/xPXL_20250117_003642928-320x181.jpg 320w, https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2025\/01\/xPXL_20250117_003642928-160x90.jpg 160w, https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2025\/01\/xPXL_20250117_003642928-768x434.jpg 768w\" sizes=\"auto, (max-width: 992px) 100vw, 992px\" \/><figcaption class=\"wp-element-caption\">the before screens \u2026<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"936\" height=\"520\" src=\"https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2025\/01\/xPXL_20250117_003720739.jpg\" alt=\"The same two OLED screens, this time showing a complete Mandelbrot set and an elapsed time for each microcontroller. Pico 2 comes in at 10.3 seconds, original Pico at 19.8 seconds\" class=\"wp-image-17714\" srcset=\"https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2025\/01\/xPXL_20250117_003720739.jpg 936w, https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2025\/01\/xPXL_20250117_003720739-320x178.jpg 320w, https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2025\/01\/xPXL_20250117_003720739-160x89.jpg 160w, https:\/\/scruss.com\/wordpress\/wp-content\/uploads\/2025\/01\/xPXL_20250117_003720739-768x427.jpg 768w\" sizes=\"auto, (max-width: 936px) 100vw, 936px\" \/><figcaption class=\"wp-element-caption\">Pico 2 comes in at 10.3 seconds, original Pico at 19.8 seconds<\/figcaption><\/figure>\n\n\n\n<p>Stuff I found out setting this up:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>some old OLEDs, like these surplus pulse oximeter ones, don&#8217;t have pull-up resistors on their data lines. These I&#8217;ve carefully hidden behind the displays, but they&#8217;re there.<\/li>\n\n\n\n<li>Some MicroPython ports don&#8217;t include the complex type, so I had to lose the elegant <em>z\u2192z\u00b2+C<\/em> mapping to some ugly code.<\/li>\n\n\n\n<li>Some MicroPython ports don&#8217;t have <em>os.uname()<\/em>, but <em>sys.implementation<\/em> seems to cover most of the data I need.<\/li>\n\n\n\n<li>On some boards, machine.freq() is an integer value representing the CPU frequency. On others, it&#8217;s a list. Aargh.<\/li>\n<\/ul>\n\n\n\n<p><em>These displays came from the collection of the late Tom Luff, a Toronto maker who passed away late 2024 after a long illness. Tom had a huge component collection, and my way of remembering him is to show off his stuff being used.<\/em><\/p>\n\n\n\n<p>Source:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\n# benchmark Mandelbrot set (aka Brooks-Matelski set) on OLED\n# scruss, 2025-01\n# MicroPython\n# -*- coding: utf-8 -*-\n\nfrom machine import Pin, I2C, idle, reset, freq\n\n# from os import uname\nfrom sys import implementation\nfrom ssd1306 import SSD1306_I2C\nfrom time import ticks_ms, ticks_diff\n\n# %%% These are the only things you should edit %%%\nstartpin = 16  # pin for trigger configured with external pulldown\n# I2C connection for display\ni2c = machine.I2C(1, freq=400000, scl=19, sda=18, timeout=50000)\n# %%% Stop editing here - I mean it!!!1! %%%\n\n\n# maps value between istart..istop to range ostart..ostop\ndef valmap(value, istart, istop, ostart, ostop):\n    return ostart + (ostop - ostart) * (\n        (value - istart) \/ (istop - istart)\n    )\n\n\nWIDTH = 128\nHEIGHT = 64\nTEXTSIZE = 8  # 16x8 text chars\nmaxit = 120  # DO NOT CHANGE!\n# value of 120 gives roughly 10 second run time for Pico 2W\n\n# get some information about the board\n# thanks to projectgus for the sys.implementation tip\nif type(freq()) is int:\n    f_mhz = freq() \/\/ 1_000_000\nelse:\n    # STM32 has freq return a tuple\n    f_mhz = freq()&#x5B;0] \/\/ 1_000_000\nsys_id = (\n    implementation.name,\n    &quot;.&quot;.join(&#x5B;str(x) for x in implementation.version]).rstrip(\n        &quot;.&quot;\n    ),  # version\n    implementation._machine.split()&#x5B;-1],  # processor\n    &quot;%d MHz&quot; % (f_mhz),  # frequency\n    &quot;%d*%d; %d&quot; % (WIDTH, HEIGHT, maxit),  # run parameters\n)\n\np = Pin(startpin, Pin.IN)\n\n# displays I have are yellow\/blue, have no pull-up resistors\n#  and have a confusing I2C address on the silkscreen\noled = SSD1306_I2C(WIDTH, HEIGHT, i2c)\noled.contrast(31)\noled.fill(0)\n# display system info\nypos = (HEIGHT - TEXTSIZE * len(sys_id)) \/\/ 2\nfor s in sys_id:\n    ts = s&#x5B;: WIDTH \/\/ TEXTSIZE]\n    xpos = (WIDTH - TEXTSIZE * len(ts)) \/\/ 2\n    oled.text(ts, xpos, ypos)\n    ypos = ypos + TEXTSIZE\n\noled.show()\n\nwhile p.value() == 0:\n    # wait for button press\n    idle()\n\noled.fill(0)\noled.show()\nstart = ticks_ms()\n# NB: oled.pixel() is *slow*, so only refresh once per row\nfor y in range(HEIGHT):\n    # complex range reversed because display axes wrong way up\n    cc = valmap(float(y + 1), 1.0, float(HEIGHT), 1.2, -1.2)\n    for x in range(WIDTH):\n        cr = valmap(float(x + 1), 1.0, float(WIDTH), -2.8, 2.0)\n        # can&#039;t use complex type as small boards don&#039;t have it dammit)\n        zr = 0.0\n        zc = 0.0\n        for k in range(maxit):\n            t = zr\n            zr = zr * zr - zc * zc + cr\n            zc = 2 * t * zc + cc\n            if zr * zr + zc * zc &gt; 4.0:\n                oled.pixel(x, y, k % 2)  # set pixel if escaped\n                break\n    oled.show()\nelapsed = ticks_diff(ticks_ms(), start) \/ 1000\nelapsed_str = &quot;%.1f s&quot; % elapsed\n# oled.text(&quot; &quot; * len(elapsed_str), 0, HEIGHT - TEXTSIZE)\noled.rect(\n    0, HEIGHT - TEXTSIZE, TEXTSIZE * len(elapsed_str), TEXTSIZE, 0, True\n)\n\noled.text(elapsed_str, 0, HEIGHT - TEXTSIZE)\noled.show()\n\n# we&#039;re done, so clear screen and reset after the button is pressed\nwhile p.value() == 0:\n    idle()\noled.fill(0)\noled.show()\nreset()\n\n<\/pre><\/div>\n\n\n<p>(also here: <a href=\"https:\/\/gist.github.com\/scruss\/c85cfd98dd8c6884b2ea6cb91bb6e658\">benchmark Mandelbrot set (aka Brooks-Matelski set) on OLED &#8211; MicroPython<\/a>)<\/p>\n\n\n\n<p>I will add more tests as I get to wiring up the boards. I have so many (<em>too<\/em> many?) MicroPython boards!<\/p>\n\n\n\n<p>Results are here: <a href=\"https:\/\/scruss.com\/blog\/2025\/01\/21\/micropython-benchmarks\/\">MicroPython Benchmarks<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Stuff I found out setting this up: These displays came from the collection of the late Tom Luff, a Toronto maker who passed away late 2024 after a long illness. Tom had a huge component collection, and my way of remembering him is to show off his stuff being used. Source: (also here: benchmark Mandelbrot [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[7],"tags":[2721,2874,3094,3337],"class_list":["post-17711","post","type-post","status-publish","format-standard","hentry","category-computers-suck","tag-benchmark","tag-mandelbrot","tag-micropython","tag-oled"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/pQNZZ-4BF","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/posts\/17711","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/comments?post=17711"}],"version-history":[{"count":3,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/posts\/17711\/revisions"}],"predecessor-version":[{"id":17736,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/posts\/17711\/revisions\/17736"}],"wp:attachment":[{"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/media?parent=17711"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/categories?post=17711"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/scruss.com\/blog\/wp-json\/wp\/v2\/tags?post=17711"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}