Эффективность передачи переменных по сравнению с их переопределением для функции, вызываемой много раз

#python #processing-efficiency #memory-efficient

#python #эффективность обработки #экономия памяти

Вопрос:

У меня есть вопрос об эффективности, в котором меня интересуют как скорость обработки, так и эффективность памяти.

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

В следующем примере я ожидаю, что первые два одинаково эффективны, а последний не так эффективен. Однако у меня нет четкого представления о том, почему. Кто-нибудь может помочь объяснить это?

РЕДАКТИРОВАТЬ: в приведенном ниже примере я использую только 100 итераций. На самом деле я ожидал бы, что функции будут вызываться таким образом тысячи или миллионы раз.

РЕДАКТИРОВАТЬ 2: для тех, кто просто говорит мне профилировать его. обратите внимание, что я спросил, почему, а не только если, некоторые способы более эффективны, чем другие. Профилирование ответит, если, но не почему.

 import numpy as np

def myfunc(a):
    (a**4)   np.sqrt(3)*a - a/3

for a in arange(100):
    y[a] = myfunc(a)
  

по сравнению

 import numpy as np
myb = 3

def myfunc(a):
    (a**4)   np.sqrt(myb)*a - a/myb

for a in arange(100):
    y[a] = myfunc(a)
  

по сравнению

 import numpy as np
myb = 3

def myfunc(a, b):
    (a**4)   np.sqrt(b)*a - a/b

for a in arange(100):
    y[a] = myfunc(a, myb)
  

Комментарии:

1. О чем мы говорим с точки зрения количества итераций, которые вам нужно выполнить? сотни? тысячи? миллионы? — Я вижу цикл со 100 итерациями. Будет ли это выполняться много раз другим кодом, который также выполняет циклы? — Я хочу сказать, что, если вы не говорите о миллионах итераций, беспокоиться об эффективности передачи нескольких переменных не стоит trouble…it это не приведет ни к чему существенному с точки зрения времени.

2. Вы пробовали профилировать свой код? Все они по существу эквивалентны.

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

4. @Steve Этот вопрос — проблема, о которой я думаю для нескольких кодов. В некоторых случаях функция like myfunc вызывается несколько миллионов раз. В других случаях код A вызывает myfun ~ 100 раз, а код B вызывает код A ~ 100 раз, поэтому меня интересуют оба случая.

5. Так почему бы не профилировать его самостоятельно, используя, например timeit

Ответ №1:

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

Вот пример, который я собрал:

 import random

def foo(iter, a, b, c, d, e, f, g, h, i ,j):
    x = a   b   c   d   e   f   g   h   i   j
    if iter % 100000 == 0:
        print(x)

for i in range(1000000):
    foo(i, random.random() * 100,
        random.random() * 100,
        random.random() * 100,
        random.random() * 100,
        random.random() * 100,
        random.random() * 100,
        random.random() * 100,
        random.random() * 100,
        random.random() * 100,
        random.random() * 100)
  

Результат:

 658.9874644541911
643.4372986147371
636.6218502753122
475.3660640474451
648.4789890659888
466.2721794578193
595.3755252194462
583.45879143973
498.04278700281304
283.2047039562956
  

Этот код выполняет миллион итераций создания 10 случайных значений, умножения каждого на 100, передачи их по отдельности в функцию и суммирования их в функции. Каждые 100 000 итераций я печатаю значение суммы просто для проверки работоспособности.

Это выполняется за 2-3 секунды на моем Macbook Pro. Наши компьютеры в наши дни действительно очень быстрые и мощные. Настолько, что почти никогда не стоит беспокоиться о видах оптимизации, о которых вы говорите.

ОБНОВЛЕНИЕ: чтобы продвинуть точку дальше, и потому, что мне было любопытно, я попытался извлечь поколения случайных чисел и запустить это:

 for i in range(1000000):
    foo(i, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
  

Это выполняется практически мгновенно, печатая 55 десять раз в мгновение ока. Итак, большая часть первого примера — это генерация 10 миллионов случайных чисел. Далее я отмечу, что при использовании этих констант компилятор и процессор, вероятно, оптимизируют wazoo, поскольку в этом случае ничего не меняется. Но это только подталкивает к дальнейшему. Нет причин беспокоиться о передаче постоянных значений, отчасти потому, что компиляторы и процессоры в наши дни распознают и оптимизируют такие шаблоны для вас. Именно поэтому я использовал random() для первого примера, чтобы избежать этих оптимизаций.

Память — это другая проблема, но обычно это решается самой проблемой, а не тем, как именно вы это делаете. Безусловно, бывают случаи, когда память становится особой проблемой, когда вам нужно быть умным (выполнять что-то в пакетах, обрабатывать с потоками и т. Д.). Вопрос памяти заключается в том, где было бы неплохо знать, о каких числах мы говорим, и как данные выглядят в целом.

Комментарии:

1. Это полезно для понимания того, что мне не нужно беспокоиться об этом вопросе, но мне все еще интересно, что делается по-разному между различными методами на более низком уровне. Я полагаю, что это потребовало бы отдельного отдельного вопроса, поэтому я отмечу это как принятое. Спасибо!

2. Вы можете использовать дизассемблер, чтобы увидеть байтовый код для двух версий.

3. @Barmar — Да, пн. Отличная идея. Я думаю, что PyCharm может сделать это за вас, хотя я забыл, как именно. Хотя я мог бы подумать о Java и IntelliJ.