2015-09-18 4 views
-1

0-я петля ниже работает намного быстрее *, чем 1 (3 × для Python-2.7, 5 × для Python-3.4):Компилятор оптимизации операций по констант

for x in range(0,999999): 
    "u" in {"a", "e", "i", "o", "u"} 

for x in range(0,999999): 
    "u" in set("aeiou") 

Похоже, что Python, когда он задан {"a", "e", "i", "o", "u"}, строит набор один раз, но при задании set("aeiou"), он создает набор каждый раз, когда это необходимо.

Это правильно? Если да, то почему Python не оптимизирует последнее?


* Как измерено с помощью time python -c '<code>'

+2

Связанный: [фильтрация понимания списка - «ловушка набора()» (http://stackoverflow.com/questions/20056458/list-comprehension-filtering-the-set-trap) –

ответ

1

интерпретатор Python-не распознает set("aeiou") как константа, потому что, хотя это тривиально, чтобы сказать, что "aeiou" не изменится, это гораздо труднее сказать, будет ли функция set изменить.

Переводчик мог проверить, что set() не переназначен в любом месте в пределах цикла, но я предполагаю, что нервотрепки просто не стоит.

+3

Почему Python распознает функцию вызывать как константу? Пользователь может назначить переменную 'set', и это не относится к литералам. Как указано в моем ответе в другом посте, эта оптимизация была добавлена ​​в Python 3.2: http://stackoverflow.com/a/20056585/846892 –

+0

@Ashwini: Ahhh. Я вижу сейчас. Виноват. – Zaz

0

В первом случае вы создаете множество раз во время компиляции (когда код питона компилируются питон байт-OPS). Во втором вы создаете набор один раз для каждой итерации цикла.

1

Вы вызываете конструктор в каждом цикле. Попробуйте еще раз:

import time 

start = time.time() 
for x in range(0,999999): 
    "u" in {"a", "e", "i", "o", "u"} 
print time.time() - start, "for dictionary look-up" 

start = time.time() 
for x in range(0,999999): 
    "u" in ["a", "e", "i", "o", "u"] 
print time.time() - start, "for list look-up" start = time.time() 

for x in range(0,999999): 
    "u" in set("aeiou") 
print time.time() - start, "for set construction" 

start = time.time() 
vowel = set("aeiou") 
for x in range(0,999999): 
    "u" in vowel 
print time.time() - start, "for set reference" 

Это дает время:

0.292190074921 for dictionary look-up 
0.160042047501 for list look-up 
0.529402971268 for set construction 
0.098151922226 for set reference 
1

Я создал функции f1 и f2, основанные на ваших фрагментах (от # 0 и # 1 соответственно). Затем я использую модуль dis. Короче говоря, вы можете видеть, что конструктор set вызывается во втором случае внутри цикла (31 во втором dis). В то время как константа просто загружается в первую (25 из первой).

In [20]: dis.dis(f1) 
    2   0 SETUP_LOOP    33 (to 36) 
       3 LOAD_GLOBAL    0 (range) 
       6 LOAD_CONST    1 (0) 
       9 LOAD_CONST    2 (999999) 
      12 CALL_FUNCTION   2 (2 positional, 0 keyword pair) 
      15 GET_ITER 
     >> 16 FOR_ITER    16 (to 35) 
      19 STORE_FAST    0 (x) 

    3   22 LOAD_CONST    3 ('u') 
      25 LOAD_CONST    8 (frozenset({'e', 'o', 'a', 'u', 'i'})) 
      28 COMPARE_OP    6 (in) 
      31 POP_TOP 
      32 JUMP_ABSOLUTE   16 
     >> 35 POP_BLOCK 
     >> 36 LOAD_CONST    0 (None) 
      39 RETURN_VALUE 

In [21]: dis.dis(f2) 
    2   0 SETUP_LOOP    39 (to 42) 
       3 LOAD_GLOBAL    0 (range) 
       6 LOAD_CONST    1 (0) 
       9 LOAD_CONST    2 (999999) 
      12 CALL_FUNCTION   2 (2 positional, 0 keyword pair) 
      15 GET_ITER 
     >> 16 FOR_ITER    22 (to 41) 
      19 STORE_FAST    0 (x) 

    3   22 LOAD_CONST    3 ('u') 
      25 LOAD_GLOBAL    1 (set) 
      28 LOAD_CONST    4 ('aeiou') 
      31 CALL_FUNCTION   1 (1 positional, 0 keyword pair) 
      34 COMPARE_OP    6 (in) 
      37 POP_TOP 
      38 JUMP_ABSOLUTE   16 
     >> 41 POP_BLOCK 
     >> 42 LOAD_CONST    0 (None) 
      45 RETURN_VALUE 
Смежные вопросы