LINUX.ORG.RU

Влияет ли избыточное создание переменных на производительность?

 , , , ,


0

2

Привет всем! Давно такой вопрос мучал меня. Влияет ли избыточное создание переменных на производительность? Допустим я в Python буду руководствоваться принципам красоты кода, и буду разбивать длинные выражения на несколько более мелких, создавая избыточные переменные, это на быстродействии не скажется? И вообще, этот случай на всех языках программирования на быстродействии не скажется? Например такой код:

def egg(a, b, c):
   some = some1.some2.some3(a)
   todo = some.fucking1.fucking2.fucking3(b)
   .... // и т.д. :)
   return fucking_finish

Ответ на: комментарий от uuwaan

Нет. Каждый мейнстримовый язык что ли буду проверять? Допустим я на крестах пока что не понимаю. На расте тоже. JS конечно ненмного понимаю, но опять же глубоко не знаю. Мне нужен ответ, более общий и абстрактный.

dimcoin ()

на всех языках программирования на быстродействии не скажется

Слишком сильно зависит от вм||компилятора. Даже в случае с питоном у тебя есть CPython, Jython, PyPy. Формально и ВМ и компилятор может заинлайнить вот эту твою лапшу и ничего лишнего не выделять на стеке.

Deleted ()
Ответ на: комментарий от Deleted

Слишком сильно зависит от вм||компилятора.

Много ли программистов в нынче эти вещи соблюдают? И вообще стоит ли глубоко копать эту тему, чтобы быть правильным девелопером?

dimcoin ()
Ответ на: комментарий от dimcoin

Уметь формулировать модель в терминах языка с которым ты работаешь намного полезнее. Базовые ловушки с производительностью со временем ты и так будешь видеть, а для всего остального важно уметь пользоваться профайлером.

Deleted ()

в каких-то случаях скажется, в каких-то нет. в интерпретируемых языках без оптимизации, например, производительность может и возрасти:

import time

def test_no_vars(x):
    for i in range(10**8):
        x.append(i)
        x.pop()
        
def test_vars(x):
    append = x.append
    pop = x.pop
    for i in range(10**8):
        append(i)
        pop()
        
def do_f(f):
    x = list()
    t = time.time()
    f(x)
    return time.time() - t
    
print('Without vars:', do_f(test_no_vars))
print('With vars:', do_f(test_vars))
Without vars: 18.365822553634644
With vars: 11.63066840171814
anonymous ()

Компилятор скорее всего сделает как ему надо в любом случае. ЯП много, а ассемблер для твоего железа, он один (ну почти). Так что не замарачивайся, пиши читабельно.

Deleted ()
Ответ на: комментарий от bvn13

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

Очень хороший подход! И его придерживается большинство современных программистов. А производительность - да кому она нужна, ну купят еще 8 гигов оперативы да процессор побыстрее, делов то. Главное быстро слепить очередной продукт, правда?

otto ★★ ()
Ответ на: комментарий от uuwaan

Возьми и измерь среднее время выполнения разных вариантов одной и той же функции.

А если оно слишком мало то поставь функуцию в цикл так, чтобы гонять её несколько минут.

Прогноз: Питон интерпретатор, по этому лишние переменные приведут к замедлению.

torvn77 ★★★★ ()

Если у тебя нет необходимости или ты не в состоянии это замерить, то тебе без разницы.

Видали мы таких оптимизаторов, экономят на спичках, а у них танк украли.

pon4ik ★★★★★ ()
Ответ на: комментарий от pon4ik

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

dimcoin ()
Ответ на: комментарий от dimcoin

В общем случае - влияет. Если компилятор или вм умеет оптимизировать такие конструкции, то нет, не влияет. Никто за тебя проверять это все не будет, потому что всем на это насрать в большинстве случаев.

anonymous ()
Ответ на: комментарий от dimcoin

Дело не в желании, и даже не в умении, дело в самом подшивании подворотничка к воротничку.(c)

Можно сколько угодно наяривать на серебрянный общий случай, но когда дело дойдёт до практики стоимость этих «познаний» будет <= 0. Анон дело говорит - зависит от качества оптимизатора и уровня оптимизации. Например в gcc -O0 зависит(на такие копейки, что не каждый оптимизаторщик теоретик замеряет какие именно) а в gcc -O2 в 99% нет, при условии, что сиё можно было записать и без переменных.

pon4ik ★★★★★ ()
Последнее исправление: pon4ik (всего исправлений: 1)

конечно влияет, но читаемость кода важнее чем скорость выполнения. слава б-огу что в python подобные горе оптимизаторы не могут писать эпичные онострочники как в perl

tz4678 ()

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

