/**
* 原文出处:
* https://toucantoco.com/en/tech-blog/tech/python-performance-optimization
* 原文作者: Sylvain Josserand
* 原文发布: 2017-1-16
*/
第一个方案: 利用timeit 模块
#! /usr/bin/python
# -*- coding: utf-8 -*-
import random
import timeit
def wirte_sorted_letters(nb_letters=10**7):
"""
首先创建一个长度为10的7次方的随机字符串;
然后对字符串排序;
然后写入文件。
"""
random_string = ''
for i in range(nb_letters):
random_string += random.choice('abcdefghijklmnopqrstuvwxyz')
sorted_string = sorted(random_string)
with open("Sorted_text.txt", "w") as sorted_text:
for character in sorted_string:
sorted_text.write(character)
return True
def main():
sec1 = timeit.timeit(stmt=wirte_sorted_letters, number=1)
print(sec1)
if __name__ == '__main__':
main()
在我的机器上,timeit 计算出来的时间是14秒多点。
第二种方案:命令行调用cProfile模块。
然后去掉代码用引用 timeit 的部分。并且删除掉生成的文件。
在 cmd 中执行:
python -m cProfile -s tottime main.py
62309408 function calls (62309365 primitive calls) in 20.256 seconds Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function) 1 6.599 6.599 20.231 20.231 main.py:8(wirte_sorted_letters) 10000000 4.216 0.000 5.841 0.000 random.py:222(_randbelow) 10000000 3.320 0.000 9.691 0.000 random.py:252(choice) 10000000 2.189 0.000 2.189 0.000 {method 'write' of '_io.TextIOWrapper' objects} 1 1.753 1.753 1.753 1.753 {built-in method builtins.sorted} 12307695 1.180 0.000 1.180 0.000 {method 'getrandbits' of '_random.Random' objects} 10000012 0.530 0.000 0.530 0.000 {built-in method builtins.len} 10000000 0.445 0.000 0.445 0.000 {method 'bit_length' of 'int' objects} 1 0.021 0.021 20.253 20.253 main.py:26(main) 30 0.001 0.000 0.001 0.000 {built-in method nt.stat} 1 0.001 0.001 0.001 0.001 {built-in method _imp.create_dynamic} 1 0.001 0.001 0.001 0.001 {built-in method io.open} 23 0.000 0.000 0.001 0.000 <frozen importlib._bootstrap_external>:1233(find_spec) 3 0.000 0.000 0.000 0.000 {built-in method marshal.loads} 5 0.000 0.000 0.000 0.000 {built-in method _imp.create_builtin} 3 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap_external>:830(get_data) 8 0.000 0.000 0.000 0.000 {built-in method winreg.OpenKey} 3 0.000 0.000 0.000 0.000 {method 'read' of '_io.FileIO' objects} 112 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap_external>:57(_path_join) 112 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap_external>:59(<listcomp>) 1 0.000 0.000 0.003 0.003 random.py:38(<module>) 4 0.000 0.000 0.002 0.000 <frozen importlib._bootstrap_external>:1117(_get_spec) 9 0.000 0.000 0.002 0.000 <frozen importlib._bootstrap>:861(_find_spec) 230 0.000 0.000 0.000 0.000 {method 'rstrip' of 'str' objects}
解读: 用cProfile模块 (-m cProfile)把全部的call分类,并按照 total time usage 排序( -s tottime) 。所以我们看到执行了1000万次random._randbelow 累计用时 5.8秒;
1000万次random.choice,, 累计用时9.6秒;
1000万次写文件,累计用时2.1秒;
对1000万长度的字符串排序,用时1.7秒。
第三种方案:程序中调用cProfile模块修改main()成:
def main(): cp = cProfile.Profile() cp.enable() wirte_sorted_letters() cp.disable() cp.print_stats()
并且删除以前生成的文件。然后运行:62306502 function calls in 20.279 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.000 0.000 _bootlocale.py:11(getpreferredencoding) 1 6.683 6.683 20.279 20.279 main.py:9(wirte_sorted_letters) 10000000 4.174 0.000 5.815 0.000 random.py:222(_randbelow) 10000000 3.375 0.000 9.745 0.000 random.py:252(choice) 1 0.000 0.000 0.000 0.000 {built-in method _locale._getdefaultlocale} 10000000 0.555 0.000 0.555 0.000 {built-in method builtins.len} 1 1.747 1.747 1.747 1.747 {built-in method builtins.sorted} 1 0.000 0.000 0.000 0.000 {built-in method io.open} 10000000 0.446 0.000 0.446 0.000 {method 'bit_length' of 'int' objects} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 12306496 1.194 0.000 1.194 0.000 {method 'getrandbits' of '_random.Random' objects} 10000000 2.103 0.000 2.103 0.000 {method 'write' of '_io.TextIOWrapper' objects}
时间结果没有什么不同,但是输出部分对应调用做了一些整理,更清楚了一些。
第四种方案: 使用line profile
pip install line_profiler
然后原代码段中函数的前面加一朵花
#! /usr/bin/python # -*- coding: utf-8 -*- import random @profile def wirte_sorted_letters(nb_letters=10**7): """ 首先创建一个长度为10的7次方的随机字符串; 然后对字符串排序; 然后写入文件。 """ random_string = '' for i in range(nb_letters): random_string += random.choice('abcdefghijklmnopqrstuvwxyz') sorted_string = sorted(random_string) with open("Sorted_text.txt", "w") as sorted_text: for character in sorted_string: sorted_text.write(character) return True def main(): wirte_sorted_letters() if __name__ == '__main__': main()
root@Local:~# kernprof -l -v ~/codes/main.py Wrote profile results to main.py.lprof Timer unit: 1e-06 s Total time: 18.0735 s File: /root/codes/main.py Function: wirte_sorted_letters at line 8 Line # Hits Time Per Hit % Time Line Contents ============================================================== 8 @profile 9 def wirte_sorted_letters(nb_letters=10**7): 10 """ 11 首先创建一个长度为10的7次方的随机字符串; 12 然后对字符串排序; 13 然后写入文件。 14 """ 15 1 1 1.0 0.0 random_string = '' 16 10000001 2747395 0.3 15.2 for i in range(nb_letters): 17 10000000 7636799 0.8 42.3 random_string += random.choice('abcdefghijklmnopqrstuvwxyz') 18 1 1351775 1351775.0 7.5 sorted_string = sorted(random_string) 19 20 1 732 732.0 0.0 with open("Sorted_text.txt", "w") as sorted_text: 21 10000001 2577197 0.3 14.3 for character in sorted_string: 22 10000000 3759562 0.4 20.8 sorted_text.write(character) 23 24 1 2 2.0 0.0 return True
还蛮有意思。。 我换到了Debian 虚拟机。资源少了,反而运行时间小了。
第五种方案, profilingpip install profiling
好吧,移除掉函数前面的那朵花。
不是很看得懂。。。但是运行时间就长了很多。
做几个原作者推荐的优化:
1: 利用numpy 加快速度。
#! /usr/bin/python3 # -*- coding: utf-8 -*- import numpy as np import cProfile def wirte_sorted_letters(nb_letters=10**7): """ 首先创建一个长度为10的7次方的随机字符串; 然后对字符串排序; 然后写入文件。 """ letters = tuple('abcdefghijklmnopqrstuvwxyz') random_letters = np.random.choice(letters, nb_letters) random_letters.sort() sorted_string = ''.join(i for i in random_letters) with open("Sorted_text.txt", "w") as sorted_text: for character in sorted_string: sorted_text.write(character) return True def main(): cp = cProfile.Profile() cp.enable() wirte_sorted_letters() cp.disable() cp.print_stats() if __name__ == '__main__': main()
运行结果:20000012 function calls in 6.947 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.000 0.000 _bootlocale.py:11(getpreferredencoding) 1 0.000 0.000 0.000 0.000 _methods.py:34(_prod) 1 0.000 0.000 0.000 0.000 fromnumeric.py:2388(prod) 10000001 2.342 0.000 2.342 0.000 main.py:20(<genexpr>) 1 0.925 0.925 6.947 6.947 main.py:9(wirte_sorted_letters) 1 0.000 0.000 0.000 0.000 {built-in method _locale._getdefaultlocale} 1 0.000 0.000 0.000 0.000 {built-in method io.open} 1 0.098 0.098 0.098 0.098 {method 'choice' of 'mtrand.RandomState' objects} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 1 1.001 1.001 3.343 3.343 {method 'join' of 'str' objects} 1 0.000 0.000 0.000 0.000 {method 'reduce' of 'numpy.ufunc' objects} 1 0.461 0.461 0.461 0.461 {method 'sort' of 'numpy.ndarray' objects} 10000000 2.120 0.000 2.120 0.000 {method 'write' of '_io.TextIOWrapper' objects}
对比一下方案三, 从20秒下降到6秒。。。。
然后把写文件部分改成
with open("Sorted_text.txt", "w") as sorted_text: sorted_text.write(sorted_string)
10000013 function calls in 3.918 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.000 0.000 _bootlocale.py:11(getpreferredencoding) 1 0.000 0.000 0.000 0.000 _methods.py:34(_prod) 1 0.000 0.000 0.000 0.000 fromnumeric.py:2388(prod) 10000001 2.340 0.000 2.340 0.000 main.py:20(<genexpr>) 1 0.003 0.003 3.918 3.918 main.py:9(wirte_sorted_letters) 1 0.000 0.000 0.000 0.000 {built-in method _locale._getdefaultlocale} 1 0.001 0.001 0.001 0.001 {built-in method io.open} 1 0.095 0.095 0.096 0.096 {method 'choice' of 'mtrand.RandomState' objects} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 1 0.997 0.997 3.337 3.337 {method 'join' of 'str' objects} 1 0.000 0.000 0.000 0.000 {method 'reduce' of 'numpy.ufunc' objects} 1 0.457 0.457 0.457 0.457 {method 'sort' of 'numpy.ndarray' objects} 1 0.025 0.025 0.025 0.025 {method 'write' of '_io.TextIOWrapper' objects}
好吧,这个明显是坑嘛。
去掉cProfile, 最后程序是
#! /usr/bin/python3 # -*- coding: utf-8 -*- import numpy as np def write_sorted_letters(nb_letters=10**7): """ 首先创建一个长度为10的7次方的随机字符串; 然后对字符串排序; 然后写入文件。 """ letters = tuple('abcdefghijklmnopqrstuvwxyz') random_letters = np.random.choice(letters, nb_letters) random_letters.sort() sorted_string = ''.join(i for i in random_letters) with open("Sorted_text.txt", "w") as sorted_text: sorted_text.write(sorted_string) return True def main(): wirte_sorted_letters() if __name__ == '__main__': main()