2011-01-11 3 views
14

У меня есть код python, который имеет много классов. Я использовал cProfile, чтобы узнать, что общее время запуска программы составляет 68 секунд. Я обнаружил, что следующая функция в классе под названием Buyers занимает около 60 секунд этих 68 секунд. Мне нужно запустить программу примерно 100 раз, поэтому любое увеличение скорости поможет. Можете ли вы предложить способы увеличить скорость, изменив код? Если вам нужна дополнительная информация, которая поможет, сообщите мне.Увеличение скорости кода python

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.people список person объектов. Каждый из person имеет customer и utility в качестве своих атрибутов.

EDIT - responsed добавил

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

Большое спасибо за предложения. Вот ответ на некоторые вопросы и предложения, которые люди любезно делают . Я не пробовал их всех, но попробую других и напишу позже.

(1) @amber - функция доступна 80 000 раз.

(2) @gnibbler и другие - self.people - это список объектов Person в памяти. Не подключен к базе данных.

(3) @Hugh Ботвелл

cumtime принятые исходной функции - 60,8 с (доступ к 80000 раз)

cumtime, принятого новой функции с локальными функциональными псевдонимами как предложено - 56,4 с (доступ 80000 раз)

(4) @rotoglup и @Martin Томас

Я не пробовал свои решения еще. Мне нужно проверить остальную часть кода, чтобы увидеть места, где я использую self.customers, прежде чем я смогу внести изменения, не добавляя клиентов в список self.customers. Но я попробую это и напишу.

(5) @TryPyPy - спасибо за ваше любезное предложение проверить код.

Позвольте мне вначале немного ознакомиться с предложениями, которые вы сделали, чтобы убедиться, что это будет возможно использовать.

EDIT 2 Некоторые предположили, что поскольку я маркировка клиентов и noncustomers в self.people, я должен попробовать без создания отдельных списков self.customers и self.noncustomers с помощью добавления. Вместо этого я должен перебрать self.people, чтобы найти количество клиентов. Я попробовал следующий код и приурочил обе функции ниже f_w_append и f_wo_append. Я обнаружил, что последнее занимает меньше времени, но это еще 96% времени, затраченного первым. То есть, это очень небольшое увеличение скорости.

@TryPyPy - Следующий фрагмент кода достаточно полно, чтобы проверить функцию узкого места, если ваше предложение все еще существует, чтобы проверить его с другими компиляторами.

Еще раз спасибо всем, кто ответил.

import numpy 

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

class population(object): 
    def __init__(self, numpeople): 
     self.people = [] 
     self.cus = [] 
     self.noncus = [] 
     numpy.random.seed(1) 
     utils = numpy.random.uniform(0, 300, numpeople) 
     for u in utils: 
      per = person(u) 
      self.people.append(per) 

popn = population(300) 

