Don't Fall into the Trap of Over-Optimizing
Optimization is my favorite task. It usually means speeding up code, but sometimes improving memory usage or network bandwidth.
I love it because success is easy to measure. In most coding work, what makes code good is unclear. Optimization answers are straightforward: if you're trying to make something faster, you can measure it directly, along with the trade-offs in code size or complexity. No ambiguity, no trusting that someone else will understand your code later.
The famous adage "Premature optimization is the root of all evil" is well-known. But the full quote by Donald Knuth in 1974 is more nuanced:
"We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil."
Note the context: in 1974, compilers were less advanced, making small optimizations more complicated.
Compilers are now smart enough to generate correct code, as long as you write straightforward code. The more complex it is, the less likely the compiler will understand it correctly.
Instead of using old tricks, it's better to write simple and straightforward code.
Programmers naturally worry about resources, which can lead to trying to solve performance issues before they arise.
Don't optimize
The first rule of optimization is: don't optimize. Make your code simple and don't worry about speed. It'll be fast enough. If it's not, you can easily improve its performance later.
Simple first
Imagine you have simple, solid code that was written with average care. You notice it's running slowly, so you add monitoring to see where the bottleneck is. Surprise! You find that a small part of the code is using up half of the performance.
This is great news! If you can fix this slow code, you can double your overall performance.
Typically, when you first look at code's performance, you'll find obvious areas to improve. However, if you don't find any slow parts, that's unusual.
Here's a common rule: if you haven't optimized code before, you can make it 5-10 times faster with minimal effort. This might seem overly optimistic, but it's true. Unoptimized code often has many easy-to-fix issues.
Test and measure
If something is taking up half of your processor time, just switch to a simpler implementation and it will run 20 times faster.
However, let's assume we're starting with the first implementation, and it's not that easy to fix. Here's a 5-step process to optimize performance:
Step 1: Measure Time
Measure how much processor time is being spent and attribute it to specific functions or objects.
Step 2: Fix Bugs
It's common to find that performance issues are actually bugs. Look for mistakes in the logic or loop conditions.
Step 3: Measure Your Data
How many calls are made? How many options are there? Are there repeated values or zero weights? Most optimizations exploit data patterns.
Step 4: Plan and Prototype
Imagine what overall performance would look like with a perfect optimization. If it doesn't meet the target, identify other code to optimize. Prototype the optimization to see its impact.
Step 5: Optimize and Repeat
Optimize the code based on its complexity and memory access. Look for data patterns to exploit. Don't just dive in; have a plan and measure performance after each step. If the target isn't reached, go back to Step 1.
In summary, optimizing performance is not just about switching to a simpler implementation. It requires a structured approach to measure, attribute, and optimize code.
Conclusion
A better third lesson might be: "Don't worry about mistakes, because you can always fix them."
If you write a high-speed app in Python, and it's slow, you can still fix it. Convert the slow parts to Rust and leave the rest in Python. This can give you a 50x to 100x speed boost.
We often write the first version of something in Python and then convert it to Rust if it becomes slow. This way, we can try ideas quickly and then optimize the performance if needed.
Remember, if you make a mistake that needs a huge performance boost, you'll know about it early on. So, don't worry too much about it.
Just follow the two optimization lessons:
write simple and clear code
trust that solutions will appear for performance problems.