Make it work first, then make it fast. This is one common principle many professional programmers go by. At first, you may write your code using whichever approach seems the most intuitive to save development time on the draft. After you got a working implementation, you may want to optimize it by carefully choosing which techniques data structures work best in your specific case.
In this article, we’ll explore five language-agnostic methods you can use to improve your code runtime. The following concepts are generic and can be applied to any programming language.
Loop-invariant extraction
Consider the following Python code that checks a list of strings against a regular expression to find all the matches:
import regex as re# Get some strings as input
strings = get_strings()
# List to store all the matches
matches = []
# Iterate over the input strings
for string in strings:
# Compile the regex
rex = re.compile(r'[a-z]+')
# Check the string against the regex
matches = rex.findall(string)
# Finally, append the matches to the list
matches.extend(matches)
Loops repeatedly apply a set of instructions to a varying input. With this in mind, can you spot any operation that doesn’t change in the code above?
The statement `rex = re.compile(r’[a-z]+’)
` operates on a constant input: the regex string. For every iteration of the loop, this statement does exactly the same thing, independently from the loop’s input. If we were to extract this invariant statement and execute it once before the loop, the code would still have the same overall behavior while saving some CPU cycles.
import regex as re# Get some strings as input
strings = get_strings()
# List to store all the matches
matches = []
# Compile the regex only once before the loop
rex = re.compile(r'[a-z]+')
# Iterate over the input strings
for string in strings:
# Check the string against the regex
matches = rex.findall(string)
# Finally, append the matches to the list
matches.extend(matches)