When I was first starting out as an engineer, I was tasked with a job that sounded easy but quickly became a challenge for me.
I was working with a senior engineer to develop a embedded controller for a piece of proprietary hardware and our system requirements called for frequent polynomial fits based on raw sensor data, at runtime, in the firmware.
I know what you’re thinking. This sounds like a pretty easy task, right?
Now, in most software contexts, it’s safe to assume that there will be a prewritten software library that can be called by your application to perform a “simple” least-squares polynomial fit. If you’re writing your application for a PC, you probably won’t have any problems using Google to discover the basis for a good solution (Python has a great polyfit routine built into numpy). Actually, thanks to Github, you’ll can find pretty good solutions in nearly. any. language. pretty quickly.
Unfortunately, back in the year 2000, it was frustratingly difficult to find a starting point, let alone a working polynomial fit routine. To make matters worse, our target platform was an 8-bit 8051 microcontroller that was severely underpowered for the complete application.
I spent the better part of a month struggling to get solutions from the
Numerical Recipes book to work in addition to fruitless searches in libraries and bookstores.
When I finally found an algorithm that worked with my C-compiler, I was elated! The algorithm worked consistently but had an unfortunate side effect. It required four times longer to complete than my allotted execution time.
I wasn’t worried though. Having a working piece of code is the first step in succeeding with software. Incremental refinement of a viable solution is what makes software development fun.
The hard part is to first identify a process that works. Refining that process to achieve a targeted result with the known constraints requires effort but often isn’t nearly as difficult as identifying a workable solution first.
Often, we engineers get so caught up in trying to solve a problem “efficiently” that we immediately discount potentially great solutions that meet or exceed the requirements and are inherently more maintainable. This is almost always a mistake.
If you are choosing to implement something in assembly because its resultant executables are fast and you think you need a fast solution, that’s fine… but until you see how slow the
[insert high level, more maintainable choice here] Python version is, how do you know that you really need to spend the extra development time to write and debug assembly code?
I believe it is always smarter to start with a “simple”, “workable” solution first and iterate it to meet all of the requirements. Optimization should always be “Step Two.”