Тестирование производительности Python 2.7 при обработке списков различными способами

в 17:38, , рубрики: python, Программирование, производительность, метки: , ,

image

В ходе одного из моих питоновских проектов, с большой примесью ООП и обработкой большого числа данных — у меня возник вопрос, а насколько эффективно обрабатывать списки в классе с использованием вызовов его методов, или может использовать вызов внешней функции? Для этого были написаны 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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js