Top.Mail.Ru
Ответы

Время выполнения функций

Вопрос новичка, связанный с кишками питона. Почему записанная в две строки функция выполняется быстрее, чему в одну? У второго варианта время выполнения стабильно меньше в 1,5 раза.

12345678910111213141516171819202122
 def benchmark(funct): 
    import time 

    def wrapper(): 
        start = time.time() 
        funct() 
        print(f'Выполнение: {time.time()-start} сек.') 
    return wrapper


@benchmark
def test1(): 
    res = [i for i in list(range(10000)) if i % 2 == 0] 
     
@benchmark 
def test2(): 
    l = list(range(10000))
    res = [i for i in l if i % 2 == 0]

test1()
test2()
 
По дате
По рейтингу
Аватар пользователя
Новичок
6мес

Сделайте много замеров, например, 10000, а не по одному. Посмотрите, какой будет результат. У меня разница получается намного меньше, но тем не менее всё равно второй вариант быстрее. Весьма интересный вопрос. Но значительную практическую ценность результат вряд ли несёт: если я задумаюсь о производительности своей программы, я вряд ли буду писать её на питоне.

Аватар пользователя
6мес

нипочему она не выполняется быстрее, просто питон медленно разгоняется, измени порядок вызова, сначала 2, потом 1 - теперь 1 быстрее, да? :)

Аватар пользователя
Высший разум
6мес

Например, потому, что в первом случае list не имеет никакого смысла и только замедляет код:

12
 def test3():
    res = [i for i in range(10000) if i % 2 == 0] 

Но даже это не имеет смысла, т.к. есть многократно более быстрый способ:

12
 def test4():
    res = list(range(0, 10000, 2)) 
Аватар пользователя
Мыслитель
6мес

На первый взгляд кажется, что оба варианта должны работать с одинаковой скоростью, так как они выполняют по сути одно и то же. Однако в действительности разница во времени выполнения может быть связана с деталями работы Python. Давайте разберем это по шагам:

### В чем различие между функциями?
1. **`test1`**:

1
    res = [i for i in list(range(10000)) if i % 2 == 0] 

Здесь `range(10000)` сначала преобразуется в список с помощью функции `list()`, а затем этот список используется в списочном выражении (`list comprehension`).

2. **`test2`**:

12
    l = list(range(10000))
   res = [i for i in l if i % 2 == 0] 

Здесь `list(range(10000))` создается заранее и сохраняется в переменную `l`, а затем используется в списочном выражении.

### Почему `test2` быстрее?
1. **Обращение к встроенным функциям:**
В первом случае (`test1`) вызов `list(range(10000))` происходит **внутри списочного выражения**, что добавляет некоторую дополнительную нагрузку. Python вынужден каждый раз вызывать встроенную функцию `list()` и одновременно работать с результатом.

2. **Оптимизация промежуточных объектов:**
Во втором случае (`test2`) переменная `l` хранит уже готовый список. Python обращается к уже созданному объекту, что уменьшает накладные расходы на повторное выполнение `list(range(10000))`.

3. **Повторная работа с объектом:**
В первом случае Python обрабатывает генерацию списка и фильтрацию "на лету". Это может быть менее эффективно, поскольку требует больше переключений контекста.

4. **Кэширование объектов:**
В `test2` список `l` уже находится в оперативной памяти и готов к использованию. В `test1` каждый раз создается временный объект, что может быть менее оптимально с точки зрения времени выполнения.

### Как проверить разницу объективно?
Если вы хотите измерить разницу времени выполнения более точно, используйте модуль `timeit`, который специально предназначен для измерения производительности:

123456789
 import timeit

setup_code = "l = list(range(10000))"

test1_code = "[i for i in list(range(10000)) if i % 2 == 0]"
test2_code = "[i for i in l if i % 2 == 0]"

print("test1:", timeit.timeit(test1_code, number=1000, globals=globals()))
print("test2:", timeit.timeit(test2_code, setup=setup_code, number=1000, globals=globals())) 


### Итог
Разница во времени выполнения связана с тем, что `test1` каждый раз создает новый список с помощью `list(range(10000))`, а в `test2` список создается один раз и используется повторно. Это объясняет, почему `test2` работает быстрее.

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

Аватар пользователя
Ученик
6мес

Работает не трогай, не работает - иди проветрись