【腾讯云】境外1核2G服务器低至2折,半价续费券限量免费领取!

搜搜吧

查看: 103|回复: 0

[资讯] 教你几招,Python性能提升30%!

[复制链接]
  • TA的每日心情
    奋斗
    3 天前
  • 签到天数: 684 天

    [LV.9]以坛为家II

    博士生

    1万

    主题

    1万

    帖子

    5万

    积分

    Rank: 8Rank: 8

    UID
    15343
    威望
    -561
    贡献
    8107
    在线时间
    361 小时
    注册时间
    2015-10-12
    发表于 2020-2-6 15:46:00 | 显示全部楼层 |阅读模式

    本文主要分享如何提升Python性能的几个使用方法!

    时序分析

    优化之前,首先要找到是哪部分代码拖慢了整个程序的运行。有时候程序的"瓶颈"不是很明显,如果找不到,以下是一些建议以供参考:

    注意:这是一个计算e的x次幂的演示程序(出自Python文档):

    • # slow_program.py
    • from deCIMAl import*
    • defexp(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))

    在GitHub上查看rawslow_program.py全部代码

    最省力的“性能分析”

    首先,最简单且最省力的解决方案是使用Unix的time命令:

    • ~ $ time python3.8  slow_program.py
    • real     0m11,058s
    • user     0m11,050s
    • sys      0m0,008s

    在GitHub上查看rawbase_time.shell全部代码

    如果只是给整个程序计时,它很有用,但还不足够……

    最详细的性能分析

    性能分析的另一方法是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 :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 :57(_path_join)
    •        48     0.000    0.000    0.000     0.000 :59()
    •         1     0.000    0.000   11.081    11.081 slow_program.py:1()
    • ...

    在GitHub上查看rawcprofile.shell全部代码

    这里用cProfile模块和time参数运行测试脚本,以便按内部时间(cumtime)对行进行排序。从中可以得到很多信息,以上所列结果约为实际输出的10%。由此可见,exp函数就是拖慢程序的“罪魁祸首”(太神奇啦!),现在看看更详尽的时序和性能分析......

    对特定函数计时

    已经知道拖慢程序运行的函数,下一步可使用简单的修饰器,专门对该函数计时,不测量其余代码。如下所示:

    • deftimeit_wrapper(func):
    •     @wraps(func)
    •     defwrapper(*args, **kwargs):
    •         start =  time.perf_counter()  # Alternatively, you  can use time.process_time()
    •         funcfunc_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

    在GitHub上查看rawtimeit_decorator.py全部代码

    该修饰器可以应用于功能测试,如下所示:

    • @timeit_wrapper
    • defexp(x):
    •     ...
    • print( {0:<10}{1:<8}{2:^8} .format( module ,  function ,  time ))
    • exp(Decimal(150))
    • exp(Decimal(400))
    • exp(Decimal(3000))

    在GitHub上查看rawtimeit_decorator_usage.py全部代码

    输出如下:

    • ~ $ python3.8  slow_program.py
    • module     function   time   
    • __main__  .exp       :0.003267502994276583
    • __main__  .exp       :0.038535295985639095
    • __main__  .exp       : 11.728486061969306

    在GitHub上查看rawrun_with_timeit_decorator.shell全部代码

    要考虑的一个问题是实际/想要测量的时间类型是什么。Time程序包提供了time.perf_counter和time.process_time。两者的区别是:perf_counter返回绝对值,其中包括Python程序进程未运行时的时间,因此可能会受计算机负载的影响;而process_time仅返回用户时间(不包括系统时间),这仅是程序的运行时间。

    加快程序运行速度

    5cc1e7d424765126fc5de382a17c9e21.jpg-wh_651x-s_3487584945.jpg

    图源:Unsplash

    这是全文有趣的部分,关于如何加快Python的程序运行速度。我并没有列出一些可以奇妙解决性能问题的小技巧或代码段,而是涉及一般性的构想和策略,它们能极大地提高性能,某些情况下甚至能将性能提高30%。

    使用内置数据类型

    显而易见,内置数据类型运行很快,尤其是与自定义类型(例如树或链表)相比。主要是因为内置程序是用C语言实现的,远超过用Python编码的运行速度。

    使用lru_cache缓存/记忆

    我已经在上一篇博文中讲过这块内容,但在此还是要用简单的示例说明:

    • import functools
    • import time
    • # caching up to 12  different results
    • @functools.lru_cache(maxsize=12)
    • defslow_func(x):
    •     time.sleep(2)  # Simulate long computation
    •     return x
    • slow_func(1)  # ... waiting for 2 sec before getting  result
    • slow_func(1)  # already cached - result returned  instantaneously!
    • slow_func(3)  # ... waiting for 2 sec before getting  result

    在GitHub上查看rawlru_cache.py全部代码

    以上函数使用time.sleep模拟大量运算。第一次使用参数1调用该函数时,返回结果需要2秒。再次调用时,结果已被缓存,因此会跳过函数主体并立即返回结果。更多内容请参见此处。

    使用局部变量

    这与在每个作用域中查找变量的速度有关。我用了“每个作用域”这个字眼,因为它不仅仅是“使用局部变量还是全局变量”的问题。实际上,即使在函数的局部变量(最快)、类级属性(如self.name-较慢)和全局变量(如导入的函数,time.time-最慢)之间,查找速度也有所不同。

    可以通过运行无用的任务来提高性能,如下所示:

    • #  Example #1
    • classFastClass:
    •     defdo_stuff(self):
    •         temp =self.value  # this speeds up lookup in loop
    •         for i inrange(10000):
    •             ...  # Do something with `temp` here
    • #  Example #2
    • import random
    • deffast_function():
    •     r = random.random
    •     for i inrange(10000):
    •         print(r())  # calling `r()` here, is faster than  global random.random()

    在GitHub上查看rawlocal_vars.py全部代码

    使用函数(Function)

    这怎么和假想的不同?理论上调用函数不是会将更多的东西放到堆栈上,加大返回结果的负担吗?但实际上,使用函数确实能加快运行速度,这与前一点有关。将整个代码放在一个文件中而非函数中,它是全局变量而非局部变量,运行速度就会慢得多。因此,可以将整个代码包裹在main函数中并通过一次调用来加速代码,如下所示:

    • defmain():
    •     ...  # All your previously global code
    • main()

    在GitHub上查看rawglobal_vars.py全部代码

    避免访问属性(Attribute)

    可能拖慢程序的一个原因是使用点运算符(.)访问对象属性。该运算符通过使用__getattribute__方法触发了字典查找,使代码产生额外负担。那么,如何避免或减少属性访问?

    • #  Slow:
    • import re
    • defslow_func():
    •     for i inrange(10000):
    •         re.findall(regex, line)  # Slow!
    • #  Fast:
    • from re import findall
    • deffast_func():
    •     for i inrange(10000):
    •         findall(regex, line)  # Faster!

    在GitHub上查看rawimports.py全部代码

    当心使用字符串

    在循环里使用格式符(%s)或.format()时,字符串操作可能会变得非常慢。有没有更好的选择?Raymond Hettinger在最近发布的推文中提到:唯一应该使用的是f-string(格式化字符串常量),它是最易读、最简洁且最快捷的方法。根据这篇推文,下面列出了可用的方法(由快到慢):

    • f {s}{t}   # Fast!
    • s +    + t
    •    .join((s, t))
    • %s %s % (s, t)
    • {} {} .format(s, t)
    • Template( $s $t ).substitute(ss=s, tt=t)  # Slow!

    在GitHub上查看rawstrings.py全部代码

    本质上,生成器并没有变得更快,因为它在设计上允许延迟计算以节省内存而非节约时间。然而节省的内存也可以加快程序实际运行速度。怎么做?如果有一个很大的数据集且不使用生成器(迭代器),那么数据可能会溢出CPU的L1 cache(1级缓存),这将大大减慢内存的查找速度。

    在性能方面,极重要的一点是:CPU可以将正在处理的所有数据尽可能地保存在缓存中。

    e3db38d523135ab33aa8697e2c6f8e2f.jpg

    图源:Unsplash

    结语

    优化的首要规则就是“不优化”。

    若真的有必要优化,那我希望这些技巧会有所帮助。

    但是,优化代码时一定要小心,因为优化的结果可能是代码难以阅读进而难以维护,这就得不偿失了。

    最后,希望大家能搭上python号火箭,编码越来越快!


    搜搜吧社区温馨提示:
    搜搜吧(www.sosoba.org)十分重视网络版权及其他知识产权的保护,针对网络侵权采取如下版权政策:
    1、本站有理由相信网友侵犯任何人的版权或作品,(图文,文字,下载,视频,非法传播),本站有权不事先通知即删除涉嫌侵权的作品和内容
    2、本站将采取必要的网络技术手段,确认为侵权作品或内容的用户有权进行警告、屏蔽、删除的行为,尽可能的防止侵权行为的发生
    3、搜搜吧影视资源均收集自互联网,没有提供影片资源存储,也未参与录制上传,若本站收录的资源涉及您的版权或知识产权或其他利益,我们会立即删除
    4、搜搜吧,删帖,投诉,举报,侵权,若本站侵犯您的权益,附上身份及权利证明,请直接发送邮件到 kefu-sosoba@qq.com 我们将在一个工作日内删除
    soso搜搜吧社区是聚合百度搜索,搜狗搜索,360搜索,新闻,教育,站长,广告,娱乐,影视,微信,网盘,营销,手机,汽车,游戏,论坛综合为一体的大型门户社区www.sosoba.org
    【腾讯云】中小企业福利专场,多款刚需产品,满足企业通用场景需求,云服务器2.5折起
    Powered by www.sosoba.org Copyright © 2013-2020 搜搜吧社区 小黑屋|手机版|地图|联系站长|腾讯云代金券|帮助中心|公共DNS|搜搜
    广告服务/项目合作/会员购买:QQ 侵权举报邮箱: kefu-sosoba@qq.com  搜搜吧建站时间:创建于2013年07月23日
    免责声明:本站所有的内容均来自互联网以及第三方作者自由发布,版权归原作者版权所有,搜搜吧不承担任何的法律责任,若有侵权请来信告知,我们立即删除!

    GMT+8, 2020-9-20 06:47 , Processed in 0.226511 second(s), 12 queries , MemCache On.

    快速回复 返回顶部 返回列表