def f_w_append(): 
    '''Function with append''' 
    P = 75 
    cus = [] 
    noncus = [] 
    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(): 
    '''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 

EDIT 3: Кажется NumPy проблема

Это в ответ на то, что ниже, сказал Джон Мачин. Ниже вы видите два способа определения класса Population. Я запускал программу ниже дважды, один раз с каждым способом создания класса Population. Один использует numpy и один не использует numpy. Один без numpy занимает аналогичное время, как Джон нашел в своих прогонах. Один с numpy занимает гораздо больше времени. Мне не ясно, что экземпляр popn создается до начала записи времени (по крайней мере, это то, что оно появляется из кода). Затем, почему версия numpy занимает больше времени. И, я думал, что numpy должно быть более эффективным. Во всяком случае, проблема, похоже, связана с numpy и не с добавлением, хотя она немного замедляет работу. Может кто-нибудь, пожалуйста, подтвердите с помощью кода ниже? Благодарю.

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: Смотрите ответы от Джона Мачин и TryPyPy

Поскольку было так много правок и изменений здесь, те, кто находится здесь в первый раз, может быть немного запутался. См. Ответы Джона Мачина и TryPyPy. Оба они могут помочь в улучшении скорости кода. Я благодарен им и другим, которые предупредили меня о медлительности append. Поскольку в этом случае я собираюсь использовать решение Джона Мачина и не использовать numpy для создания утилит, я принимаю его ответ в качестве ответа. Тем не менее, я очень ценю указания, упомянутые TryPyPy.

+15

+1 для профилирования перед тем, как обратиться за помощью. – Falmarri

+0

Какова длина 'self.people'? – Apalala

+1

Попробуйте добавить '__slots__' в класс Person –

ответ

4

Пожалуйста, обратите внимание уравновешивать вниз вашу 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 раз?) ... вот мой код, который демонстрирует значительное сокращение« без приложения », как я предложил, а также вносит существенное улучшение в «с ppend ".

import random # instead of numpy 
import time 
timer_func = time.clock # better on Windows, use time.time on *x platform 

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 = []   

def f_w_append(popn): 
    '''Function with append''' 
    P = 75 
    cus = [] 
    noncus = [] 
    for per in popn.people: 
     if per.utility >= P: 
      per.customer = 1 
      cus.append(per) 
     else: 
      per.customer = 0 
      noncus.append(per) 
    popn.cus = cus # omitted from OP's code 
    popn.noncus = noncus # omitted from OP's code 
    return len(cus) 

def f_w_append2(popn): 
    '''Function with append''' 
    P = 75 
    popn.cus = [] 
    popn.noncus = [] 
    cusapp = popn.cus.append 
    noncusapp = popn.noncus.append 
    for per in popn.people: 
     if per.utility >= P: 
      per.customer = 1 
      cusapp(per) 
     else: 
      per.customer = 0 
      noncusapp(per) 
    return len(popn.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 f_wo_append2(popn): 
    '''Function without append''' 
    P = 75 
    numcustomers = 0 
    for person in popn.people: 
     person.customer = iscust = person.utility >= P 
     numcustomers += iscust 
    return numcustomers  

if __name__ == "__main__": 
    import sys 
    popsize, which, niter = map(int, sys.argv[1:4]) 
    pop = Population(popsize) 
    func = (f_w_append, f_w_append2, f_wo_append, f_wo_append2)[which] 
    t0 = timer_func() 
    for _unused in xrange(niter): 
     nc = func(pop) 
    t1 = timer_func() 
    print "popsize=%d func=%s niter=%d nc=%d seconds=%.2f" % (
     popsize, func.__name__, niter, nc, t1 - t0) 

и вот результаты выполнения его (Python 2.7.1, Windows 7 Pro, "Intel Core i3 CPU 540 @ 3,07 ГГц"):

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 во время настройки населенности.

+0

Это сделало это намного хуже! Урезанная версия занимает в 4 раза больше времени, чем версия, опубликованная выше. – Curious2learn

+0

@ Curious2learn: Пожалуйста, внимательно изучите, есть ли у вас экспериментальная ошибка, и опубликуйте код, который вы используете. См. Мой отредактированный ответ. –

+0

МОЙ ОШИБКА! Я вижу, как ты ее запустил. Дай мне попробовать. (... Продолжение сверху) Когда я сделал это с помощью обрезанного кода, он дал мне 52.xxx и с моим исходным кодом, который у меня есть 13. Я предположил, что они были секундами, потому что это заняло и столько времени в реальном времени. Если вы выполнили 80000 итераций за 5 секунд, это здорово. Теперь я надеюсь, что это действительно так, потому что это существенно сэкономит мое время и позволит запускать симуляции с множеством различных конфигураций. Спасибо, что помогли мне в этом. – Curious2learn

0

Некоторые любопытные вещи, которые я заметил:

timePd передается в качестве параметра, но никогда не использовал

цена является массивом, но вы только когда-либо использовать последнюю запись - то почему бы не передать значение там вместо прохождения список?

счетчик инициализируется и никогда не использовали

self.people содержит несколько объектов человека, которые затем скопировать либо self.customers или self.noncustomers, а также иметь их установлен флаг клиента. Почему бы не пропустить операцию копирования и, вернувшись, просто перебирать список, глядя на флаг клиента? Это сэкономит дорогостоящее приложение.

В качестве альтернативы попробуйте использовать psyco, который может ускорить чистый Python, иногда значительно.

+0

У меня разные типы покупателей: b1 и b2. timePd не требуется для расчета количества, требуемого для покупателей типа b1. Однако для других типов покупателей, b2, это важно. Я включил timePd в качестве входа в b1, чтобы входы функции qtyDemanded были одинаковыми для b1 и b2. Таким образом, мне не нужно изменять аргументы функции qtyDemand в файле Main.py, где я ее использую. – Curious2learn

+0

Как просто установить флаг для каждого объекта, а не добавлять его в один из двух списков? Это ускоряет работу? – Spaceghost

+0

Извините, забыл упомянуть, что это похоже на хорошее предложение. Я попробую и напишу здесь. – Curious2learn

1

Вы можете устранить некоторую Lookups с помощью локальных функций псевдонимов:

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) 
5

Есть много вещей, которые вы можете попробовать после оптимизации кода Python для скорости. Если этой программе не нужны расширения C, вы можете запустить ее под PyPy, чтобы воспользоваться ее компилятором JIT. Вы можете попробовать сделать C extension, возможно, для huge speedups. Shed Skin позволит конвертировать вашу программу Python в автономный двоичный файл C++.

Я готов ко времени вашей программы в рамках этих различных сценариев оптимизации, если вы можете обеспечить достаточное количество коды для сравнительного анализа,

Edit: Прежде всего, я должен согласиться со всем остальными: вы уверены, Правильно ли вы измеряете время?Пример кода выполняется 100 раз в течение менее 0,1 секунд здесь, так что есть хороший шанс, либо время не так, либо у вас есть узкое место (IO?), Которого нет в образце кода.

Это сказало, что я сделал это 300000 человек, поэтому времена были последовательными. Вот адаптированный код, разделяемое 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» вместо «питона». Для Shed Skin, вы должны преобразовать в C++, компиляция и запуск:

shedskin -e makefaster.py && make 

# Check that you're using the makefaster.so file and run test 
python -c "import makefaster; print makefaster.__file__; makefaster.main()" 

А вот Cython-роскопия код:

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() 

Для построения, это приятно иметь setup.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 
) 

Вы строите его: питон setupfaster.py build_ext --inplace

Затем тест: python -c "import cymakefaster; печать cymakefaster. file; cymakefaster.main() "

Сроки выполнялись пять раз для каждой версии, причем Cython был самым быстрым и простым в использовании генераторами кода (Shed Skin стремится быть более простым, но загадочные сообщения об ошибках и неявное статическое типирование сделали это . труднее здесь) Что касается лучшего значения, 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 

Edit:. Aaaand более значимые тайминги для 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 

Shed Skin становится быстрее, PyPy превосходит Cython. Все три скорости ускоряются по сравнению с CPython.

+0

Gah, я пропустил «доступ к функции 80 000 раз»! Вам придется снова запустить все с кодом, отражающим это! : D – TryPyPy

+0

Большое спасибо! Я постараюсь понять PyPy. Это может быть проще всего использовать, учитывая мои текущие знания в программировании. – Curious2learn

+0

В идеальном сценарии (ваш код работает с Python 2.5 и не зависит от внешних расширений C), вы просто загружаете PyPy и вызываете программы с помощью «pypy file.py». Если вы зависите от расширений C, PyPy может не работать для вас, но Cython будет. Сообщите нам, есть ли у вас проблемы с совместимостью или программированием с любым из них. – TryPyPy

1

В зависимости от того, как часто вы добавляете новые элементы self.people или изменить person.utility, вы могли бы рассмотреть возможность сортировки self.people по utility поле.

Для этого вы можете использовать функцию bisect, чтобы найти нижний индекс i_pivot, где выполнено условие person[i_pivot].utility >= price. Это будет иметь более низкую сложность (O (журнал N)), чем ваша исчерпывающая петля (O (N))

С этой информацией, вы можете затем обновить people список, если это необходимо:

ли вам действительно нужны обновлять поле utility каждый раз? В отсортированном случае, вы могли бы легко вывести это значение в то время как итерация: например, принимая во внимание свой список, отсортированный в порядке incresing, utility = (index >= i_pivot)

Тот же вопрос с customers и nonCustomers списки. Зачем они нужны? Они могут быть заменены срезами исходного отсортированного списка: например, customers = self.people[0:i_pivot]

Все это позволит вам уменьшить сложность вашего алгоритма и использовать более встроенные (быстрые) функции Python, это может ускорить вашу реализацию ,

0

Удивительно, что показанная функция является таким узким местом, потому что она настолько проста. По этой причине я дважды проверю процедуру и результаты профилирования. Однако, если они верны, самая трудоемкая часть вашей функции должна состоять из цикла for, который он содержит, конечно, поэтому имеет смысл сосредоточиться на ускорении этого. Один из способов сделать это - заменить if/else на линейный код. Вы также можете немного уменьшить поиск атрибутов для метода списка append. Вот как обе эти вещи могут быть выполнены:

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] # last price 
    kinds = [[], []] # initialize sublists of noncustomers and customers 
    kindsAppend = [kinds[b].append for b in (False, True)] # append methods 

    for person in self.people: 
     person.customer = person.utility >= price # customer test 
     kindsAppend[person.customer](person) # add to proper list 

    self.nonCustomers = kinds[False] 
    self.customers = kinds[True] 

    return len(self.customers) 

То есть, я должен добавить, что это кажется немного излишним иметь как customer флаг в каждом человеке объекта и также поместить каждый из них в отдельный в зависимости от этого атрибута. Не создание этих двух списков, конечно, ускорит цикл.

1

Этой комментарий кольцо тревога:

'''Returns quantity demanded in period timePd. In addition, 
also updates the list of customers and non-customers. 

Помимо того, что timePd не используется в функции, если вы действительно хотите просто вернуть количество, делать только, что в этой функции. Делайте «дополнительно» в отдельной функции.

Затем профиль еще раз и посмотреть, какой из этих двух функций, которые вы тратите большую часть своего времени в

Я хотел бы применить SRP к методам, а также классам:. Это делает их легче проверить.

+1

Возможно, мне нужно обновить docstring. Выполнение двух отдельных функций для обновления и вычисления количества потребует выполнения двух циклов над одним и тем же списком. Теперь цикл работает только один раз. Это увеличит время. – Curious2learn

+0

Почему downvote? – Johnsyweb

0

Вы просите угадать, и в основном вы догадываетесь.

Не нужно угадывать. Here's an example.