热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

优化python执行效率

开始优化前,写一个高级测试来证明原来代码很慢。你可能需要采用一些最小值数据集来复现它足够慢。通常一两个显示运行时秒的程序就足够处理一些改进的地方了。有一些基础测试来保证你的优化没有

开始优化前,写一个高级测试来证明原来代码很慢。你可能需要采用一些最小值数据集来复现它足够慢。通常一两个显示运行时秒的程序就足够处理一些改进的地方了。


有一些基础测试来保证你的优化没有改变原有代码的行为也是很必要的。你也能够在很多次运行测试来优化代码的时候稍微修改这些测试的基准。


那么现在,我们来来看看优化工具把。

简单的计时器


计时器很简单,这是一个最灵活的记录执行时间的方法。你可以把它放到任何地方并且副作用很小。运行你自己的计时器非常简单,并且你可以将其定制,使它以你期望的方式工作。例如,你个简单的计时器如下:

import time    

def timefunc(f):  

def f_timer(*args, **kwargs):  

 start = time.time()   result = f(*args, **kwargs)  

 end = time.time()   print f.__name__, ‘took‘, end - start, ‘time‘  return result  return f_timer    def get_number():  for x in xrange(5000000):   yield x    @timefuncdef expensive_function():  for x in get_number():   i = x ^ x ^ x  return ‘some result!‘   # prints "expensive_function took 0.72583088875 seconds" result = expensive_function() 


当然,你可以用上下文管理来让它功能更加强大,添加一些检查点或者一些其他的功能:

 import time    class timewith():  def __init__(self, name=‘‘):   self.name = name   self.start = time.time()     @property def elapsed(self):   return time.time() - self.start     def checkpoint(self, name=‘‘):   print ‘{timer} {checkpoint} took {elapsed} seconds‘.format(    timer=self.name,    checkpoint=name,    elapsed=self.elapsed,   ).strip()     def __enter__(self):   return self    def __exit__(self, type, value, traceback):   self.checkpoint(‘finished‘)   pass   def get_number():  for x in xrange(5000000):   yield x    def expensive_function():  for x in get_number():   i = x ^ x ^ x  return ‘some result!‘   # prints something like: # fancy thing done with something took 0.582462072372 seconds # fancy thing done with something else took 1.75355315208 seconds # fancy thing finished took 1.7535982132 seconds with timewith(‘fancy thing‘) as timer:  expensive_function()  timer.checkpoint(‘done with something‘)  expensive_function()  expensive_function()  timer.checkpoint(‘done with something else‘)    # or directly timer = timewith(‘fancy thing‘) expensive_function() timer.checkpoint(‘done with something‘) 


计时器还需要你做一些挖掘。包装一些更高级的函数,并且确定瓶颈在哪,然后深入的函数里,能够不停的重现。当你发现一些不合适的代码,修复它,然后测试一遍以确认它被修复了。


一些小技巧:不要忘了好用的timeit模块!它对小块代码做基准测试而不是实际调查更加有用。


    Timer 优点:很容易理解和实现。也非常容易在修改后进行比较。对于很多语言都适用。 

    Timer 缺点:有时候对于非常复杂的代码有点过于简单,你可能会花更多时间放置或移动引用代码而不是修复问题! 

内建优化器


启用内建的优化器就像是用一门大炮。它非常强大,但是有点不太好用,使用和解释起来比较复杂。


你可以了解更多关于profile模块的东西,但是它的基础是非常简单的:你能够启用和禁用优化器,而且它能打印所有的函数调用和执行时间。它能给你编译和打印出输出。一个简单的装饰器如下:

 import cProfile    def do_cprofile(func):  def profiled_func(*args, **kwargs):   profile = cProfile.Profile()   try:    profile.enable()    result = func(*args, **kwargs)    profile.disable()    return result   finally:    profile.print_stats()  return profiled_func    def get_number():  for x in xrange(5000000):   yield x    @do_cprofiledef expensive_function():  for x in get_number():   i = x ^ x ^ x  return ‘some result!‘   # perform profiling result = expensive_function() 


在上面代码的情况下,你应该看到有些东西在终端打印出来,打印的内容如下:

5000003 function calls in 1.626 seconds     Ordered by: standard name     ncalls tottime percall cumtime percall filename:lineno(function)  5000001 0.571 0.000 0.571 0.000 timers.py:92(get_number)   1 1.055 1.055 1.626 1.626 timers.py:96(expensive_function)   1 0.000 0.000 0.000 0.000 {method ‘disable‘ of ‘_lsprof.Profiler‘ objects} 


