# Baseline version (Inefficient way) # Calculating the power of numbers # Without using List Comprehension def test_01_v0(numbers): output = [] for n in numbers: output.append(n ** 2.5) return output
# Improved version # (Using List Comprehension) def test_01_v1(numbers): output = [n ** 2.5 for n in numbers] return output
结果如下:
# Summary Of Test Results Baseline: 32.158 ns per loop Improved: 16.040 ns per loop % Improvement: 50.1 % Speedup: 2.00x
可以看到使用列表推导式可以得到2倍速的提高
2、在外部计算长度
如果需要依靠列表的长度进行迭代,请在for循环之外进行计算。
# Baseline version (Inefficient way) # (Length calculation inside for loop) def test_02_v0(numbers): output_list = [] for i in range(len(numbers)): output_list.append(i * 2) return output_list
# Improved version # (Length calculation outside for loop) def test_02_v1(numbers): my_list_length = len(numbers) output_list = [] for i in range(my_list_length): output_list.append(i * 2) return output_list
通过将列表长度计算移出for循环,加速1.6倍,这个方法可能很少有人知道吧。
# Summary Of Test Results Baseline: 112.135 ns per loop Improved: 68.304 ns per loop % Improvement: 39.1 % Speedup: 1.64x
3、使用Set
在使用for循环进行比较的情况下使用set。
# Use for loops for nested lookups def test_03_v0(list_1, list_2): # Baseline version (Inefficient way) # (nested lookups using for loop) common_items = [] for item in list_1: if item in list_2: common_items.append(item) return common_items
# Summary Of Test Results Baseline: 9047.078 ns per loop Improved: 18.161 ns per loop % Improvement: 99.8 % Speedup: 498.17x
4、跳过不相关的迭代
避免冗余计算,即跳过不相关的迭代。
# Example of inefficient code used to find # the first even square in a list of numbers def function_do_something(numbers): for n in numbers: square = n * n if square % 2 == 0: return square
return None # No even square found
# Example of improved code that # finds result without redundant computations def function_do_something_v1(numbers): even_numbers = [i for n in numbers if n%2==0] for n in even_numbers: square = n * n return square
return None # No even square found
这个方法要在设计for循环内容的时候进行代码设计,具体能提升多少可能根据实际情况不同:
# Summary Of Test Results Baseline: 16.912 ns per loop Improved: 8.697 ns per loop % Improvement: 48.6 % Speedup: 1.94x
5、代码合并
在某些情况下,直接将简单函数的代码合并到循环中可以提高代码的紧凑性和执行速度。
# Example of inefficient code # Loop that calls the is_prime function n times. def is_prime(n): if n <= 1: return False for i in range(2, int(n**0.5) + 1): if n % i == 0: return False
return True
def test_05_v0(n): # Baseline version (Inefficient way) # (calls the is_prime function n times) count = 0 for i in range(2, n + 1): if is_prime(i): count += 1 return count
def test_05_v1(n): # Improved version # (inlines the logic of the is_prime function) count = 0 for i in range(2, n + 1): if i <= 1: continue for j in range(2, int(i**0.5) + 1): if i % j == 0: break else: count += 1 return count
这样也可以提高1.3倍
# Summary Of Test Results Baseline: 1271.188 ns per loop Improved: 939.603 ns per loop % Improvement: 26.1 % Speedup: 1.35x
def test_07_v0(n): # Example of inefficient code # Repetitive calculation within nested loop result = 0 for i in range(n): for j in range(n): result += i * j return result
def test_07_v1(n): # Example of improved code # Utilize precomputed values to help speedup pv = [[i * j for j in range(n)] for i in range(n)] result = 0 for i in range(n): result += sum(pv[i][:i+1]) return result
结果如下
# Summary Of Test Results Baseline: 139.146 ns per loop Improved: 92.325 ns per loop % Improvement: 33.6 % Speedup: 1.51x
def test_08_v0(n): # Baseline version (Inefficient way) # (Inefficiently calculates the nth Fibonacci # number using a list) if n <= 1: return n f_list = [0, 1] for i in range(2, n + 1): f_list.append(f_list[i - 1] + f_list[i - 2]) return f_list[n]
def test_08_v1(n): # Improved version # (Efficiently calculates the nth Fibonacci # number using a generator) a, b = 0, 1 for _ in range(n): yield a a, b = b, a + b
可以看到提升很明显:
# Summary Of Test Results Baseline: 0.083 ns per loop Improved: 0.004 ns per loop % Improvement: 95.5 % Speedup: 22.06x
def some_function_X(x): # This would normally be a function containing application logic # which required it to be made into a separate function # (for the purpose of this test, just calculate and return the square) return x**2
def test_09_v0(numbers): # Baseline version (Inefficient way) output = [] for i in numbers: output.append(some_function_X(i))
def test_11_v0(n): # Baseline version # (Inefficient way of summing numbers in a range) output = 0 for i in range(0, n): output = output + i
return output
def test_11_v1(n): # Improved version # (# Efficient way of summing numbers in a range) output = np.sum(np.arange(n)) return output
向量化一般用于机器学习的数据处理库numpy和pandas
# Summary Of Test Results Baseline: 32.936 ns per loop Improved: 1.171 ns per loop % Improvement: 96.4 % Speedup: 28.13x
11、避免创建中间列表
使用filterfalse可以避免创建中间列表。它有助于使用更少的内存。
def test_12_v0(numbers): # Baseline version (Inefficient way) filtered_data = [] for i in numbers: filtered_data.extend(list( filter(lambda x: x % 5 == 0, range(1, i**2))))
return filtered_data
使用Python的内置itertools的filterfalse函数实现相同功能的改进版本。
from itertools import filterfalse
def test_12_v1(numbers): # Improved version # (using filterfalse)
filtered_data = [] for i in numbers: filtered_data.extend(list( filterfalse(lambda x: x % 5 != 0, range(1, i**2))))
# Summary Of Test Results Baseline: 333167.790 ns per loop Improved: 2541.850 ns per loop % Improvement: 99.2 % Speedup: 131.07x
12、高效连接字符串
任何使用+操作符的字符串连接操作都会很慢,并且会消耗更多内存。使用join代替。
def test_13_v0(l_strings): # Baseline version (Inefficient way) # (concatenation using the += operator) output = "" for a_str in l_strings: output += a_str
return output
def test_13_v1(numbers): # Improved version # (using join) output_list = [] for a_str in l_strings: output_list.append(a_str)
def generate_fake_names(count : int=10000): # Helper function used to generate a # large-ish list of names fake = Faker() output_list = [] for _ in range(count): output_list.append(fake.name())
return output_list
l_strings = generate_fake_names(count=50000)
结果如下:
# Summary Of Test Results Baseline: 32.423 ns per loop Improved: 21.051 ns per loop % Improvement: 35.1 % Speedup: 1.54x