/**
* 原文出处:
* 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 虚拟机。资源少了,反而运行时间小了。
第五种方案, profiling
pip 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()
本文介绍了两种Python性能测试和优化的方法,包括利用timeit模块进行分段测试以及通过命令行调用cProfile模块进行详细分析。通过对代码的不同部分进行测试,可以有效地找出并优化性能瓶颈。
1862

被折叠的 条评论
为什么被折叠?



