В ходе одного из моих питоновских проектов, с большой примесью ООП и обработкой большого числа данных — у меня возник вопрос, а насколько эффективно обрабатывать списки в классе с использованием вызовов его методов, или может использовать вызов внешней функции? Для этого были написаны 24 теста которые показали очень интересные результаты, Кому интересна данная тема, прошу в подробности.
Тестовый образец:
создается список из 500 тыс. элементов и заполняется случайными значениями
lst = []
for i in range(1, 500000):
val = random() + i
lst.append(val)
Обработка в 2 этапа:
1) сначала получение нового списка путем возведение в квадрат каждого элемента списка (несколькими способами)
2) потом добавление к полученному списку некоторой константы, которая может быть: локальной, глобально, аттрибутом (для класса)
Например так
@howlong
def process_list_global_func_plus_local_value():
"""Функция. Обработка в цикле с вызовом глобальной функции и добавлением локальной переменной"""
local_plus_value = GLOBAL_VALUE
new_lst = []
for i in lst:
new_lst.append(global_func(i))
for v in new_lst:
v + local_plus_value
Производительность
Под производительностью понималось длительность выполнения каждого теста, помереная с помощью декоратора
import time
def howlong(f):
def tmp(*args, **kwargs):
t = time.time()
res = f(*args, **kwargs)
need_time = time.time()-t
tmp.__name__ = f.__name__
tmp.__doc__ = f.__doc__
#print u"%s time: %f" % ((f.__doc__), need_time)
print ".",
return need_time
return tmp
Были получены следующие результаты по двум группам тестов:
Функция. Обработка Генератором без вызова внешних функций и добавлением локальной переменной — 0.192 — 100%
Функция. Обработка Генератором без вызова внешних функций и добавлением глобальной переменной — 0.2 — 104%
Функция. Обработка в цикле без вызова внешних функций и добавлением локальной переменной — 0.238 — 123%
Функция. Обработка в цикле без вызова внешних функций и добавлением глобальной переменной — 0.245 — 127%
Функция. Обработка с использованием map и добавлением локальной переменной — 0.25 — 130%
Функция. Обработка с использованием map и добавлением глобальной переменной — 0.255 — 132%
Функция. Обработка Генератором с вызовом локальной функции и добавлением локальной переменной — 0.258 — 134%
Функция. Обработка Генератором с вызовом глобальной функции и добавлением глобальной перем. — 0.274 — 142%
Функция. Обработка в цикле с вызовом локальной функции и добавлением локальной переменной — 0.312 — 162%
Функция. Обработка в цикле с вызовом локальной функции и добавлением глобальной переменной — 0.32 — 166%
Функция. Обработка в цикле с вызовом глобальной функции и добавлением глобальной переменной — 0.327 — 170%
Функция. Обработка в цикле с вызовом глобальной функции и добавлением локальной переменной — 0.332 — 172%
Класс. Обработка Генератором без вызова внешних функций и добавлением локальной переменной — 0.191 — 100%
Класс. Обработка Генератором без вызова внешних функций и добавлением значения глобальной пер. — 0.20 — 104%
Класс. Обработка Генератором без вызова внешних функций и добавлением значения аттрибута — 0.213 — 111%
Класс. Обработка вызовом локальной функции и добавлением локальной переменной — 0.312 — 163%
Класс. Обработка вызовом глобальной функции и добавлением локальной переменной — 0.318 — 166%
Класс. Обработка вызовом локальной функции и добавлением глобальной переменной — 0.318 — 166%
Класс. Обработка вызовом глобальной функции и добавлением глобальной переменной — 0.328 — 171%
Класс. Обработка вызовом локальной функции и добавлением значения аттрибута — 0.333 — 174%
Класс. Обработка вызовом глобальной функции и добавлением значения аттрибута — 0.34 — 178%
Класс. Обработка вызовом метода класса и добавлением локальной переменной — 0.39 — 204%
Класс. Обработка вызовом метода класса и добавлением глобальной переменной — 0.398 — 208%
Класс. Обработка вызовом метода класса и добавление значения аттрибута — 0.411 — 215%
Выводы:
Наибольший интерес представляю процентные разницы.
По обработке списка функцией
1) Самая быстрая обработка списка — в генераторе без вызова внешних функций и с использованием локальных переменных, с использованием глобальных переменных будет на ~5% дольше
2) при обработке списка перебором в цикле for время работы будет на 23% с использование локальных переменных и на 27% дольше с использованием глобальных переменных, чем при обработке через генератор
3) при использовании в цикле for для обработки списка внешней локальной или глобальной функции скорость будет ниже более чем на 60%
4) при использовании map в качестве функции обработки элементов списка практически такой же как ри использовании вызова внешней функции в генераторе
5) самым долгим способом обработки списка является обработка в цикле с использование вызова внешней глобальной функции
По обработке списка в классе
1) в классах доступ к аттрибутам дольше чем к локальным переменным, примерно на 10%, и на 5% дольше чем к глобальным переменным
2) в классах доступ к методам дольше, чем к локальным функциям примерно на 22% и на 20% дольше чем к глобальным функциям
3) при обработке списка в классе с использованием генератора получается также быстро, как и с использованием функции с генератором
4) самым долгим способом (более чем в 2 раза дольше) оказалось использование вызова метода в цикле и добавлением значения аттрибута, что явилось для меня большим удивлением
Код доступен в одном файле по адресу http://pastebin.com/rgdXNdXb
Дальше планирую исследовать скорость доступа к элементам списков, туплов, различных видом словарей, а потом вопросы целесообразности кеширования функций и методов классов
Автор: pymen