У меня есть код python, который имеет много классов. Я использовал cProfile
, чтобы узнать, что общее время запуска программы составляет 68 секунд. Я обнаружил, что следующая функция в классе под названием Buyers
занимает около 60 секунд этих 68 секунд. Мне нужно запустить программу примерно 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.people
- это список объектов person
. Каждый person
имеет customer
и utility
в качестве своих атрибутов.
EDIT - добавлен ответ
-------------------------------------
Большое спасибо за предложения. Здесь ответ на некоторые вопросы и предложения, которые люди любезно сделал. Я не пробовал их всех, но попробую других и напишу позже.
(1) @amber - доступ к функции осуществляется 80 000 раз.
(2) @gnibbler и другие - self.people - это список объектов Person в памяти. Не подключен к базе данных.
(3) @Hugh Bothwell
cumtime, взятый исходной функцией - 60,8 с (доступ к 80000 раз)
cumtime, взятый новой функцией с локальными псевдонимами функций, как было предложено - 56,4 с (доступ к 80000 раз)
(4) @rotoglup и @Martin Thomas
Я еще не пробовал ваши решения. Мне нужно проверить остальную часть кода, чтобы увидеть места, где я использую self.customers, прежде чем я смогу внести изменения, не добавляя клиентов в список self.customers. Но я попробую это и напишу.
(5) @TryPyPy - спасибо за ваше любезное предложение проверить код.
Позвольте мне вначале прочитать немного о тех предложениях, которые вы сделали, чтобы узнать, будут ли эти возможности использоваться.
РЕДАКТИРОВАТЬ 2
Некоторые предположили, что, поскольку я помещаю клиентов и неклиентов в self.people
, я должен попробовать, не создавая отдельные списки self.customers
и self.noncustomers
, используя append. Вместо этого я должен перебрать 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
РЕДАКТИРОВАТЬ 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.