你可以看到,它给出了不同函数的调用次数,但它遗漏了一些关键的信息:是哪个函数让运行这么慢?


可是,这对于基础优化来说是个好的开始。有时候甚至能用更少的精力找到解决方案。我经常用它来在深入挖掘究竟是哪个函数慢或者调用次数过多之前来调试程序。


    内建优点:没有额外的依赖并且非常快。对于快速的高等级检查非常有用。 

    内建缺点:信息相对有限,需要进一步的调试;报告有点不太直接,尤其是对于复杂的代码。 

Line Profiler


如果内建的优化器是一门大炮,那么line profiler可以看作是一门离子加农炮。它非常的重量级和强大。


在这个例子里,我们会用非常棒的line_profiler库。为了容易使用,我们会再次用装饰器包装一下,这种简单的方法也可以防止把它放在生产代码里。

try:  from line_profiler import LineProfiler     def do_profile(follow=[]):   def inner(func):    def profiled_func(*args, **kwargs):     try:      profiler = LineProfiler()      profiler.add_function(func)      for f in follow:       profiler.add_function(f)      profiler.enable_by_count()      return func(*args, **kwargs)     finally:      profiler.print_stats()    return profiled_func   return inner    except ImportError:  def do_profile(follow=[]):   "Helpful if you accidentally leave in production!"  def inner(func):    def nothing(*args, **kwargs):     return func(*args, **kwargs)    return nothing   return inner    def get_number():  for x in xrange(5000000):   yield x    @do_profile(follow=[get_number]) def expensive_function():  for x in get_number():   i = x ^ x ^ x  return ‘some result!‘   result = expensive_function() 


如果你运行上面的代码,你就可以看到一下的报告:

 Timer unit: 1e-06 s    File: test.py Function: get_number at line 43Total time: 4.44195 s    Line #  Hits   Time Per Hit % Time Line COntents============================================================== 43           def get_number():  44 5000001  2223313  0.4  50.1  for x in xrange(5000000):  45 5000000  2218638  0.4  49.9   yield x    File: test.py Function: expensive_function at line 47Total time: 16.828 s    Line #  Hits   Time Per Hit % Time Line COntents============================================================== 47           def expensive_function():  48 5000001  14090530  2.8  83.7  for x in get_number():  49 5000000  2737480  0.5  16.3   i = x ^ x ^ x  50   1   0  0.0  0.0  return ‘some result!‘ 


你可以看到,有一个非常详细的报告,能让你完全洞悉代码运行的情况。不想内建的cProfiler,它能计算话在语言核心特性的时间,比如循环和导入并且给出在不同的行花费的时间。


这些细节能让我们更容易理解函数内部。如果你在研究某个第三方库,你可以直接将其导入并加上装饰器来分析它。


一些小技巧:只装饰你的测试函数并将问题函数作为接下来的参数。


     Line Profiler 优点:有非常直接和详细的报告。能够追踪第三方库里的函数。 

     Line Profiler 缺点:因为它会让代码比真正运行时慢很多,所以不要用它来做基准测试。这是额外的需求。 

总结和最佳实践


你应该用更简单的工具来对测试用例进行根本的检查,并且用更慢但能显示更多细节的line_profiler来深入到函数内部。


九成情况下,你可能会发现在一个函数里循环调用或一个错误的数据结构消耗了90%的时间。一些调整工具是非常适合你的。


如果你仍然觉得这太慢,而是用一些你自己的秘密武器,如比较属性访问技术或调整平衡检查技术。你也可以用如下的方法:


1.忍受缓慢或者缓存它们


2.重新思考整个实现


3.更多使用优化的数据结构


4.写一个C扩展


注意了,优化代码是种罪恶的快感!用合适的方法来为你的Python代码加速很有意思,但是注意不要破坏了本身的逻辑。可读的代码比运行速度更重要。先把它缓存起来再进行优化其实更好。



本文出自 “yishengayou” 博客,请务必保留此出处http://10078369.blog.51cto.com/10068369/1627892

优化python执行效率


推荐阅读
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文是一位90后程序员分享的职业发展经验,从年薪3w到30w的薪资增长过程。文章回顾了自己的青春时光,包括与朋友一起玩DOTA的回忆,并附上了一段纪念DOTA青春的视频链接。作者还提到了一些与程序员相关的名词和团队,如Pis、蛛丝马迹、B神、LGD、EHOME等。通过分享自己的经验,作者希望能够给其他程序员提供一些职业发展的思路和启示。 ... [详细]
author-avatar
可乐16311
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有