Python鳕鱼的增长速度

2024-04-25 08:07:38 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一些python代码有很多类。我使用cProfile发现运行程序的总时间是68秒。我发现在一个名为Buyers的类中,下面的函数花费了68秒中的60秒。我必须运行程序大约100次,所以任何速度的提高都会有帮助。你能建议一些通过修改代码来提高速度的方法吗?如果你需要更多有用的信息,请告诉我。在

def qtyDemanded(self, timePd, priceVector):
    '''Returns quantity demanded in period timePd. In addition,
    also updates the list of customers and non-customers.

    Inputs: timePd and priceVector
    Output: count of people for whom priceVector[-1] < utility
    '''

    ## Initialize count of customers to zero
    ## Set self.customers and self.nonCustomers to empty lists
    price = priceVector[-1]
    count = 0
    self.customers = []
    self.nonCustomers = []


    for person in self.people:
        if person.utility >= price:             
            person.customer = 1
            self.customers.append(person)
        else:
            person.customer = 0
            self.nonCustomers.append(person)

    return len(self.customers)

self.peopleperson对象的列表。每个person都有customer和{}作为其属性。在

编辑-已添加响应

---------------------------------------

非常感谢你的建议。这是 对人们提出的一些问题和建议作出回应 制造的。我没有试过他们所有的,但会尝试其他人和回信稍后。在

(1)@琥珀色-该功能被访问80000次。在

(2)@gnibbler等人-自我。人是内存中Person对象的列表。未连接到数据库。在

(3)@休·博思韦尔

原函数占用的累计时间-60.8秒(访问80000次)

使用建议的本地函数别名的新函数获取的cumtime-56.4 s(访问80000次)

(4)@rotoglup和@Martin Thomas

我还没有试过你的解决办法。我需要检查代码的其余部分以查看我使用的位置自己的顾客在我改变不把顾客附加到自己的顾客列表。但我会试试这个然后回信。在

(5)@TryPyPy-感谢您提供的检查代码。在

让我先读一下你提出的建议,看看这些建议是否可行。在

编辑2 有人建议,由于我在self.people中标记客户和非客户,我应该尝试不使用append创建self.customers和{}的单独列表。相反,我应该遍历self.people来查找客户数量。我尝试了以下代码,并对f_w_append和{}下面的两个函数计时。我确实发现后者花费的时间更少,但仍然是前者所用时间的96%。也就是说,它只是速度的一个很小的提升。在

@TryPyPy-下面的代码足够完整,可以检查瓶颈函数,以防您的报价仍然存在,以便与其他编译器一起检查它。在

再次感谢所有回复的人。在

^{pr2}$

编辑3:似乎问题出在numpy

这是对johnmachin下面所说的回应。下面您将看到两种定义Population类的方法。我运行了下面的程序两次,一次使用创建Population类的每种方法。一个使用numpy,一个不使用numpy。一个没有纽比的人所花的时间和约翰在跑步中发现的时间相似。一个有纽比的需要更长的时间。我不清楚的是,popn实例是在时间记录开始之前创建的(至少这是从代码中显示的)。那么,为什么新版本要花更长的时间呢。而且,我觉得纽比应该更有效率。不管怎么说,问题似乎出在numpy上,而不是append,尽管它确实会让事情慢一点。有人能用下面的代码确认一下吗?谢谢。在

import random # instead of numpy
import numpy
import time
timer_func = time.time # using Mac OS X 10.5.8

class Person(object):
    def __init__(self, util):
        self.utility = util
        self.customer = 0

class Population(object):
    def __init__(self, numpeople):
        random.seed(1)
        self.people = [Person(random.uniform(0, 300)) for i in xrange(numpeople)]
        self.cus = []
        self.noncus = []   

# Numpy based    
# class Population(object):
#     def __init__(self, numpeople):
#         numpy.random.seed(1)
#         utils = numpy.random.uniform(0, 300, numpeople)
#         self.people = [Person(u) for u in utils]
#         self.cus = []
#         self.noncus = []    


def f_wo_append(popn):
    '''Function without append'''
    P = 75
    for per in popn.people:
        if  per.utility >= P:
            per.customer = 1
        else:
            per.customer = 0

    numcustomers = 0
    for per in popn.people:
        if per.customer == 1:
            numcustomers += 1                
    return numcustomers



t0 = timer_func()
for i in xrange(20000):
    x = f_wo_append(popn)
t1 = timer_func()
print t1-t0

编辑4:查看John Machin和Trypy的答案

因为这里有这么多的编辑和更新,那些第一次在这里发现自己的人可能有点困惑。请看约翰·马钦和特雷皮的答案。这两种方法都有助于显著提高代码的速度。我很感谢他们和其他人,他们提醒我注意append的缓慢。因为在这个例子中,我将使用johnmachin的解决方案,而不是使用numpy来生成实用程序,所以我接受他的回答作为答案。不过,我真的很感激崔比指出的方向。


Tags: 函数代码inselfnumpyfordef时间
3条回答

可以使用本地函数别名来消除某些查找:

def qtyDemanded(self, timePd, priceVector):
    '''Returns quantity demanded in period timePd. In addition,
    also updates the list of customers and non-customers.

    Inputs: timePd and priceVector
    Output: count of people for whom priceVector[-1] < utility
    '''
    price = priceVector[-1]
    self.customers = []
    self.nonCustomers = []

    # local function aliases
    addCust = self.customers.append
    addNonCust = self.nonCustomers.append

    for person in self.people:
        if person.utility >= price:             
            person.customer = 1
            addCust(person)
        else:
            person.customer = 0
            addNonCust(person)

    return len(self.customers)

请考虑缩减您的f_wo_append函数:

def f_wo_append():
    '''Function without append'''
    P = 75
    numcustomers = 0
    for person in popn.people:
        person.customer = iscust = person.utility >= P
        numcustomers += iscust
    return numcustomers

编辑回应OP的评论“”“这让事情变得更糟!修剪后的版本比我在上面发布的版本花费的时间多4倍。”“”在

这是不可能的,可以采取“4倍以上”(5倍?)。。。这是我的代码,它演示了“withoutappend”情况的显著减少,正如我所建议的,还引入了“withappend”情况的显著改进。在

^{pr2}$

下面是运行它的结果(Python 2.7.1,Windows 7 Pro,“Intel Core i3 CPU 540@3.07 GHz”):

C:\junk>\python27\python ncust.py 300 0 80000
popsize=300 func=f_w_append niter=80000 nc=218 seconds=5.48

C:\junk>\python27\python ncust.py 300 1 80000
popsize=300 func=f_w_append2 niter=80000 nc=218 seconds=4.62

C:\junk>\python27\python ncust.py 300 2 80000
popsize=300 func=f_wo_append niter=80000 nc=218 seconds=5.55

C:\junk>\python27\python ncust.py 300 3 80000
popsize=300 func=f_wo_append2 niter=80000 nc=218 seconds=4.29

编辑3为什么numpy需要更长时间:

>>> import numpy
>>> utils = numpy.random.uniform(0, 300, 10)
>>> print repr(utils[0])
42.777972538362874
>>> type(utils[0])
<type 'numpy.float64'>

这就是为什么我的f_wo_append2函数花费了4倍的时间:

>>> x = utils[0]
>>> type(x)
<type 'numpy.float64'>
>>> type(x >= 75) 
<type 'numpy.bool_'> # iscust refers to a numpy.bool_
>>> type(0 + (x >= 75)) 
<type 'numpy.int32'> # numcustomers ends up referring to a numpy.int32
>>>

经验主义的证据是,这些自定义类型在用作标量时并不是很快。。。可能是因为每次使用时都需要重置浮点硬件。对于大数组,不是标量。在

你在使用其他的numpy功能吗?如果没有,就使用random模块。如果numpy有其他用途,您可能希望在填充设置期间将numpy.float64强制为float。在

在优化Python代码以提高速度之后,您可以尝试许多事情。如果这个程序不需要C扩展,可以在PyPy下运行它,以从它的JIT编译器中获益。您可以尝试为可能的huge speedups生成一个C extension。{A4}甚至允许您将Python程序转换为独立的C++二进制代码。在

如果你能为基准测试提供足够的代码,我愿意在这些不同的优化场景下为你的程序计时

编辑:首先,我必须同意其他所有人的观点:你确定你测量时间正确吗?示例代码在0.1秒内运行了100次,因此很有可能是时间错误,或者是您遇到了瓶颈(IO?)代码示例中不存在。在

也就是说,我做了30万人,所以时间是一致的。以下是由CPython(2.5)、PyPy和Shed Skin共享的改编代码:

from time import time
import random
import sys


class person(object):
    def __init__(self, util):
        self.utility = util
        self.customer = 0


class population(object):
    def __init__(self, numpeople, util):
        self.people = []
        self.cus = []
        self.noncus = []
        for u in util:
            per = person(u)
            self.people.append(per)


def f_w_append(popn):
    '''Function with append'''
    P = 75
    cus = []
    noncus = []
    # Help CPython a bit
    # cus_append, noncus_append = cus.append, noncus.append
    for per in popn.people:
        if  per.utility >= P:
            per.customer = 1
            cus.append(per)
        else:
            per.customer = 0
            noncus.append(per)
    return len(cus)


def f_wo_append(popn):
    '''Function without append'''
    P = 75
    for per in popn.people:
        if  per.utility >= P:
            per.customer = 1
        else:
            per.customer = 0

    numcustomers = 0
    for per in popn.people:
        if per.customer == 1:
            numcustomers += 1
    return numcustomers


def main():
    try:
        numpeople = int(sys.argv[1])
    except:
        numpeople = 300000

    print "Running for %s people, 100 times." % numpeople

    begin = time()
    random.seed(1)
    # Help CPython a bit
    uniform = random.uniform
    util = [uniform(0.0, 300.0) for _ in xrange(numpeople)]
    # util = [random.uniform(0.0, 300.0) for _ in xrange(numpeople)]

    popn1 = population(numpeople, util)
    start = time()
    for _ in xrange(100):
        r = f_wo_append(popn1)
    print r
    print "Without append: %s" % (time() - start)


    popn2 = population(numpeople, util)
    start = time()
    for _ in xrange(100):
        r = f_w_append(popn2)
    print r
    print "With append: %s" % (time() - start)

    print "\n\nTotal time: %s" % (time() - begin)

if __name__ == "__main__":
    main()

使用PyPy运行与使用CPython运行一样简单,只需键入“PyPy”而不是“python”。对于SUPE外壳,必须转换为C++,编译和运行:

^{pr2}$

下面是Cython-ized代码:

from time import time
import random
import sys


cdef class person:
    cdef readonly int utility
    cdef public int customer

    def __init__(self, util):
        self.utility = util
        self.customer = 0


class population(object):
    def __init__(self, numpeople, util):
        self.people = []
        self.cus = []
        self.noncus = []
        for u in util:
            per = person(u)
            self.people.append(per)


cdef int f_w_append(popn):
    '''Function with append'''
    cdef int P = 75
    cdef person per
    cus = []
    noncus = []
    # Help CPython a bit
    # cus_append, noncus_append = cus.append, noncus.append

    for per in popn.people:
        if  per.utility >= P:
            per.customer = 1
            cus.append(per)
        else:
            per.customer = 0
            noncus.append(per)
    cdef int lcus = len(cus)
    return lcus


cdef int f_wo_append(popn):
    '''Function without append'''
    cdef int P = 75
    cdef person per
    for per in popn.people:
        if  per.utility >= P:
            per.customer = 1
        else:
            per.customer = 0

    cdef int numcustomers = 0
    for per in popn.people:
        if per.customer == 1:
            numcustomers += 1
    return numcustomers


def main():

    cdef int i, r, numpeople
    cdef double _0, _300
    _0 = 0.0
    _300 = 300.0

    try:
        numpeople = int(sys.argv[1])
    except:
        numpeople = 300000

    print "Running for %s people, 100 times." % numpeople

    begin = time()
    random.seed(1)
    # Help CPython a bit
    uniform = random.uniform
    util = [uniform(_0, _300) for i in xrange(numpeople)]
    # util = [random.uniform(0.0, 300.0) for _ in xrange(numpeople)]

    popn1 = population(numpeople, util)
    start = time()
    for i in xrange(100):
        r = f_wo_append(popn1)
    print r
    print "Without append: %s" % (time() - start)


    popn2 = population(numpeople, util)
    start = time()
    for i in xrange(100):
        r = f_w_append(popn2)
    print r
    print "With append: %s" % (time() - start)

    print "\n\nTotal time: %s" % (time() - begin)

if __name__ == "__main__":
    main()

为了建造它,有一个设置.py像这个:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [Extension("cymakefaster", ["makefaster.pyx"])]

setup(
  name = 'Python code to speed up',
  cmdclass = {'build_ext': build_ext},
  ext_modules = ext_modules
)

您可以使用以下方法构建它: pythonsetupfaster.py构建扩展--就地

然后测试: python-c“导入cymak更快;打印cymak更快。文件;cymakeester.main()”

每个版本的计时运行了五次,其中Cython是最快和最容易使用的代码生成器(shedskin的目标是更简单,但隐藏的错误消息和隐式静态类型使其更难使用)。至于最佳值,PyPy在没有代码更改的情况下,在计数器版本中提供了令人印象深刻的加速。在

#Results (time in seconds for 30000 people, 100 calls for each function):
                  Mean      Min  Times    
CPython 2.5.2
Without append: 35.037   34.518  35.124, 36.363, 34.518, 34.620, 34.559
With append:    29.251   29.126  29.339, 29.257, 29.259, 29.126, 29.272
Total time:     69.288   68.739  69.519, 70.614, 68.746, 68.739, 68.823

PyPy 1.4.1
Without append:  2.672    2.655   2.655,  2.670,  2.676,  2.690,  2.668
With append:    13.030   12.672  12.680, 12.725, 14.319, 12.755, 12.672
Total time:     16.551   16.194  16.196, 16.229, 17.840, 16.295, 16.194

Shed Skin 0.7 (gcc -O2)
Without append:  1.601    1.599   1.599,  1.605,  1.600,  1.602,  1.599
With append:     3.811    3.786   3.839,  3.795,  3.798,  3.786,  3.839
Total time:      5.704    5.677   5.715,  5.705,  5.699,  5.677,  5.726

Cython 0.14 (gcc -O2)
Without append:  1.692    1.673   1.673,  1.710,  1.678,  1.688,  1.711
With append:     3.087    3.067   3.079,  3.080,  3.119,  3.090,  3.067
Total time:      5.565    5.561   5.562,  5.561,  5.567,  5.562,  5.572

编辑:a和更有意义的计时,针对80000个电话,每个电话300人:

Results (time in seconds for 300 people, 80000 calls for each function):
                  Mean      Min  Times
CPython 2.5.2
Without append: 27.790   25.827  25.827, 27.315, 27.985, 28.211, 29.612
With append:    26.449   24.721  24.721, 27.017, 27.653, 25.576, 27.277
Total time:     54.243   50.550  50.550, 54.334, 55.652, 53.789, 56.892


Cython 0.14 (gcc -O2)
Without append:  1.819    1.760   1.760,  1.794,  1.843,  1.827,  1.871
With append:     2.089    2.063   2.100,  2.063,  2.098,  2.104,  2.078
Total time:      3.910    3.859   3.865,  3.859,  3.944,  3.934,  3.951

PyPy 1.4.1
Without append:  0.889    0.887   0.894,  0.888,  0.890,  0.888,  0.887
With append:     1.671    1.665   1.665,  1.666,  1.671,  1.673,  1.681
Total time:      2.561    2.555   2.560,  2.555,  2.561,  2.561,  2.569

Shed Skin 0.7 (g++ -O2)
Without append:  0.310    0.301   0.301,  0.308,  0.317,  0.320,  0.303
With append:     1.712    1.690   1.733,  1.700,  1.735,  1.690,  1.702
Total time:      2.027    2.008   2.035,  2.008,  2.052,  2.011,  2.029

脱皮最快,PyPy超过Cython。与CPython相比,这三种方法都能大大加快速度。在

相关问题 更多 >