C, and the Power of Threads

Published 11/02/2021

My past two blog posts involve me describing the development of my latest project and first venture in C, an interval timer. I created an implementation using one large for loop, with a sub loop and then all the timing, sound, output inside that loop. This then led to a friend of mine writing a far better implementation in Rust using threads. My original project had a time drift of around 15 seconds over 30 minutes, whereas this new Rust one had a time drift of 0.23 seconds over 30 minutes. You can read more about the details of this in my previous blog post.

Therefore, I set about optimising and improving my version. The first optimisation were minor things - specifying unsigned int, using register for my for loop variables, etc. This shaved off a few milliseconds, but nowhere near enough. The next stage was therefore multi-threading the program, which I decided to use pthreads to do. pthreads standing for POSIX threads of course, since all the cool kids write POSIX-compliant code.

It took some experimentation but eventually I setup 3 threads: Timekeeper, Announcer, and Megaphone, for time, output, and dings respectively. The timekeeper script simply ran through a for loop of intervals and time, as it had in the original version, whereas the announcer processed the time to ouput using a for(;;) to constantly check for a change in the seconds remaining. If a change had occured, it parsed a figlet command and outputted it. The megaphone thread worked similarly, tracking the interval count and playing a ding whenever it changed. I had some initial problems with the announcer thread where it wouldn't output each change only some, which led me to leave the project alone for a day or so in frustration. The next day, I changed a few things which shouldn't have impacted it and it suddenly started working properly. I'm not sure why or how, but I'm not complaining.

This concluded with a time drift over 30 minutes of 0.21 - beating my friend's version by 0.02 seconds. Over ten seconds, the drift was 0.003s. By the time I pushed these changes, my friend had posted a new version using some Rust crate to reduce it to microseconds of drift. I was happy enough with my new version - it was a 98.6% time drift decrease from the non-threaded version. And anyways, the average human reaction time is 0.19, so you would barely even have time to theoretically react to the result. Basically, the time drift was down to, for most intents and purposes, a negligible amount.

Either way, the healthy bit of competition was a great motivator for me to learn about multi-threading and how to optimise C code, so despite the fact I concede defeat to the other project, I think we both enjoyed the opportunity to learn some new programming techniques.