让你的Python提速30%!(上)
讨厌Python的人总是说,他们不想使用Python的原因之一是它的速度太慢。好吧,不管使用哪种编程语言,具体的程序是快还是慢,很大程度上取决于编写程序的开发人员以及他们编写优化的快速程序的技能和能力。 所以,让我们来证明一些人是错的,让我们看看如何提高Python程序的性能并使它们变得非常快! 时间和剖析在开始优化任何东西之前,我们首先需要找出代码的哪些部分实际上会减慢整个程序的速度。有时,程序的瓶颈可能很明显,但如果您不知道它在哪里,那么下面是您可以找到的选项: 注:这是我将用于演示目的的程序,它计算e的X次方(取自Python文档): # slow_program.py from decimal import * def exp(x): getcontext().prec += 2 i, lasts, s, fact, num = 0, 0, 1, 1, 1 while s != lasts: lasts = s i += 1 fact *= i num *= x s += num / fact getcontext().prec -= 2 return +s exp(Decimal(150)) exp(Decimal(400)) exp(Decimal(3000)) 最懒的“剖析”首先,最简单、最懒的解决方案-Unix time命令: ~ $ time python3.8 slow_program.py real 0m11,058s user 0m11,050s sys 0m0,008s 如果你只想给你的整个程序计时,这是可行的,但这通常是不够的… 最详细的剖析在光谱的另一端是cProfile,它会给你提供太多的信息: ~ $ python3.8 -m cProfile -s time slow_program.py 1297 function calls (1272 primitive calls) in 11.081 seconds Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function) 3 11.079 3.693 11.079 3.693 slow_program.py:4(exp) 1 0.000 0.000 0.002 0.002 {built-in method _imp.create_dynamic} 4/1 0.000 0.000 11.081 11.081 {built-in method builtins.exec} 6 0.000 0.000 0.000 0.000 {built-in method __new__ of type object at 0x9d12c0} 6 0.000 0.000 0.000 0.000 abc.py:132(__new__) 23 0.000 0.000 0.000 0.000 _weakrefset.py:36(__init__) 245 0.000 0.000 0.000 0.000 {built-in method builtins.getattr} 2 0.000 0.000 0.000 0.000 {built-in method marshal.loads} 10 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap_external>:1233(find_spec) 8/4 0.000 0.000 0.000 0.000 abc.py:196(__subclasscheck__) 15 0.000 0.000 0.000 0.000 {built-in method posix.stat} 6 0.000 0.000 0.000 0.000 {built-in method builtins.__build_class__} 1 0.000 0.000 0.000 0.000 __init__.py:357(namedtuple) 48 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap_external>:57(_path_join) 48 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap_external>:59(<listcomp>) 1 0.000 0.000 11.081 11.081 slow_program.py:1(<module>) ... 在这里,我们使用cProfile模块和time参数运行测试脚本,以便按内部时间(cumtime)对行进行排序。这给了我们很多信息,你可以看到上面的行大约是实际输出的10%。从这里,我们可以看出exp函数是罪魁祸首(惊喜,惊喜),现在我们可以得到更具体的时间和分析… 时间特定功能现在我们知道了应该将注意力放在哪里,我们可能希望对慢速函数计时,而不需要测量代码的其余部分。为此,我们可以使用简单的decorator: def timeit_wrapper(func): @wraps(func) def wrapper(*args, **kwargs): start = time.perf_counter() # Alternatively, you can use time.process_time() func_return_val = func(*args, **kwargs) end = time.perf_counter() print('{0:<10}.{1:<8} : {2:<8}'.format(func.__module__, func.__name__, end - start)) return func_return_val return wrapper 这个decorator随后可以应用于测试中的函数,如下所示: @timeit_wrapper def exp(x): ... print('{0:<10} {1:<8} {2:^8}'.format('module', 'function', 'time')) exp(Decimal(150)) exp(Decimal(400)) exp(Decimal(3000)) 这会给我们这样的输出: ~ $ python3.8 slow_program.py module function time __main__ .exp : 0.003267502994276583 __main__ .exp : 0.038535295985639095 __main__ .exp : 11.728486061969306 要考虑的一件事是我们实际(想要)测量的是什么样的时间。时间包提供Time.perf_计数器和Time.process_时间。这里的区别在于perf_计数器返回绝对值,其中包括Python程序进程未运行的时间,因此它可能会受到机器负载的影响。另一方面,process_time只返回用户时间(不包括系统时间),这只是进程的时间。 原文链接:https://towardsdatascience.com/making-python-programs-blazingly-fast-c1cd79bd1b32 (编辑:滁州站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |