python运行时间过长怎么优化_Python性能优化的20条建议

python运行时间过长怎么优化_Python性能优化的20条建议

2023年7月10日发(作者:)

python运⾏时间过长怎么优化_Python性能优化的20条建议1.优化算法时间复杂度算法的时间复杂度对程序的执⾏效率影响最⼤,在Python中可以通过选择合适的数据结构来优化时间复杂度,如list和set查找某⼀个元素的时间复杂度分别是O(n)和O(1)。不同的场景有不同的优化⽅式,总得来说,⼀般有分治,分⽀界限,贪⼼,动态规划等思想。2.减少冗余数据如⽤上三⾓或下三⾓的⽅式去保存⼀个⼤的对称矩阵。在0元素占⼤多数的矩阵⾥使⽤稀疏矩阵表⽰。3.合理使⽤copy与deepcopy对于dict和list等数据结构的对象,直接赋值使⽤的是引⽤的⽅式。⽽有些情况下需要复制整个对象,这时可以使⽤copy包⾥的copy和deepcopy,这两个函数的不同之处在于后者是递归复制的。效率也不⼀样:(以下程序在ipython中运⾏)import copya = range(100000)%timeit -n 10 (a) # 运⾏10次 (a)%timeit -n 10 py(a)10 loops, best of 3: 1.55 ms per loop10loops, best of 3: 151 ms per looptimeit后⾯的-n表⽰运⾏的次数,后两⾏对应的是两个timeit的输出,下同。由此可见后者慢⼀个数量级。4.使⽤dict或set查找元素python dict和set都是使⽤hash表来实现(类似c++11标准库中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 s10000 loops, best of 3: 43.5 ns per loop10000 loops, best of 3: 49.6 ns per loopdict的效率略⾼(占⽤的空间也多⼀些)。5.合理使⽤⽣成器(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 loop100 loops, best of 3: 4.56 ms per loop使⽤()得到的是⼀个generator对象,所需要的内存空间与列表的⼤⼩⽆关,所以效率会⾼⼀些。在具体应⽤上,⽐如set(i for i inrange(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)]: pass10 loops, best of 3: 6.51 ms per loop10 loops, best of 3: 5.54 ms perloop后者的效率反⽽更⾼,但是如果循环⾥有break,⽤generator的好处是显⽽易见的。yield也是⽤于创建generator:def yield_func(ls):for i in ls: yield i+1def not_yield_func(ls):return [i+1 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):pass10 loops, best of 3: 63.8 ms per loop10loops, best of 3: 62.9 ms per loop对于内存不是⾮常⼤的list,可以直接返回⼀个list,但是可读性yield更佳(⼈个喜好)。python2.x内置generator功能的有xrange函数、itertools包等。6.优化循环循环之外能做的事不要放在循环内,⽐如下⾯的优化可以快⼀倍: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_a1000 loops, best of 3: 569 µs per loop1000 loops, best of 3: 256 µs per loop7.优化包含多个判断表达式的顺序对于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 loop100 loops, best of 3: 214 µs perloop100 loops, best of 3: 128 µs per loop100 loops, best of 3: 56.1 µs per loop8.使⽤join合并迭代器中的字符串In [1]: %%: s = ''...: for i in a:...: s += i...:10000 loops, best of 3: 59.8 µs per loopIn [2]: %%timeits = ''.join(a)...:100000 loops, best of 3: 11.8 µs per loopjoin对于累加的⽅式,有⼤约5倍的提升。9.选择合适的格式化字符⽅式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 + s2100000 loops, best of 3: 183 ns per loop100000 loops, best of 3: 169 ns perloop100000 loops, best of 3: 103 ns per loop三种情况中,%的⽅式是最慢的,但是三者的差距并不⼤(都⾮常快)。(个⼈觉得%的可读性最好)10.不借助中间变量交换两个变量的值In [3]: %%timeit -n 10000a,b=: c=a;a=b;b=c;....:10000 loops, best of 3: 172 ns per loopIn [4]: %%timeit -n 10000a,b=1,2a,b=:10000 loops, best of 3: 86 ns per loop使⽤a,b=b,a⽽不是c=a;a=b;b=c;来交换a,b的值,可以快1倍以上。11.使⽤if isa = 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 loop100 loops, best of 3: 362 µs per loop使⽤ if is True ⽐ if == True 将近快⼀倍。12.使⽤级联⽐较x < y < zx, y, z = 1,2,3%timeit -n 1000000 if x < y < z:pass%timeit -n 1000000 if x < y and y < z:pass1000000 loops, best of 3: 101 ns per loop1000000 loops, best of 3: 121 ns perloopx < y < z效率略⾼,⽽且可读性更好。 1 ⽐ while True 更快def while_1():n = 100000while 1:n -= 1if n <= 0: breakdef while_true():n = 100000while True:n -= 1if 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 loop100 loops, best of 3: 5.61 ms per loopwhile 1 ⽐ while true快很多,原因是在python2.x中,True是⼀个全局变量,⽽⾮关键字。14.使⽤**⽽不是pow%timeit -n 10000 c = pow(2,20)%timeit -n 10000 c = 2**2010000 loops, best of 3: 284 ns per loop10000 loops, best of 3: 16.9 ns per loop**就是快10倍以上!15.使⽤ cProfile, cStringIO 和 cPickle等⽤c实现相同功能(分别对应profile, StringIO, pickle)的包import cPickleimport picklea = range(10000)%timeit -n 100 x = (a)%timeit -n 100 x = (a)100 loops, best of 3: 1.58 ms per loop100 loops, best of 3: 17 ms per loop由c实现的包,速度快10倍以上!16.使⽤最佳的反序列化⽅式下⾯⽐较了eval, cPickle, json⽅式三种对相应字符串反序列化的效率:import jsonimport cPicklea = range(10000)s1 = str(a)s2 = (a)s3 = (a)%timeit -n 100 x = eval(s1)%timeit -n 100 x = (s2)%timeit -n 100 x = (s3)100 loops, best of 3: 16.8 ms per loop100 loops, best of 3: 2.02 ms per loop100 loops,best of 3: 798 µs per loop可见json⽐cPickle快近3倍,⽐eval快20多倍。17.使⽤C扩展(Extension)⽬前主要有CPython(python最常见的实现的⽅式)原⽣API, ctypes,Cython,cffi三种⽅式,它们的作⽤是使得Python程序可以调⽤由C编译成的动态链接库,其特点分别是:CPython原⽣API: 通过引⼊Python.h头⽂件,对应的C程序中可以直接使⽤Python的数据结构。实现过程相对繁琐,但是有⽐较⼤的适⽤范围。ctypes: 通常⽤于封装(wrap)C程序,让纯Python程序调⽤动态链接库(Windows中的dll或Unix中的so⽂件)中的函数。如果想要在python中使⽤已经有C类库,使⽤ctypes是很好的选择,有⼀些基准测试下,python2+ctypes是性能最好的⽅式。Cython: Cython是CPython的超集,⽤于简化编写C扩展的过程。Cython的优点是语法简洁,可以很好地兼容numpy等包含⼤量C扩展的库。Cython的使得场景⼀般是针对项⽬中某个算法或过程的优化。在某些测试中,可以有⼏百倍的性能提升。cffi: cffi的就是ctypes在pypy(详见下⽂)中的实现,同进也兼容CPython。cffi提供了在python使⽤C类库的⽅式,可以直接在python代码中编写C代码,同时⽀持链接到已有的C类库。使⽤这些优化⽅式⼀般是针对已有项⽬性能瓶颈模块的优化,可以在少量改动原有项⽬的情况下⼤幅度地提⾼整个程序的运⾏效率。18.并⾏编程因为GIL的存在,Python很难充分利⽤多核CPU的优势。但是,可以通过内置的模块multiprocessing实现下⾯⼏种并⾏模式:多进程:对于CPU密集型的程序,可以使⽤multiprocessing的Process,Pool等封装好的类,通过多进程的⽅式实现并⾏计算。但是因为进程中的通信成本⽐较⼤,对于进程之间需要⼤量数据交互的程序效率未必有⼤的提⾼。多线程:对于IO密集型的程序,模块使⽤multiprocessing的接⼝封装threading,使得多线程编程也变得⾮常轻松(⽐如可以使⽤Pool的map接⼝,简洁⾼效)。分布式:multiprocessing中的Managers类提供了可以在不同进程之共享数据的⽅式,可以在此基础上开发出分布式的程序。不同的业务场景可以选择其中的⼀种或⼏种的组合实现程序性能的优化。19.终级⼤杀器:PyPyPyPy是⽤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扩展。随着STM,Numpy等项⽬的完善,相信PyPy将会替代CPython。20.使⽤性能分析⼯具除了上⾯在ipython使⽤到的timeit模块,还有cProfile。cProfile的使⽤⽅式也⾮常简单: python -m , 是要运⾏程序的⽂件名,可以在标准输出中看到每⼀个函数被调⽤的次数和运⾏的时间,从⽽找到程序的性能瓶颈,然后可以有针对性地优化。来⾃:SegmentFault

发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1688931817a184865.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信