2010-08-20 14 views
11

Czy istnieje różnica wydajności między używaniem wyrażenia if i wyrażeniem if oraz używaniem wielu instrukcji if? Innymi słowy, jest coś takiego jakEfektywność języka Python w przypadku wielu znaków if.

if expr1 == expr2 and expr3==expr4: 
    dostuff() 

różni się od punktu widzenia efektywności następnie:

if expr1 == expr2: 
    if expr3 == expr4: 
    dostuff() 

My bardzo podstawowe badania nie wskazują na różnicę, ale czy ktoś z większą wiedzą (lub przynajmniej bardziej dokładny testowanie) mają ostateczną odpowiedź?

+1

http://stackoverflow.com/questions/2539116/python-if-statement-efficiency może być powiązany. – anijhaw

+1

Nie znam różnic w wydajności, ale ważniejsza jest czytelność kodu. Jeśli jest wyraźniej używać wielu zagnieżdżonych instrukcji 'if', to rób to, co uważasz za sensowne. – derekerdmann

+1

Powinieneś przyjrzeć się temu w deasemblatorze, jeśli naprawdę chcesz wiedzieć, co się dzieje, ale pierwsze wyrażenie jest równie szybkie (potencjalnie szybsze, ale prawdopodobnie nie zoptymalizowane w ten sposób), ponieważ Python używa oceny zwarcia. –

Odpowiedz

4

W obu przypadkach expr1 == expr2 ocenia na false w if, drugie nie będą oceniane.

14

To nie jest wystarczająca różnica w wydajności, jeśli istnieje, aby wpłynąć na decyzję. IMO, decyzja tutaj powinna być podjęta wyłącznie z perspektywy czytelności. Pierwszy jest generalnie bardziej standardowy, ale są sytuacje, w których drugi może być jaśniejszy. Wybierz metodę, która najlepiej spełni Twoje oczekiwania.

+1

s/IMO //. To absolutnie szalone, aby wybrać jedną z nich z drugiej strony ze względu na wydajność, w prawie każdym ** języku. Nie ma sposobu na ilościowe określenie, która będzie szybsza, implementacja może zrobić to, co chce. –

+1

Dzięki.Ogólnie zgadzam się i koncentruję się na czytelności nad efektywnością. Ale w tym konkretnym przypadku pętla if jest wykonywana sporą liczbę razy i muszę zmniejszyć czas wykonywania, przynajmniej tym razem może to mieć znaczenie. – TimothyAWiseman

2

Pierwszy z nich (jeden if z and) jest szybsza :-)

Próbowałem ją używając timeit. Są to wyniki:

Variant 1: 9.82836714316 
Variant 2: 9.83886494559 
Variant 1 (True): 9.66493159804 
Variant 2 (True): 10.0392633241 

Przez ostatnie dwa, pierwszy Porównanie jest True, więc drugi jest pomijany. Interesujące wyniki.


import timeit 


print "Variant 1: %s" % timeit.timeit(""" 
for i in xrange(1000): 
    if i == 2*i and i == 3*i: 
     pass 
     """, 
     number = 1000) 

print "Variant 2: %s" % timeit.timeit(""" 
for i in xrange(1000): 
    if i == 2*i: 
     if i == 3*i: 
      pass 
     """, 
     number = 1000) 

print "Variant 1 (True): %s" % timeit.timeit(""" 
for i in xrange(1000): 
    if i == i and i == 3*i: 
     pass 
     """, 
     number = 1000) 

print "Variant 2 (True): %s" % timeit.timeit(""" 
for i in xrange(1000): 
    if i == i: 
     if i == 3*i: 
      pass 
     """, 
     number = 1000) 
10

Różnice w prędkości pomocą and i zagnieżdżone IFS będzie minimalny. Szczekasz złe drzewo. Rozważmy to drzewo:

if oftenTrueCondition and rarelyTrueCondition: 

porównaniu z

if rarelyTrueCondition and oftenTrueCondition: 

Więc chyba pierwszy warunek musi być najpierw oceniona (jest to strażnik zatrzymać następny wyraz od awarii lub robi coś głupiego/drogie), rozważ zamienienie kolejności oceny.

+0

Bardzo przydatny sposób myślenia o tym, którego nie brałem pod uwagę, a właściwie przyda się w pracy numerycznej. Dzięki! –

3

W razie wątpliwości można sprawdzić co to pyton kompilować swoje wypowiedzi, korzystania ze dis moduł:

>>> import dis 
>>> def test1(): 
...  if expr1 == expr2 and expr3==expr4: 
...  dostuff() 
... 
>>> def test2(): 
...  if expr1 == expr2: 
...  if expr3 == expr4: 
...   dostuff() 
... 
>>> dis.dis(test1) 
    2   0 LOAD_GLOBAL    0 (expr1) 
       3 LOAD_GLOBAL    1 (expr2) 
       6 COMPARE_OP    2 (==) 
       9 JUMP_IF_FALSE   24 (to 36) 
      12 POP_TOP    
      13 LOAD_GLOBAL    2 (expr3) 
      16 LOAD_GLOBAL    3 (expr4) 
      19 COMPARE_OP    2 (==) 
      22 JUMP_IF_FALSE   11 (to 36) 
      25 POP_TOP    

    3   26 LOAD_GLOBAL    4 (dostuff) 
      29 CALL_FUNCTION   0 
      32 POP_TOP    
      33 JUMP_FORWARD    1 (to 37) 
     >> 36 POP_TOP    
     >> 37 LOAD_CONST    0 (None) 
      40 RETURN_VALUE   
>>> dis.dis(test2) 
    2   0 LOAD_GLOBAL    0 (expr1) 
       3 LOAD_GLOBAL    1 (expr2) 
       6 COMPARE_OP    2 (==) 
       9 JUMP_IF_FALSE   28 (to 40) 
      12 POP_TOP    

    3   13 LOAD_GLOBAL    2 (expr3) 
      16 LOAD_GLOBAL    3 (expr4) 
      19 COMPARE_OP    2 (==) 
      22 JUMP_IF_FALSE   11 (to 36) 
      25 POP_TOP    

    4   26 LOAD_GLOBAL    4 (dostuff) 
      29 CALL_FUNCTION   0 
      32 POP_TOP    
      33 JUMP_ABSOLUTE   41 
     >> 36 POP_TOP    
      37 JUMP_FORWARD    1 (to 41) 
     >> 40 POP_TOP    
     >> 41 LOAD_CONST    0 (None) 
      44 RETURN_VALUE   

Więc jak widać, na poziomie Pythona kodu bajtowego, oba twierdzenia są takie same - nawet podczas korzystania pojedyncza, jeśli na pierwszym zestawieniu, wykona JUMP_IF_FALSE po pierwszym porównaniu.

Powiązane problemy