Python интерпретирует оптимизацию кода
Рассмотрим следующий фрагмент кода:
dict [name] = 0
dict [name] += 1
dict [name] += 1
Является ли интерпретатор python автоматически распознавать повторяющиеся ссылки на значение словаря и вместо этого использует вместо этого кешированную локальную ссылку?, что несколько напоминает оптимизацию сглаживания C/С++, что-то вроде этого:
value = dict [name]
value = 0
value += 1
value += 1
Очевидно, это не очень важно сделать это вручную, но мне любопытно, действительно ли это необходимо. любое понимание, отзывы и т.д..
Ответы
Ответ 1
Вы можете запустить его через дизассемблер, чтобы узнать:
import dis
def test():
name = 'test'
tdict = {}
tdict[name] = 0
tdict[name] += 1
tdict[name] += 1
dis.dis(test)
Запустив это, получим:
13 0 LOAD_CONST 1 ('test')
3 STORE_FAST 0 (name)
14 6 BUILD_MAP 0
9 STORE_FAST 1 (tdict)
15 12 LOAD_CONST 2 (0)
15 LOAD_FAST 1 (tdict)
18 LOAD_FAST 0 (name)
21 STORE_SUBSCR
16 22 LOAD_FAST 1 (tdict)
25 LOAD_FAST 0 (name)
28 DUP_TOPX 2
31 BINARY_SUBSCR
32 LOAD_CONST 3 (1)
35 INPLACE_ADD
36 ROT_THREE
37 STORE_SUBSCR
17 38 LOAD_FAST 1 (tdict)
41 LOAD_FAST 0 (name)
44 DUP_TOPX 2
47 BINARY_SUBSCR
48 LOAD_CONST 3 (1)
51 INPLACE_ADD
52 ROT_THREE
53 STORE_SUBSCR
54 LOAD_CONST 0 (None)
57 RETURN_VALUE
Похоже, что в этом случае LOAD_FAST
загружает значения tdict
и name
каждый раз, когда мы пытаемся получить к нему доступ для выполнения приращения, поэтому ответ будет выглядеть как нет.
Ответ 2
Этот тип оптимизации невозможно просто путем проверки кода. Ваше имя dict
может ссылаться не на родной словарь, а на определенный пользователем объект, который реализует __setitem__
, и этот метод нужно вызывать три раза. Во время выполнения сложная реализация может отметить фактическое значение имени и сделать оптимизацию, но она не может быть выполнена до выполнения, не нарушая семантики Python.
Ответ 3
Нет, потому что это не сработает, и вы даже продемонстрировали, что с вашим собственным кодом - это фактически не эквивалентно:
>>> a = {}
>>> name = 'x'
>>> a[name] = 0
>>> a[name] += 1
>>> a[name] += 1
>>> a[name] # ok no suprises so far
2
>>> a = {}
>>> a[name] = 0
>>> x = a[name] # x is now literally `0`, not some sort of reference to a[name]
>>> x
0
>>> x += 1
>>> x += 1
>>> a[name] # so this never changed
0
>>>
Python не имеет ссылок на C-ish. То, что вы имели в виду, будет работать только для изменяемых типов, таких как list
. Это очень фундаментальное свойство Python, и вы, вероятно, должны забыть все, что C научил вас переменным при программировании Python.
Ответ 4
Измените два примера на следующее:
#v1.py
di = {}
name = "hallo"
di[name] = 0
for i in range(2000000):
di[name] += 1
и
#v2.py
di = {}
name = "hallo"
di[name] = 0
value = di[name]
for i in range(2000000):
value += 1
В следующих тестах вы можете увидеть, что v2 быстрее, но pypy выполняется намного быстрее: -)
$ time python2.7 v1.py
real 0m0.788s
user 0m0.700s
sys 0m0.080s
$ time python2.7 v2.py
real 0m0.586s
user 0m0.490s
sys 0m0.090s
$ time pypy v1.py
real 0m0.203s
user 0m0.210s
sys 0m0.000s
$ time pypy v2.py
real 0m0.117s
user 0m0.080s
sys 0m0.030s
SO: нехорошо оптимизировать код для одного интерпретатора (например, я не тестировал Jython...), но это здорово, когда кто-то оптимизирует интерпретатор...