免费数据统计网站,业务多平台怎么样,网站备案身份核验,百度一下你就知道手机版Python性能优化的20条建议 http://segmentfault.com/a/1190000000666603优化算法时间复杂度 算法的时间复杂度对程序的执行效率影响最大#xff0c;在Python中可以通过选择合适的数据结构来优化时间复杂度#xff0c;如list和set查找某一个元素的时间复杂度分别是O(n)和O(1)。… Python性能优化的20条建议 http://segmentfault.com/a/1190000000666603 优化算法时间复杂度 算法的时间复杂度对程序的执行效率影响最大在Python中可以通过选择合适的数据结构来优化时间复杂度如list和set查找某一个元素的时间复杂度分别是O(n)和O(1)。不同的场景有不同的优化方式总得来说一般有分治分支界限贪心动态规划等思想。 减少冗余数据 如用上三角或下三角的方式去保存一个大的对称矩阵。在0元素占大多数的矩阵里使用稀疏矩阵表示。 合理使用copy与deepcopy 对于dict和list等数据结构的对象直接赋值使用的是引用的方式。而有些情况下需要复制整个对象这时可以使用copy包里的copy和deepcopy这两个函数的不同之处在于后者是递归复制的。效率也不一样以下程序在ipython中运行 import copy
a range(100000)
%timeit -n 10 copy.copy(a) # 运行10次 copy.copy(a)
%timeit -n 10 copy.deepcopy(a) 10 loops, best of 3: 1.55 ms per loop 10 loops, best of 3: 151 ms per loop timeit后面的-n表示运行的次数后两行对应的是两个timeit的输出下同。由此可见后者慢一个数量级。 使用dict或set查找元素 python dict和set都是使用hash表来实现(类似c11标准库中unordered_map)查找元素的时间复杂度是O(1) a range(1000)
s set(a)
d dict((i,1) for i in a)
%timeit -n 10000 100 in d %timeit -n 10000 100 in s 10000 loops, best of 3: 43.5 ns per loop 10000 loops, best of 3: 49.6 ns per loop dict的效率略高(占用的空间也多一些)。 合理使用生成器generator和yield %timeit -n 100 a (i for i in range(100000))
%timeit -n 100 b [i for i in range(100000)] 100 loops, best of 3: 1.54 ms per loop 100 loops, best of 3: 4.56 ms per loop 使用()得到的是一个generator对象所需要的内存空间与列表的大小无关所以效率会高一些。在具体应用上比如set(i for i in range(100000))会比set([i for i in range(100000)])快。 但是对于需要循环遍历的情况 %timeit -n 10 for x in (i for i in range(100000)): pass %timeit -n 10 for x in [i for i in range(100000)]: pass 10 loops, best of 3: 6.51 ms per loop 10 loops, best of 3: 5.54 ms per loop 后者的效率反而更高但是如果循环里有break,用generator的好处是显而易见的。yield也是用于创建generator def yield_func(ls): for i in ls: yield i1 def not_yield_func(ls): return [i1 for i in ls] ls range(1000000) %timeit -n 10 for i in yield_func(ls):pass %timeit -n 10 for i in not_yield_func(ls):pass 10 loops, best of 3: 63.8 ms per loop 10 loops, best of 3: 62.9 ms per loop 对于内存不是非常大的list可以直接返回一个list但是可读性yield更佳(人个喜好)。 python2.x内置generator功能的有xrange函数、itertools包等。 优化循环 循环之外能做的事不要放在循环内比如下面的优化可以快一倍 a range(10000)
size_a len(a)
%timeit -n 1000 for i in a: k len(a) %timeit -n 1000 for i in a: k size_a 1000 loops, best of 3: 569 µs per loop 1000 loops, best of 3: 256 µs per loop 优化包含多个判断表达式的顺序 对于and应该把满足条件少的放在前面对于or把满足条件多的放在前面。如 a range(2000)
%timeit -n 100 [i for i in a if 10 i 20 or 1000 i 2000] %timeit -n 100 [i for i in a if 1000 i 2000 or 100 i 20] %timeit -n 100 [i for i in a if i % 2 0 and i 1900] %timeit -n 100 [i for i in a if i 1900 and i % 2 0] 100 loops, best of 3: 287 µs per loop 100 loops, best of 3: 214 µs per loop 100 loops, best of 3: 128 µs per loop 100 loops, best of 3: 56.1 µs per loop 使用join合并迭代器中的字符串 In [1]: %%timeit...: s ...: for i in a: ...: s i ...: 10000 loops, best of 3: 59.8 µs per loop In [2]: %%timeit s .join(a) ...: 100000 loops, best of 3: 11.8 µs per loop join对于累加的方式有大约5倍的提升。 选择合适的格式化字符方式 s1, s2 ax, bx
%timeit -n 100000 abc%s%s % (s1, s2)
%timeit -n 100000 abc{0}{1}.format(s1, s2) %timeit -n 100000 abc s1 s2 100000 loops, best of 3: 183 ns per loop 100000 loops, best of 3: 169 ns per loop 100000 loops, best of 3: 103 ns per loop 三种情况中%的方式是最慢的但是三者的差距并不大都非常快。(个人觉得%的可读性最好) 不借助中间变量交换两个变量的值 In [3]: %%timeit -n 10000a,b1,2....: ca;ab;bc;....:
10000 loops, best of 3: 172 ns per loop In [4]: %%timeit -n 10000 a,b1,2 a,bb,a ....: 10000 loops, best of 3: 86 ns per loop 使用a,bb,a而不是ca;ab;bc;来交换a,b的值可以快1倍以上。 使用if is a range(10000)
%timeit -n 100 [i for i in a if i True] %timeit -n 100 [i for i in a if i is True] 100 loops, best of 3: 531 µs per loop 100 loops, best of 3: 362 µs per loop 使用 if is True 比 if True 将近快一倍。 使用级联比较x y z x, y, z 1,2,3
%timeit -n 1000000 if x y z:pass %timeit -n 1000000 if x y and y z:pass 1000000 loops, best of 3: 101 ns per loop 1000000 loops, best of 3: 121 ns per loop x y z效率略高而且可读性更好。 while 1 比 while True 更快 def while_1(): n 100000 while 1: n - 1 if n 0: break def while_true(): n 100000 while True: n - 1 if n 0: break m, n 1000000, 1000000 %timeit -n 100 while_1() %timeit -n 100 while_true() 100 loops, best of 3: 3.69 ms per loop 100 loops, best of 3: 5.61 ms per loop while 1 比 while true快很多原因是在python2.x中True是一个全局变量而非关键字。 使用**而不是pow %timeit -n 10000 c pow(2,20)
%timeit -n 10000 c 2**20 10000 loops, best of 3: 284 ns per loop 10000 loops, best of 3: 16.9 ns per loop **就是快10倍以上 使用 cProfile, cStringIO 和 cPickle等用c实现相同功能分别对应profile, StringIO, pickle的包 import cPickle
import pickle
a range(10000)
%timeit -n 100 x cPickle.dumps(a)
%timeit -n 100 x pickle.dumps(a) 100 loops, best of 3: 1.58 ms per loop 100 loops, best of 3: 17 ms per loop 由c实现的包速度快10倍以上 使用最佳的反序列化方式 下面比较了eval, cPickle, json方式三种对相应字符串反序列化的效率 import json
import cPickle
a range(10000)
s1 str(a)
s2 cPickle.dumps(a)
s3 json.dumps(a)
%timeit -n 100 x eval(s1)
%timeit -n 100 x cPickle.loads(s2) %timeit -n 100 x json.loads(s3) 100 loops, best of 3: 16.8 ms per loop 100 loops, best of 3: 2.02 ms per loop 100 loops, best of 3: 798 µs per loop 可见json比cPickle快近3倍比eval快20多倍。 使用C扩展(Extension) 目前主要有CPython(python最常见的实现的方式)原生API, ctypes,Cythoncffi三种方式它们的作用是使得Python程序可以调用由C编译成的动态链接库其特点分别是 CPython原生API: 通过引入Python.h头文件对应的C程序中可以直接使用Python的数据结构。实现过程相对繁琐但是有比较大的适用范围。 ctypes: 通常用于封装(wrap)C程序让纯Python程序调用动态链接库Windows中的dll或Unix中的so文件中的函数。如果想要在python中使用已经有C类库使用ctypes是很好的选择有一些基准测试下python2ctypes是性能最好的方式。 Cython: Cython是CPython的超集用于简化编写C扩展的过程。Cython的优点是语法简洁可以很好地兼容numpy等包含大量C扩展的库。Cython的使得场景一般是针对项目中某个算法或过程的优化。在某些测试中可以有几百倍的性能提升。 cffi: cffi的就是ctypes在pypy详见下文中的实现同进也兼容CPython。cffi提供了在python使用C类库的方式可以直接在python代码中编写C代码同时支持链接到已有的C类库。 使用这些优化方式一般是针对已有项目性能瓶颈模块的优化可以在少量改动原有项目的情况下大幅度地提高整个程序的运行效率。 并行编程 因为GIL的存在Python很难充分利用多核CPU的优势。但是可以通过内置的模块multiprocessing实现下面几种并行模式 多进程对于CPU密集型的程序可以使用multiprocessing的Process,Pool等封装好的类通过多进程的方式实现并行计算。但是因为进程中的通信成本比较大对于进程之间需要大量数据交互的程序效率未必有大的提高。 多线程对于IO密集型的程序multiprocessing.dummy模块使用multiprocessing的接口封装threading使得多线程编程也变得非常轻松(比如可以使用Pool的map接口简洁高效)。 分布式multiprocessing中的Managers类提供了可以在不同进程之共享数据的方式可以在此基础上开发出分布式的程序。 不同的业务场景可以选择其中的一种或几种的组合实现程序性能的优化。 终级大杀器PyPy PyPy是用RPython(CPython的子集)实现的Python根据官网的基准测试数据它比CPython实现的Python要快6倍以上。快的原因是使用了Just-in-Time(JIT)编译器即动态编译器与静态编译器(如gcc,javac等)不同它是利用程序运行的过程的数据进行优化。由于历史原因目前pypy中还保留着GIL不过正在进行的STM项目试图将PyPy变成没有GIL的Python。 如果python程序中含有C扩展(非cffi的方式)JIT的优化效果会大打折扣甚至比CPython慢比Numpy。所以在PyPy中最好用纯Python或使用cffi扩展。 随着STMNumpy等项目的完善相信PyPy将会替代CPython。 使用性能分析工具 除了上面在ipython使用到的timeit模块还有cProfile。cProfile的使用方式也非常简单 python -m cProfile filename.pyfilename.py 是要运行程序的文件名可以在标准输出中看到每一个函数被调用的次数和运行的时间从而找到程序的性能瓶颈然后可以有针对性地优化。 参考 [1] http://www.ibm.com/developerworks/cn/linux/l-cn-python-optim/ [2] http://maxburstein.com/blog/speeding-up-your-python-code/ http://code.oneapm.com/python/2015/05/18/python-performance-tips/ 原文地址https://blog.newrelic.com/2015/01/21/python-performance-tips/ Python是一门优秀的语言它能让你在短时间内通过极少量代码就能完成许多操作。不仅如此它还轻松支持多任务处理比如多进程。 不喜欢Python的人经常会吐嘈Python运行太慢。但是事实并非如此。尝试以下六个窍门来为你的Python应用提速。 窍门一关键代码使用外部功能包 Python简化了许多编程任务但是对于一些时间敏感的任务它的表现经常不尽人意。使用C/C或机器语言的外部功能包处理时间敏感任务可以有效提高应用的运行效率。这些功能包往往依附于特定的平台因此你要根据自己所用的平台选择合适的功能包。简而言之这个窍门要你牺牲应用的可移植性以换取只有通过对底层主机的直接编程才能获得的运行效率。以下是一些你可以选择用来提升效率的功能包 CythonPylnlnePyPyPyrex这些功能包的用处各有不同。比如说使用C语言的数据类型可以使涉及内存操作的任务更高效或者更直观。Pyrex就能帮助Python延展出这样的功能。Pylnline能使你在Python应用中直接使用C代码。内联代码是独立编译的但是它把所有编译文件都保存在某处并能充分利用C语言提供的高效率。 窍门二在排序时使用键 Python含有许多古老的排序规则这些规则在你创建定制的排序方法时会占用很多时间而这些排序方法运行时也会拖延程序实际的运行速度。最佳的排序方法其实是尽可能多地使用键和内置的sort()方法。譬如拿下面的代码来说 import operatorsomelist [(1, 5, 8), (6, 2, 4), (9, 7, 5)] somelist.sort(keyoperator.itemgetter(0)) somelist #Output [(1, 5, 8), (6, 2, 4), (9, 7, 5)] somelist.sort(keyoperator.itemgetter(1)) somelist #Output [(6, 2, 4), (1, 5, 8), (9, 7, 5)] somelist.sort(keyoperator.itemgetter(2)) somelist #Output [(6, 2, 4), (9, 7, 5), (1, 5, 8)], 在每段例子里list都是根据你选择的用作关键参数的索引进行排序的。这个方法不仅对数值类型有效还同样适用于字符串类型。 窍门三针对循环的优化 每一种编程语言都强调最优化的循环方案。当使用Python时你可以借助丰富的技巧让循环程序跑得更快。然而开发者们经常遗忘的一个技巧是尽量避免在循环中访问变量的属性。譬如拿下面的代码来说 lowerlist [this, is, lowercase] upper str.upper upperlist [] append upperlist.append for word in lowerlist: append(upper(word)) print(upperlist) #Output [THIS, IS, LOWERCASE] 每次你调用str.upper, Python都会计算这个式子的值。然而如果你把这个求值赋值给一个变量那么求值的结果就能提前知道Python程序就能运行得更快。因此关键就是尽可能减小Python在循环中的工作量。因为Python解释执行的特性在上面的例子中会大大减慢它的速度。 注意优化循环的方法还有很多这只是其中之一。比如很多程序员会认为列表推导式是提高循环速度的最佳方法。关键在于优化循环方案是提高应用程序运行速度的上佳选择。 窍门四使用较新的Python版本 如果你在网上搜索Python你会发现数不尽的信息都是关于如何升级Python版本。通常每个版本的Python都会包含优化内容使其运行速度优于之前的版本。但是限制因素在于你最喜欢的函数库有没有同步更新支持新的Python版本。与其争论函数库是否应该更新关键在于新的Python版本是否足够高效来支持这一更新。 你要保证自己的代码在新版本里还能运行。你需要使用新的函数库才能体验新的Python版本然后你需要在做出关键性的改动时检查自己的应用。只有当你完成必要的修正之后你才能体会新版本的不同。 然而如果你只是确保自己的应用在新版本中可以运行你很可能会错过新版本提供的新特性。一旦你决定更新请分析你的应用在新版本下的表现并检查可能出问题的部分然后优先针对这些部分应用新版本的特性。只有这样用户才能在更新之初就觉察到应用性能的改观。 窍门五尝试多种编码方法 每次创建应用时都使用同一种编码方法几乎无一例外会导致应用的运行效率不尽人意。可以在程序分析时尝试一些试验性的办法。譬如说在处理字典中的数据项时你既可以使用安全的方法先确保数据项已经存在再进行更新也可以直接对数据项进行更新把不存在的数据项作为特例分开处理。请看下面第一段代码 n 16myDict {} for i in range(0, n): char abcd[i%4] if char not in myDict: myDict[char] 0 myDict[char] 1 print(myDict) 当一开始myDict为空时这段代码会跑得比较快。然而通常情况下myDict填满了数据至少填有大部分数据这时换另一种方法会更有效率。 n 16myDict {} for i in range(0, n): char abcd[i%4] try: myDict[char] 1 except KeyError: myDict[char] 1 print(myDict) 在两种方法中输出结果都是一样的。区别在于输出是如何获得的。跳出常规的思维模式创建新的编程技巧能使你的应用更有效率。 窍门六交叉编译你的应用 开发者有时会忘记计算机其实并不理解用来创建现代应用程序的编程语言。计算机理解的是机器语言。为了运行你的应用你借助一个应用将你所编的人类可读的代码转换成机器可读的代码。有时你用一种诸如Python这样的语言编写应用再以C这样的语言运行你的应用这在运行的角度来说是可行的。关键在于你想你的应用完成什么事情而你的主机系统能提供什么样的资源。 Nuitka是一款有趣的交叉编译器能将你的Python代码转化成C代码。这样你就可以在native模式下执行自己的应用而无需依赖于解释器程序。你会发现自己的应用运行效率有了较大的提高但是这会因平台和任务的差异而有所不同。 注意Nuitka现在还处在测试阶段所以在实际应用中请多加注意。实际上当下最好还是把它用于实验。此外关于交叉编译是否为提高运行效率的最佳方法还存在讨论的空间。开发者已经使用交叉编译多年用来提高应用的速度。记住每一种解决办法都有利有弊在把它用于生产环境之前请仔细权衡。 在使用交叉编译器时记得确保它支持你所用的Python版本。Nuitka支持Python2.6, 2.7, 3.2和3.3。为了让解决方案生效你需要一个Python解释器和一个C编译器。Nuitka支持许多C编译器其中包括Microsoft Visual Studio,MinGW 和 Clang/LLVM。 交叉编译可能造成一些严重问题。比如在使用Nuitka时你会发现即便是一个小程序也会消耗巨大的驱动空间。因为Nuitka借助一系列的动态链接库DDLs来执行Python的功能。因此如果你用的是一个资源很有限的系统这种方法或许不太可行。 结论 前文所述的六个窍门都能帮助你创建运行更有效率的Python应用。但是银弹是不存在的。上述的这些窍门不一定每次都能奏效。在特定的Python的版本下有的窍门或许比其他的表现更好但这有时候甚至取决于平台的差异。你需要总结分析你的应用找到它效率低下的部分然后尝试这些窍门找到解决问题的最佳方法。转载于:https://www.cnblogs.com/zhizhan/p/4883806.html