I recently taught a course about improving code for performance. An obvious performance improvement is to not execute unnecessary operations. I lacked a good example when I gave the course, but here is one: Find value of the largest contiguous sub-array of fixed length in a huge array.
This is a toy example, of course, but it shows the idea quite well.
The Code is on GitHub.
Small Example
Input:
- array: [7, 9, 2, 0, 5, 2, 0, 1, 1, 2]
- sub-array length m = 3
Contiguous sub-arrays of the given length:
[7, 9, 2]
, value = 18[9, 2, 0]
, value = 11[2, 0, 5]
, value = 7[0, 5, 2]
, value = 7[5, 2, 0]
, value = 7[2, 0, 1]
, value = 3[0, 1, 1]
, value = 2[1, 1, 2]
, value = 4
Output: 18
Inefficient Solution
The following solution makes use of the slicing notation. It is short, easy to read and mostly pretty efficient:
from typing import List
def find_biggest_subarray_slice(array: List[int], m: int) -> int:
return max(sum(array[i : i + m]) for i in range(len(array) - m + 1))
Except that it has one flaw: It makes too many additions and accesses list elements way more often than necessary
Efficient Solution
from typing import List
def find_biggest_subarray_iterative(array: List[int], m: int) -> int:
value = sum(array[0:m])
largest_sub_array = value
for remove, add in zip(array, array[m:]):
value = value - remove + add
largest_sub_array = max(value, largest_sub_array)
return largest_sub_array
Comparison
The inefficient solution is in $\mathcal{O}((n - m) \cdot m)$, the efficient one is in $\mathcal{O}(n - m)$. So you will notice the difference clearly when you compare the execution times with big $m$.
The inefficient solution changes its execution time like as shown in the image below for increasing m and contant n = 100,000:
In contrast, the efficient solution looks like this:
Two things to notice:
- Worst-Case: For the inefficient solution, it is $m = n/2$. For the efficient solution, it is $m = 1$.
- Level: The efficient solution is always below 0.03s. The inefficient one is only for the best-case scenario ($n=m$) below that. And even then, the efficient solution is at 0.0008s whereas the inefficient one is at 0.001s.
- Speed-ups: If you look at $m = 60,000$, the more efficient solution gives a 1000× speedup!