slovazap ★★★★★ ()

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

Раскрою страшную (даже для многих современных «сишников») тайну, чтобы совсем уж сон и аппетит потерял — компилятор, падла эдакая, при компиляции может добавить еще целую кучу временных переменных для внутреннего пользования.

Допустим я в Python буду руководствоваться принципам красоты кода, и буду разбивать длинные выражения на несколько более мелких

Берешь и проверяешь:


>>>import dis
...     y = x
...     y += 1
...     return y
... 
>>> def foo2(x):
...     return x+1
... 
>>> dis.dis(foo)
  4           0 LOAD_FAST                0 (x)
              3 STORE_FAST               1 (y)
  5           6 LOAD_FAST                1 (y)
              9 LOAD_CONST               1 (1)
             12 INPLACE_ADD         
             13 STORE_FAST               1 (y)
  6          16 LOAD_FAST                1 (y)
             19 RETURN_VALUE        
>>> dis.dis(foo2)
  4           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               1 (1)
              6 BINARY_ADD          
              7 RETURN_VALUE 
 % echo "int foo(int x) { return x+1;} int foo2(int x) {int y = x; y += 1; return y;}"|gcc -O1  -S -o- -x c - 
	.file	""
	.text
	.globl	foo
	.type	foo, @function
foo:
.LFB0:
	.cfi_startproc
	leal	1(%rdi), %eax
	ret
	.cfi_endproc
.LFE0:
	.size	foo, .-foo
	.globl	foo2
	.type	foo2, @function
foo2:
.LFB1:
	.cfi_startproc
	leal	1(%rdi), %eax
	ret
	.cfi_endproc
.LFE1:
	.size	foo2, .-foo2
	.ident	"GCC: (FreeBSD Ports Collection) 7.3.0"
	.section	.note.GNU-stack,"",@progbits
anonymous ()

Зависит от того, переприсваиваешь значение, или второй раз вызываешь метод:

str1 = object.method()
str2 = str1

str1 = object.method()
str2 = object.method()

В первом случае на скорость и память не повлияет, так как в динамических языках сам объект (строка) не копируется, а копируется только ссылка.
Во втором случае тратится время на вызов и работу метода, плюс выделяется память под новый объект (новое значение строки).

Мой совет: не бойся простого переприсваивания, но бойся ненужных многократных вызовов методов.

P.S. Если инициализируешь константами (NULL, цифрами и т.п.), то тоже быстро происходит.

Novator ★★★★ ()
Последнее исправление: Novator (всего исправлений: 1)
Ответ на: комментарий от MrClon

Если нужно вызывать разные методы, то смысла спрашивать вообще не было - у него просто нет другого выхода, кроме как использовать разные переменные.

Хотя... можно и одну переменную, но выгода в скорости будет небольшая. А вот память можно немного сэкономить, т.к. после вызова второго метода первое значение улетит в сборщик мусора.

Novator ★★★★ ()
Последнее исправление: Novator (всего исправлений: 2)
Ответ на: комментарий от Novator

Выбор есть, не использовать переменные.
ТС спрашивает про разницу например между

x = foo()
y = bar()
return x + y

и
return foo() + bar()

Переменные тут нужны просто чтобы повысить читабельность, если foo и bar на самом деле длинные

MrClon ★★★★★ ()
Ответ на: комментарий от MrClon

В твоем примере речь только о читабельности.

Но я предвижу ещё один случай:

str = 'Hello dude!'
print(str)
str = 'Bye dude!'
print(str)
return

Здесь можно использовать одну переменную (str), а можно две (hi_str и bye_str, например). И если использовать одну, то сборщик мусора от первого значения избавится в середине. Если две - то сборщик мусора избавится от обоих значений только после return.

Novator ★★★★ ()
Последнее исправление: Novator (всего исправлений: 1)
Ответ на: комментарий от MrClon

А, понял, «промежуточные переменные» ))

Будет чуть медленней за счет выделения памяти под ссылки на объекты, но, возможно, читабельность важнее.

Я так делаю только в каких-то запутанных случаях. Но если что-то очевидное, то я стараюсь делать одним выражением, без промежуточных переменных.

Novator ★★★★ ()
Последнее исправление: Novator (всего исправлений: 2)

Второй или третий? Или pypy? Или cython? Это я намекаю. И да, вызов не-builtin функции в питоне куда дороже создания переменных

Питон вообще занятный язык с точки зрения скорости. Например что быстрее, a += b или a = a + b? Или list comprehension vs generator vs funcutils vs for-loop? Там очень много тонких нюансов, которые развернут результат на 180

upcFrost ★★★★★ ()
Последнее исправление: upcFrost (всего исправлений: 3)