Okay, now folks can call me crazy. I just about rewrote the v0.1 from scratch solely to get better performance.
The python version I wrote isn't terribly great when it comes to CPU. A single on-board PWM channel was enough to run the CPU at full load if I updated frequently enough. By updating less frequently, and also by replacing the scheduler library with a custom loop, I was able to get the CPU use down to about 14% for that single channel. But what about when using the Adafruit PWM board, which has 16 channels on it? Those extra channels cost CPU time, as does the communication with the board. Maybe not 16x, but it would still rise noticeably. I just wasn't really happy with where this was going. That 14% didn't include the extra 5-9% the GPIO daemon was using in the background for things that didn't benefit the project.
So when I noticed that Swift works on these boards, I figured I'd do some prototyping, just in case. There's a couple good things about Swift in this space that would make it easier to do this sort of time-based programming, I'm familiar with it, and bootstrapping projects with it is faster than with C/C++ (which was my next choice).
The good news is that the prototyping was very promising. That single channel test was looking really good, and the numbers for 24 updates per second looked like:
- Python: 28% CPU
- Python (Optimized): 14% CPU
- Swift: 1.3% CPU
Because of the huge improvement, I decided to do some real stress testing. Up the rate to 100 updates per second, and look at different channel counts:
- On-Board (1 Ch): 4.9%
- On-Board (2 Ch): 6.8%
- Adafruit PWM (1 Ch): 7.5%
- Adafruit PWM (2 Ch): 12%
- Adafruit PWM (4 Ch): 19%
- Adafruit PWM (16 Ch): 26%
The end result is that I can update 16 channels, 4 times as frequently as before, and still use a little less CPU to do it. These numbers in general are much closer to what I was hoping to be able to achieve. I can probably shrink the gap between the On-Board and the Adafruit PWM a bit, since the communication could be made more efficient, but that would need a bit of work. For now, since these sort of bursts of rapid updates should be rare, this is a sort of worst-case situation, and I'm okay with 16 channels needing that much CPU briefly. At least for now.
But it does mean I've spent a couple days this week rewriting everything in Swift, and I'm not quite done yet before I'm back at parity with v0.1. What's left is fairly small, routine stuff though.