The Need For Speed?

We wrote up a video about speeding up Arduino code, specifically by avoiding DigitalWrite. Now, the fact that DigitalWrite is slow as dirt is long known. Indeed, a quick search pulls up a Hackaday article from 2010 demonstrating that it’s fifty times slower than toggling the pin directly using the native pin registers, but this is still one of those facts that gets periodically rediscovered from generation to generation. How can this be new again?

First off, sometimes you just don’t need the speed. When you’re just blinking LEDs on a human timescale, the general-purpose Arduino functions are good enough. I’ve written loads of useful firmware that fits this description. When the timing requirements aren’t tight, slow as dirt can be fast enough.

But eventually you’ll want to build a project where the old slow-speed pin toggling just won’t cut it. Maybe it’s a large LED matrix, or maybe it’s a motor-control application where the loop time really matters. Or maybe it’s driving something like audio or video that just needs more bits per second. One way out is clever coding, maybe falling back to assembly language primitives, but I would claim that the right way is almost always to use the hardware peripherals that the chipmakers gave you.

For instance, in the end of the video linked above, the hacker wants to drive a large shift register string that’s lighting up an LED matrix. That’s exactly what SPI is for, and coming to this realization makes the project work with timing to spare, and in just a few lines of code. That is the way.

Which brings me to the double-edged sword that the Arduino’s abstraction creates. By abstracting away the chips’ hardware peripherals, it makes code more portable and certainly more accessible to beginners, who don’t want to learn about SPI and I2C and I2S and DMA just yet. But by hiding the inner workings of the chips in “user friendly” libraries, it blinds new users to the useful applications of these same hardware peripherals that clever chip-design engineers have poured their sweat and brains into making do just exactly what we need.

This isn’t really meant to be a rant against Arduino, though. Everyone has to start somewhere, and the abstractions are great for getting your feet wet. And because everything’s open source anyway, nothing stops you from digging deeper into the datasheet. You just have to know that you need to. And that’s why we write up videos like this every five years or so, to show the next crop of new hackers that there’s a lot to gain underneath the abstractions.

