python 性能提升的几种方法
关于python性能提升的一些方案。
一、函数调用优化(空间跨度,避免访问内存)
程序的优化核心点在于尽量减少操作跨度,包括代码执行时间上的跨度以及内存中空间跨度。
1.大数据求和,使用sum
a=range(100000) %timeit-n10sum(a) 10loops,bestof3:3.15msperloop %%timeit ...:s=0 ...:foriina: ...:s+=i ...: 100loops,bestof3:6.93msperloop
2.小数据求和,避免使用sum
%timeit-n1000s=a+b+c+d+e+f+g+h+i+j+k#数据量较小时直接累加更快 1000loops,bestof3:571nsperloop %timeit-n1000s=sum([a,b,c,d,e,f,g,h,i,j,k])#小数据量调用sum函数,空间效率降低 1000loops,bestof3:669nsperloop
结论:大数据求和sum效率高,小数据求和直接累加效率高。
二、for循环优化之取元素(使用栈或寄存器,避免访问内存)
forlstin[(1,2,3),(4,5,6)]:#lst索引需要额外开销 pass
应尽量避免使用索引。
fora,b,cin[(1,2,3),(4,5,6)]:#better pass
相当于给每一个元素直接赋值。
defforce(): lst=range(4) fora1in[1,2]: fora2inlst: fora3inlst: forb1inlst: forb2inlst: forb3inlst: forc1inlst: forc2inlst: forc3inlst: ford1inlst: yield(a1,a2,a3,b1,b2,b3,c1,c2,c3,d1) %%timeit-n10 fortinforce(): sum([t[0],t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8],t[9]]) 10loops,bestof3:465msperloop %%timeit-n10 fora1,a2,a3,b1,b2,b3,c1,c2,c3,d1inforce(): sum([a1,a2,a3,b1,b2,b3,c1,c2,c3,d1]) 10loops,bestof3:360msperloop
三、生成器优化(查表代替运算)
defforce(start,end):#用于密码暴力破解程序 foriinrange(start,end): now=i sublst=[] forjinrange(10): sublst.append(i%10)#除法运算开销较大,比乘法大 i//=10 sublst.reverse() yield(tuple(sublst),now)
defforce():#better lst=range(5) fora1in[1]: fora2inlst: fora3inlst: forb1inlst: forb2inlst: forb3inlst: forc1inlst: forc2inlst: forc3inlst: ford1inlst: yield(a1,a2,a3,b1,b2,b3,c1,c2,c3,d1)
r0=[1,2]#可读性与灵活性 r1=range(10) r2=r3=r4=r5=r6=r7=r8=r9=r1 force=((a0,a1,a2,a3,a4,a5,a6,a7,a8,a9) fora0inr0fora1inr1fora2inr2fora3inr3fora4inr4 fora5inr5fora6inr6fora7inr7fora8inr8fora9inr9)
四、幂运算优化(pow(x,y,z))
defisprime(n): ifn&1==0: returnFalse k,q=find_kq(n) a=randint(1,n-1) ifpow(a,q,n)==1:#比使用a**q%n运算优化数倍 returnTrue forjinrange(k): ifpow(a,pow(2,j)*q,n)==n-1:#a**((2**j)*q)%n returnTrue returnFalse
结论:pow(x,y,z)优于x**y%z.
五、除法运算优化
In[1]:fromrandomimportgetrandbits In[2]:x=getrandbits(4096) In[3]:y=getrandbits(2048) In[4]:%timeit-n10000q,r=divmod(x,y) 10000loops,bestof3:10.7usperloop In[5]:%timeit-n10000q,r=x//y,x%y 10000loops,bestof3:21.2usperloop
结论:divmod优于//和%。
六、优化算法时间复杂度
算法的时间复杂度对程序的执行效率影响最大,在python中可以选择合适的数据结构来优化时间复杂度,如list和set查找某一个元素的时间复杂度分别是O(n)和O(1)。不同场景有不同的优化方式,总的来说,一般有分治,分支定界、贪心动态规划等思想。
七、合理使用copy和deepcopy
对于dict和list等数据结构的对象,直接赋值使用的是引用的方式。而有些情况下需要复制整个对象,这时可以使用copy包里的copy和deepcopy,这两个函数的不同之处在于deepcopy是递归复制的。效率不同:
In[23]:importcopy In[24]:%timeit-n10copy.copy(a) 10loops,bestof3:606nsperloop In[25]:%timeit-n10copy.deepcopy(a) 10loops,bestof3:1.17usperloop
timeit后面的-n表示运行的次数,后两行对应的是两个timeit的输出,下同。由此可见后者慢一个数量级。
关于copy的一个例子:
>>>lists=[[]]*3 >>>lists [[],[],[]] >>>lists[0].append(3) >>>lists [[3],[3],[3]]
发生的事情是这样的,[[]]是包含一个空列表的只有一个元素的列表,所以[[]]*3的所有三个元素都是(指向)这个空列表。修改lists的任何元素都修改这个列表。修改效率高。
八、使用dict或set查找元素
python字典和集合都是使用hash表来实现(类似c++标准库unordered_map),查找元素的时间复杂度是O(1)。
In[1]:r=range(10**7) In[2]:s=set(r)#占用588MB内存 In[3]:d=dict((i,1)foriinr)#占用716MB内存 In[4]:%timeit-n10000(10**7)-1inr 10000loops,bestof3:291nsperloop In[5]:%timeit-n10000(10**7)-1ins 10000loops,bestof3:121nsperloop In[6]:%timeit-n10000(10**7)-1ind 10000loops,bestof3:111nsperloop
结论:set的内存占用量最小,dict运行时间最短。
九、合理使用(generator)和yield(节省内存)
In[1]:%timeit-n10a=(iforiinrange(10**7))#生成器通常遍历更高效 10loops,bestof3:933nsperloop In[2]:%timeit-n10a=[iforiinrange(10**7)] 10loops,bestof3:916msperloop In[1]:%timeit-n10forxin(iforiinrange(10**7)):pass 10loops,bestof3:749msperloop In[2]:%timeit-n10forxin[iforiinrange(10**7)]:pass 10loops,bestof3:1.05sperloop
结论:尽量使用生成器去遍历。
以上就是对python性能提升的一些方案,后续继续补充,需要的可以看下。