提高Python性能的一些建议(一)

最近换住的地方,网费到期,有两个星期没更新博客了,博客还是要坚持写的,有时候工作时遇到了相关问题,查看相关博客,还是能够得到一些思路或者灵感。虽然写篇博客要话费不少时间(我一般要花一个半小时到两个小时之间),但是这中间码字呀、归纳总结的过程还是让我受益匪浅的,温故而知新!当然分享自己的学习心得,也会让自己认识一些志同道合的朋友,也挺好。不说许多,今天讲讲如何提高Python性能的问题。

python的性能相对c语言等还是有一定的劣势,但是如果能掌握一些优化性能的技巧,不仅能够提高代码的运行效率,还能够使代码更加Pythonic。刚刚接触Python时,也在网上找了一些提高`python性能的博文看,还是另外受益匪浅,http://www.jb51.net/article/56699.htm

这篇博文还是写的不错的,可以参考。不过结合我自己最近看的书及书上的建议,做一个更细致的总结吧,不正之处欢迎批评指正,共同进步!

一、循环优化的基本技巧

循环的优化应遵循尽量减少循环过程中计算量的原则,多重循环的情况下尽量将内存的计算提到上一层。

(1)减少循环内部计算。先看下面例子

#coding=utf-8
import datetime
import math

#第一种方法
def fun1(iter,x):
    j = 0
    for i in iter:
        d = math.sqrt(x)
        j += i * d
    return j

#第二中方法
def fun2(iter,x):
    j = 0
    d = math.sqrt(x)
    for i in iter:
        j += i * d
    return j

iter = range(1,1000)
t0 = datetime.datetime.now()
a = fun1(iter,9)
t1 = datetime.datetime.now()
b = fun2(iter,9)
t2 = datetime.datetime.now()
print a,"  ",b
print t1-t0,t2-t1

运行结果如下图:

第二种方法比一种速度快,因为第一种方法中,d = math.sqrt(x)在循环内部,每次循环过程中都会重复计算一次,增加了系统开销,这里的d = math.sqrt(x)还是个比较简单的计算,如果遇到自己定义的复杂的、计算量大的那么第一种方法真的就不太好了。一般情况下,第二种方法比第一种方法运算效率高40%-60%。

(2)将显示循环改为隐式循环。比如说,在求等差数列的和时,可以直接通过循环来计算,这个很简单,如下:

#coding=utf-8
def SUM(n):
sum = 0
for i in xrange(n+1):
    sum +=i

这么写是没问题的,但是等差数列有现成的求和公式呀,即n*(n+1)/2,没必要要再用循环了,所以说如果程序中有类似的情况,可以直接将显示循环改为隐式。

(3)在循环中尽量引用局部变量。根据"LEGB"原则(不了解的可以参考之前的博文,地址:http://11026142.blog.51cto.com/11016142/1840128  ),在命名空间局部变量优先搜索,因此局部变量查询会比全局变量更快。在循环中,如果多次引用某一变量,要尽量将其转化为局部变量,看下面例子

#coding=utf-8
import datetime
import math
x = [10,20,30,40,50,60,70,80,90]
#第一种方法
def fun1(x):
    for i in xrange(len(x)):
        x[i] = math.sin(x[i])
    return x

#第二中方法
def fun2(x):
    loc_sin = math.sin
    for i in xrange(len(x)):
        x[i] = loc_sin(x[i])
    return x

t0 = datetime.datetime.now()
a = fun1(x)
t1 = datetime.datetime.now()
b = fun2(x)
t2 = datetime.datetime.now()
print a
print b
print t1-t0,t2-t1

运行结果如下:

可以看到方法二快于方法一,我觉得这种方法,在平时应用某个库(包括标准库和自定义库,模块)的函数时,可以这样用,比较程序搜索这个库的函数也是需要时间,如果用第一种方法,那就是循环搜索了,那肯定会划分更多的时间,这个技巧我觉得值得大家去学习、借鉴。

(4)对于嵌套循环,尽量将内层循环计算往上层移。看下面例子

#coding=utf-8
import datetime

#第一种方法
def fun1(iter1,iter2):
    max1 = 0
    for i in range(len(iter1)):
        for j in range(len(iter2)):
            x = iter1[i] + iter2[j]
            max1 = x if x > max1 else max1
    return max1

#第二中方法
def fun2(iter1,iter2):
    max1 = 0
    for i in range(len(iter1)):
        temp = iter1[i]
        for j in range(len(iter2)):
            x = temp + iter2[j]
            max1 = x if x > max1 else max1
    return max1
l1 = [1,23,4,5,34,8,10,18,42,10,6,88]
l2 = [100,102,34,15,16,56]
t0 = datetime.datetime.now()
a = fun1(l1,l2)
t1 = datetime.datetime.now()
b = fun2(l1,l2)
t2 = datetime.datetime.now()
print a
print b
print t1-t0,t2-t1

运行结果如下:

可见方法二的速度要快些,嵌套for循环的运行机制是i=0(以上面例子为例),然后j从0增到最大值,然后i自增1,j又从0增大到最大值,依次类推。所以在内层循环的上一层加个临时变量,内层使用是就不用重新计算。这里要说明一下,对于列表它的索引,切片等操作也是一种计算/运算,也是要花费时间的。

二、使用不同的数据结构优化性能

最常用的数据结构是list,它的内存管理类似于c++的vector,即先预分配一定数量的内存,当预分配的内存用完了但是不够用,又要继续往里插入元素,就会启动新一轮内存分配,list对象就会根据内存增长算法重新申请一块更大的内存空间,然后将原有的所有元素拷贝过去,销毁之前的内存,然后插入新元素,如果还不够用,继续重复上面的步骤。删除元素也是这样,如果发现已用空间比预分配内存空间的一半还少,list会申请一块小内存,再做一次拷贝,然后销毁大内存(如果想了解这部分知识,我推荐大家看看《Python源码剖析》这本书,这本书我也是一个月前开始看,目前还没看完,我觉得这本书写的很好,很值得读两三篇甚至四五篇,第一章与第二章内容要认真读,否则后面的东西越看越糊涂(大神除外))。

可见,如果list对象经常有元素数量的巨大变化,而且比较频繁,这个时候应该考虑使用deque。如果不了解deque,可以参考我前面的博文http://11026142.blog.51cto.com/11016142/1851791

deque是双端队列,同时具备栈和队列的特性。能够提供复杂度为O(1)的两端插入和删除操作。相对于list,它最大的优势在于内存管理。它申请的内存不够用时,不会像list那样,而是申请新的内存来容纳新的元素,然后将新元素与旧元素连接起来,避免了元素的拷贝。所以,但出现元素数量频繁出现巨大变化时,deque的性能是list的好几倍。

array,中文名是数组,是一种序列数据结构,看起来和list很相似,但是所有成员必须是相同基本类型。array实例化时需要指明其存储元素类型。如‘c‘,表示存储一个想当然c语言里的char类型,占用内存大小1字节。这就从另一个角度来说明,它可以优化代码的内存空间。看下面例子

#coding=utf-8
import sys
import array
a = array.array(‘c‘,"hello,world")

c = list("hello,world")
print sys.getsizeof(a),sys.getsizeof(c)

运行结果如下:

明显,list对象更耗内存。这就会影响到一些其它操作的性能提升,比如将容器对象转换为字符串,在这一点上array性能高于list。看下面例子

#coding=utf-8
import array
import datetime
a = array.array(‘c‘,"hello,world")
c = list("hello,world")
t0 = datetime.datetime.now()
s1 = ‘‘.join(c)
t1= datetime.datetime.now()
s2 = ‘‘.join(a)
t2 = datetime.datetime.now()
print t1 - t0,t2 - t1

运行结果如下:

也并不是所以的array性能提升比较大,比如排序,array性能不如list,看下例:

#coding=utf-8
import array
import datetime
a = array.array(‘c‘,"hello,world")
c = list("hello,world")
t0 = datetime.datetime.now()
c.reverse()
t1= datetime.datetime.now()
a.reverse()
t2 = datetime.datetime.now()
print t1 - t0,t2 - t1

结果如下:

(三)利用好set的优势

set是集合,python中集合是通过Hash算法实现的无序不重复元素集,创建集合是通过set()来实现的。看下图:

set对象也支持添加元素,但它的性能是list添加元素性能的好几倍,这个不在此过多叙述,有时间专门写篇关于python集合的博文。

set在求交集、并集、差集等与集合有关的操作,性能要逼list快,因此涉及到list的交集、并集、差集等运算,可以将list转换为set

好了,今天就到这里,明天会继续总结生成器、进程、线程(池)等对python性能提高的一些小建议。

时间: 2024-10-05 22:49:54

提高Python性能的一些建议(一)的相关文章

如何提高python性能

在Python解析器中输入 import this. 一个犀利的Python新手可能会注意到"解析"一词, 认为Python不过是另一门脚本语言. "它肯定很慢!" 毫无疑问:Python程序没有编译型语言高效快速. 甚至Python拥护者们会告诉你Python不适合这些领域. 然而,YouTube已用Python服务于每小时4千万视频的请求. 你所要做的就是编写高效的代码和需要时使用外部实现(C/C++)代码. 这里有一些建议,可以帮助你成为一个更好的Python

提高mapreduce性能的七点建议

Cloudera提供给客户的服务内容之一就是调整和优化MapReduce job执行性能.MapReduce和HDFS组成一个复杂的分布式系统,并且它们运行着各式各样用户的代码,这样导致没有一个快速有效的规则来实现优化 代码性能的目的.在我看来,调整cluster或job的运行更像一个医生对待病人一样,找出关键的“症状”,对于不同的症状有不同的诊断和处理方式. 在医学领域,没有什么可以代替一位经验丰富的医生:在复杂的分布式系统上,这个道理依然正确—有经验的用户和操作者在面对很多常见问题上都会有“

Python成为编程语言中的第一!送你20条Python性能优化的建议!

1.优化算法时间复杂度 算法的时间复杂度对程序的执行效率影响最大,在Python中可以通过选择合适的数据结构来优化时间复杂度,如list和set查找某一个元素的时间复杂度分别是O(n)和O(1).不同的场景有不同的优化方式,总得来说,一般有分治,分支界限,贪心,动态规划等思想. timeit后面的-n表示运行的次数,后两行对应的是两个timeit的输出,下同.由此可见后者慢一个数量级. 进群:548377875  即可获取数十套PDF哦! 但是对于需要循环遍历的情况: 对于内存不是非常大的lis

Python性能鸡汤(转)

英文原文:http://blog.monitis.com/index.php/2012/02/13/python-performance-tips-part-1/ 英文原文:http://blog.monitis.com/index.php/2012/03/21/python-performance-tips-part-2/ 译文:http://www.oschina.net/question/1579_45822 Python是解释型语言,因此它的执行效率不高 [1] ,但这并不影响它的流行.

Python性能(转)

第一部分 阅读 Zen of Python,在Python解析器中输入 import this. 一个犀利的Python新手可能会注意到"解析"一词, 认为Python不过是另一门脚本语言. "它肯定很慢!" 毫无疑问:Python程序没有编译型语言高效快速. 甚至Python拥护者们会告诉你Python不适合这些领域. 然而,YouTube已用Python服务于每小时4千万视频的请求. 你所要做的就是编写高效的代码和需要时使用外部实现(C/C++)代码. 这里有一

Python性能鸡汤

第一部分 阅读 Zen of Python,在Python解析器中输入 import this. 一个犀利的Python新手可能会注意到"解析"一词, 认为Python不过是另一门脚本语言. "它肯定很慢!" 毫无疑问:Python程序没有编译型语言高效快速. 甚至Python拥护者们会告诉你Python不适合这些领域. 然而,YouTube已用Python服务于每小时4千万视频的请求. 你所要做的就是编写高效的代码和需要时使用外部实现(C/C++)代码. 这里有一

Python性能优化(转)

分成两部分:代码优化和工具优化 原文:http://my.oschina.net/xianggao/blog/102600 阅读 Zen of Python,在Python解析器中输入 import this. 一个犀利的Python新手可能会注意到"解析"一词, 认为Python不过是另一门脚本语言. "它肯定很慢!" 毫无疑问:Python程序没有编译型语言高效快速. 甚至Python拥护者们会告诉你Python不适合这些领域. 然而,YouTube已用Pyth

QtQml 应用程序的性能考虑与建议(来自小V的翻译)

QtQml 应用程序的性能考虑与建议 原文:csdn aidear_evo QtQml应用程序的性能考虑与建议 本文翻译自Qt官网文档:http://doc.qt.io/qt-5/qtquick-performance.html 时间考虑 作为一名程序开发者,应该努力使渲染引擎的刷新率维持在60fps,也就是说在每帧之间大约有16ms,这段时间包括了基本图元在图形硬件上的描画.具体内容如下: 尽可能的使用异步事件驱动来编程. 使用工作者线程来处理重要的事情,比如说QML的WorkerScript

Qml应用程序的性能考虑与建议

本文翻译自Qt官网文档: http://doc.qt.io/qt-5/qtquick-performance.html QtQml应用程序的性能考虑与建议 1.时间考虑 作为一名程序开发者,应该努力使渲染引擎的刷新率维持在60fps,也就是说在每帧之间大约有16ms,这段时间包括了基本图元在图形硬件上的描画.具体内容如下: >尽可能的使用异步事件驱动来编程. >使用工作者线程来处理重要的事情,比如说QML的WorkerScript类型就是起用了一个新的线程. >不要手动重复事件循环. &