代码运行缓慢并转储Python?教你如何将你的代码速度降低30%。一个特定的程序(无论使用何种编程语言)运行得快还是慢在很大程度上取决于编写该程序的开发人员的素质以及他们编写优化和高效代码的能力。Medium上有位小哥详细讲解了如何让python提速30%,从而证明代码运行慢不是python的问题,而是代码本身的问题。时序分析在我们开始进行任何优化之前,我们首先需要找出代码的哪些部分正在减慢整个程序。有时程序的问题很明显,但如果你此时不知道问题出在哪里,这里有一些可能的选择:注意:这是我将用于演示的程序,它将进行指数计算(采取来自Python文档):#slow_program.pyfromdecimalimport*defexp(x):getcontext().prec+=2i,lasts,s,fact,num=0,0,1,1,1whiles!=lasts:lasts=si+=1fact*=inum*=xs+=num/factgetcontext().prec-=2return+sexp(Decimal(150))exp(Decimal(400))exp(Decimal(3000))最简单的“配置文件”首先,最简单最最懒的方法——Unix时间命令。~$timepython3.8slow_program.pyreal0m11,058suser0m11,050ssys0m0,008s如果你只能get到整个程序的运行时间,这就够了,但通常这还不够。最详细的配置文件是另一个命令cProfile,但是它提供的信息太详细了。~$python3.8-mcProfile-stimeslow_program.py1297functioncalls(1272primitivecalls)in11.081secondsOrderedby:internaltimencallstottimepercallcumtimepercallfilename:lineno(function)311.0793.69311.0793.693slow_program.py:4(exp)10.0000.0000.0202{}10.0000.0000.0202}10.0000.00011.08111.081{built-inmethodbuiltins.exec}60.0000.0000.0000.000{built-inmethod__new__oftypeobjectat0x9d12c0}60.0000.0000.0000.000abc.py:132(__new__)230.0000.0000.0000.000_weakrefset.py:36(__init__)2450.0000.0000.0000.000{built-inmethodbuiltins.getattr}20.0000.0000.0000.000{built-inmethodmarshal.loads}100.0000.0000.0000.000:1233(find_spec)8/40.0000.0000.0000.0000abc.py00000.0000.010abc.py。480.0000.0000.0000.000:59()10.0000.00011.08111.081slow_program.py:1()这里我们运行带有cProfile模块和time参数的测试脚本,这样就可以计时了按内部时间(cumtime)对行进行排序,这为我们提供了很多信息,您在上面看到的行大约是实际输出的10%。由此可以看出exp函数是罪魁祸首,现在我们可以更详细地查看时序和性能分析。为特定函数计时现在我们知道应该关注哪里,我们可能希望在不测量其余代码的情况下为慢函数计时。为此,我们可以使用一个简单的装饰器:deftimeit_wrapper(func):@wraps(func)defwrapper(*args,**kwargs):start=time.perf_counter()#Alternatively,youcanusetime.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))returnfunc_return_valreturnwrapper然后可以将此装饰器应用于被测函数,如下所示:@timeit_wrapperdefexp(x):...print('{0:<10}{1:<8}{2:^8}'.format('module','function','time'))exp(Decimal(150))exp(Decimal(400))exp(Decimal(3000))这给了我们以下输出:~$python3.8slow_program.pymodulefunctiontime__main__。exp:0.003267502994276583__main__.exp:0.038535295985639095__main__.exp:11.728486061969306需要考虑的一件事是我们实际想要测量的时间。time包提供了两个函数time.perf_counter和time.process_time。它们的区别在于perf_counter返回的绝对值包含了你的Python程序进程没有运行的时间,所以可能会受到计算机负载的影响。另一方面,process_time仅返回用户时间(不包括系统时间),这只是您的进程时间。加快速度!让Python程序运行得更快的部分会很有趣!我不会展示可以解决您的性能问题的技巧和代码,更多的是关于想法和策略,当使用时,可能会对性能产生巨大影响,在某些情况下速度提高30%。这在使用内置数据类型时很明显。内置数据类型非常快,特别是与我们的自定义类型(如树或链表)相比。这主要是因为builtins是用C实现的,所以我们无法真正匹配用Python编码时的速度。使用lru_cache进行缓存/记忆我在之前的博客文章中展示了这一点,但我认为有必要通过一个简单的例子来重复它:importfunctoolsimporttime#cachingupto12differentresults@functools.lru_cache(maxsize=12)defslow_func(x):time.sleep(2)#Simulatelongcomputationreturnxslow_func(1)#...waitingfor2secbeforegettingresultslow_func(1)#alreadycached-resultreturnedinstantaneously!slow_func(3)#...waitingfor2secbeforegettingresult以上函数使用time.sleep模拟大量计算。第一次使用参数1调用时,它将等待2秒,然后返回结果。再次调用时,结果已经缓存了,所以会跳过函数体,直接返回结果。有关更多实际示例,请参阅以前的博客文章。使用局部变量这与在每个范围内查找变量的速度有关,因为它不只是使用局部或全局变量。事实上,即使在函数的局部变量(最快)、类级属性(例如self.name-较慢)和全局变量(例如导入函数)之间,如time.time(最慢),查找实际上也是不同的。您可以通过使用看似不必要的分配来提高性能,如下所示:#Example#1classFastClass:defdo_stuff(self):temp=self.value#thisspeedsuplookupinloopforiinrange(10000):...#Dosomethingwith`temp`here#Example#2importrandomdefast_function():r=random.randomforiinrange(10000):print(r())#calling`r()`这里,isfasterthanglobalrandom.random()使用了一个函数,这个函数看起来违反直觉,因为调用一个函数会把更多的东西放到栈上,并招致函数返回的开销,但这与前一点有关。如果您只是将整个代码放在一个文件中而不将其放在一个函数中,那么由于全局变量,它的运行速度会慢得多。因此,您可以通过将整个代码包装在一个main函数中并调用它一次来加速您的代码,如下所示:点运算符(.),在获取对象属性时使用。此运算符使用__getattribute__来触发字典查找,这会在代码中产生额外的开销。那么,如何才能真正避免(限制)使用它呢?#Slow:importredefslow_func():foriinrange(10000):re.findall(regex,line)#Slow!#Fast:fromreimportfindalldeffast_func():foriinrange(10000):findall(regex,line)#更快!当心字符串当使用模数(%s)或.format()循环时,字符串操作会变得非常慢。我们有什么更好的选择?根据RaymondHettinger最近的推文,我们唯一应该使用的是f-strings,这是最易读、最简洁和最快的方法。根据推文,这是您可以使用的方法列表-从最快到最慢:f'{s}{t}'#Fast!s+''+t''.join((s,t))'%s%s'%(s,t)'{}{}'.format(s,t)Template('$s$t').substitute(ss=s,tt=t)#慢!生成器本质并不快,因为它们被允许延迟计算,节省内存而不是时间。但是,节省的内存可能会使您的程序实际运行得更快。这是如何运作的?如果你有一个大数据集并且你不使用生成器(迭代器),那么数据可能会溢出CPUL1缓存,这将大大减慢在内存中查找值的速度。就性能而言,CPU能够将其正在处理的所有数据尽可能多地保留在缓存中非常重要。您可以观看RaymondHettingers解决这些问题的视频。结论优化的首要规则是不优化。但是,如果您真的需要,那么我希望上面的这些提示可以帮助到您。但是,在优化代码时要小心,因为它最终可能会使代码难以阅读并因此难以维护,这可能会超过优化的好处。相关报道:https://towardsdatascience.com/making-python-programs-blazingly-fast-c1cd79bd1b32【本文为栏目组织大数据文摘原文翻译,微信公众号》大数据文摘(id:BigDataDigest)》】戳这里,阅读更多本作者的好文