18 thoughts on “The Need For Speed?

  1. And because everything’s open source anyway, nothing stops you from digging deeper into the datasheet.

    I think one issue is realising that it is possible to dip below the level of what most tutorials cover, and then finding which files to look at and which resources will help you do that. It took me longer than I would have liked to learn I2C, because most tutorials would tell you all about the electrical properties of the bus, but not which bits of the datasheet were actually relevant and how to relate those to basic I2C program instructions. It feels to me like there’s a real gap of intermediate resources for people between “I’m a total newbie” and “I’m an expert”.

    1. it’s a steep mountain to climb to learn how to read a datasheet…that’s really what arduino saves you from.

      but it’s well worth it, and once you do it you realize you can learn anything that way.

      knowing how to read a datasheet is, imo, what separates a newbie from an intermediate person. you can’t be an expert if you’ve only ever answered a couple questions from datasheets but simply knowing you can do that puts you on the path to being an expert. the ‘trick’ is to keep looking after finding insufficient documentation. to imagine the next kind of reference material you will consult. for I2C, you can find tutorials, application notes, the formal specification, and specifications also embedded within a zillion product datasheets, example source code, real-life source code. if you’ve recognized that the document you have isn’t cutting it for you, there’s a bunch more out there. just keep going until you’ve answered your questions.

      the process itself definitely feels overwhelming at moments. i would say i’m good at it, but still there is that first moment when i am orienting to the project where i feel like i need to remember a million details before i’ve figured out how they relate to eachother. maybe the most important skill is just to accept that phase, to move forward without nailing down the details or understanding the big picture. it’s just an exercise in faith that you will eventually understand the big picture, and the details will still be there for you to come back to.

      1. that’s really what arduino saves you from.

        And that’s the biggest disservice that Arduino does to you. It’s not magic – the datasheet is literally there so you’d know what you’re doing, and it should be the first thing you check when you’re starting out. Heck, you should be reading the datasheet well before you’re even planning to do anything, so you’d know what you’re getting into.

        1. That’s not how onboarding works. You need something that works out of the box and simply. If anything needs to be added it’s a series of tutorials specifically on diving past the ready-to-go configuration.

    1. they didn’t write the original to be fast…they didn’t do any of the different contortions possible at the time to avoid performance penalties. they optimized for ease of use and safety over speed and code size. so far as i know they haven’t really updated it much since, as more modern C++ features have become more widely available.

      anyways as a side note, all that constexpr stuff doesn’t really work. it’s a fun meditation. it suckers people into it. everyone loves to dream about generic programming with the speed of C. but it’s very hard to actually conform to all of the requirements to get C++ to live up to its performance dream. every time i’ve looked at a piece of real-life code using C++, i have been impressed by how many neat optimizations succeeded and then how the result is still nonetheless much slower than it needs to be.

      in the arduino case, i think digitalWrite() does some cross-checking, to ensure the pin is in the right I/O mode etc. in principle, it’s possible to fold a lot of those things away…but in practice it is really quite challenging, and even if you succeed at the hard parts of it (tracking state at compile-time), there will always be boundary cases where the user inadvertently uses an idiom that makes it slow again.

      the underlying function is kind of a frustrating problem because once your program works, you definitely do not want that checking…but that first time you run it as a noob “why isn’t my LED blinking”, it is really very nice to have robust error handling. i would almost think something like “#define NDEBUG” (like to disable assert() tests) might have been a nice option. but arduino really leaves version 2.0 as an exercise in abandoning / circumventing arduino.

    2. With constexpr etc. available

      Let’s recall. Arduino 1.0 is from 2011. The language definition for C++11 was published in 2011. As all C++ standards, fully compliant compilers were only available after a few years. In other words, the interface for digitalWrite() is, for backward compatibility, forever moored in the past.

      But let’s suppose we want a new digitalWriteNG(). constexpr in modern C++ means “can be evaluated at compile time”. It does not mean “will be”, so although it’s typically the case, you’d want some (hopefully) automated test procedure to verify performance.

      And then there’s the matter of the semantics. If you want compile-time generation of a digitalWriteNG() with contexpr arguments to be reliable, you need to ensure that (1) the hardware is set up correctly, and (2) that it stays forever set up correctly. That would mean a whole raft of constexpr specifications for hardware setup, a singleton static initializer to ensure that setup happens before use, and a large amount of documentation about other packages that are incompatible with static hardware configuration. This is all possible, and even all worth doing, but it’s not simple.

    1. There are in fact a bunch of reasons that digitalWrite() is slow.
      Remember that the “fast” version is a single instruction, hard-coded with pin and port number, so “50 times as slow” is “only” 50 instruction cycles…
      Go ahead and try to write your own version, given these requirements:
      1) The pin being written and the value are both variables.
      2) the “pin” is a “board pin number” rather than a chip port/bit combination. Users should not have to understand “ports” and “bitmasks.”
      3) the digitalWrite should override any analogWrite done previously.
      4) function should would on at least ATmega8, ATmega168, ATmega328p, ATmega1280, and ATmega2560
      5) function should be atomic, even if the port is not in the AVR’s “fast IO” space.
      You can probably get faster than Arduino’s digitalWrite() (PJRC’s version for Teensy does), but you’re not going to get “close” to the single-cycle special purpose instruction.

  2. When you use delay() inside loop(), you don’t need to worry about speed !

    When you use delay() inside one of the first tutorial on blinking an Led, it should come as no surprise that newbies then use it as a do-it-all instruction inside loops.

  3. It’s hard to remember sometimes that there are a lot of people starting on Arduino now were literally in diapers in 2010, when “everybody” learned the speed cost of digitalWrite().

    I think the relevant xkcd is relevant: https://xkcd.com/1053/

    If it becomes too obvious to talk about it, nobody is going to learn about it without experimenting for themselves or doing a deep-dive in the source. Having learned that, and not seen anyone talking about it, can you blame a younger person for wanting to share that knowledge?

    The response should absolutely not be a gruff, derisive, “everybody knows that already”– we should celebrate the person who rediscovered and re-popularized this factoid for another day’s 10,000 “everybody”. (Which was my goal in sharing that video. I knew digitalWrite was slow, but I’d never seen anybody put it on an O-scope to demonstrate it so clearly for me and everyone else.)

    Online culture, particularly ‘hacker’ culture or programming/electronics generally, can be pretty toxic to newcomers. In person, in hackerspaces, work places, colleges, etc, mentorship exists– online, though? Not often. Far too many graybeards would rather puff out their chests and feel better about themselves by making the newbs feel worse. That’s not new (it goes back to , I’m sure) and it’s not going to change, but it sucks.

    In the comments of another post I realized that that is partly what might drive people to ChatGPT– sure, its answers are mostly BS, but half the time what you got on forums was BS anyway. At least the LLM won’t act like its’ better than you while giving you bad/useless/inapplicable advice.

  4. The gatekeeping used to be insane. I remember when someone asked a question, the “experts” used to reply “I’m not going to help you so you figure it out”. I do not miss that.

  5. I personally do not like the crust cut off my pb&j. I would rather have the option to remove it, if I so choose. 🤷 Arduino is great for what it is, making accessible what was once enjoyed and utilized by so few… as Dr Johnny Fever once said, “Speed kills, Del.” 😉

  6. The other thing to remember is that while “digitalWrite()” is “horribly slow” compared to what you could do with special cases in bare metal C or ASM, it’s SOOO much faster than earlier alternative “beginner friendly” systems like 8052-BASIC or the BASIC Stamp (or even the BASIC interpreters running on desktop systems.)
    Probably faster than the microconrtoller Pythons, too.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.