2014-11-03 7 views
9
from timeit import Timer as T 

def calc(n): 
    return T("class CLS(object): pass").timeit(n) 

print(calc(90000)) 
print(calc(90000)) 
print(calc(90000)) 

# python3.4 
1.1714721370008192 
1.0723806529986177 
1.111804607000522 

# python2.7 
15.7533519268 
16.7191421986 
16.8397979736 

Perché c'è così tanta differenza nel tempo di creazione della classe utilizzando diverse versioni di python? Testato sulla stessa macchina:Perché c'è differenza tra la creazione di una classe in Python 2.7 e la prestazione di python 3.4

  • i5-3450 CPU @ 3.10GHz
  • 8GB di RAM
+1

dei relativi interessi: http://stackoverflow.com/q/10072428/3001761 – jonrsharpe

+1

immagino oggetto ha qualcosa a che fare con esso –

+4

ho ristretto il problema a 'timeit' chiamare' gc.disable() '. Poiché le classi creano sempre molti cicli, disabilitando il raccoglitore significa che ciascuna istanza di 'CLS' non può essere deallocata. Puoi ottenere una lista degli oggetti tracciati chiamando 'gc.get_objects()'. Le prestazioni sono simili se non si disabilita GC, ma basta aggiungere ciascuna istanza di 'CLS' a un elenco per mantenerlo come riferimento. Il puzzle sembra essere il motivo per cui '_PyObject_GC_Malloc' sta andando molto peggio in questo caso per Python 2. – eryksun

risposta

0

Bene, il problema sembra essere con il vecchio stile classi vs. nuovo stile in pitone 2.7.

in Python 3.4 si può vedere che la differenza tra l'utilizzo di oggetto e non lo si utilizza è solo il caricamento del simbolo (non che significativo):

C:\TEMP>C:\Python34\python.exe 
Python 3.4.2 (v3.4.2:ab2c023a9432, Oct 6 2014, 22:15:05) [MSC v.1600 32 bit (Intel)] on win32 
Type "help", "copyright", "credits" or "license" for more information. 
>>> 
>>> def a(): 
... class A(object): pass 
... 
>>> def b(): 
... class B(): pass 
... 
>>> import dis 
>>> dis.dis(a) 
    2   0 LOAD_BUILD_CLASS 
       1 LOAD_CONST    1 (<code object A at 0x020B8F20, file "<stdin>", line 2>) 
       4 LOAD_CONST    2 ('A') 
       7 MAKE_FUNCTION   0 
      10 LOAD_CONST    2 ('A') 
      13 LOAD_GLOBAL    0 (object) # Extra step, not that expensive. 
      16 CALL_FUNCTION   3 (3 positional, 0 keyword pair) 
      19 STORE_FAST    0 (A) 
      22 LOAD_CONST    0 (None) 
      25 RETURN_VALUE 
>>> dis.dis(b) 
    2   0 LOAD_BUILD_CLASS 
       1 LOAD_CONST    1 (<code object B at 0x020B8D40, file "<stdin>", line 2>) 
       4 LOAD_CONST    2 ('B') 
       7 MAKE_FUNCTION   0 
      10 LOAD_CONST    2 ('B') 
      13 CALL_FUNCTION   2 (2 positional, 0 keyword pair) 
      16 STORE_FAST    0 (B) 
      19 LOAD_CONST    0 (None) 
      22 RETURN_VALUE 
>>> 

Mentre su Python 2.7 si ha un altro passo che coinvolge LOAD_TUPLE:

C:\Users\jsargiot\Downloads\so>C:\Python27\python.exe 
Python 2.7.8 (default, Jun 30 2014, 16:03:49) [MSC v.1500 32 bit (Intel)] on win32 
Type "help", "copyright", "credits" or "license" for more information. 
>>> 
>>> def a(): 
... class A(object): pass 
... 
>>> def b(): 
... class B(): pass 
... 
>>> import dis 
>>> dis.dis(a) 
    2   0 LOAD_CONST    1 ('A') 
       3 LOAD_GLOBAL    0 (object) # First extra step (just like 3.4) 
       6 BUILD_TUPLE    1   # Second extra step, expensive 
       9 LOAD_CONST    2 (<code object A at 01EAEA88, file "<stdin>", line 2>) 
      12 MAKE_FUNCTION   0 
      15 CALL_FUNCTION   0 
      18 BUILD_CLASS 
      19 STORE_FAST    0 (A) 
      22 LOAD_CONST    0 (None) 
      25 RETURN_VALUE 
>>> dis.dis(b) 
    2   0 LOAD_CONST    1 ('B') 
       3 LOAD_CONST    3 (()) 
       6 LOAD_CONST    2 (<code object B at 01EB8EC0, file "<stdin>", line 2>) 
       9 MAKE_FUNCTION   0 
      12 CALL_FUNCTION   0 
      15 BUILD_CLASS 
      16 STORE_FAST    0 (B) 
      19 LOAD_CONST    0 (None) 
      22 RETURN_VALUE 
>>> 
2

timeit disabilita garbage collector, che altrimenti rompere i cicli che mantengono in vita un oggetto classe. Pertanto nessuna delle classi viene deallocata fino al termine di timeit.

object.__subclasses__() fa riferimento a queste classi tramite una raccolta interna di riferimenti deboli. La vecchia implementazione basata su elenchi di tp_subclasses ricerca ogni volta l'intero elenco per trovare un riferimento morto che può essere sostituito. Questo processo richiede più tempo con ciascuna sottoclasse aggiuntiva. D'altra parte, il nuovo design basato su dict in 3.4 può aggiungere un riferimento in tempo costante. Vedi issue 17936.


Grazie a @MichaelYounkin per sottolineare come questo è anche lento a 3.2. Inizialmente ho provato a restringere la differenza di prestazioni a un cambiamento nell'allocatore di oggetti piccoli tra 2.xe 3.x, ma dopo aver letto il suo commento ho scoperto che anche 3.3 era notevolmente più lento del 3.4. Così ho scansionato lo typeobject.c filelog per rivedere le modifiche recenti.

Problemi correlati