2014-04-14 12 views
8

ho questo semplice codice che mi ha aiutato a misurare quanto classi con __slots__ eseguire (tratto da here):Utilizzando __slots__ sotto PyPy

import timeit 

def test_slots(): 
    class Obj(object): 
     __slots__ = ('i', 'l') 

     def __init__(self, i): 
      self.i = i 
      self.l = [] 

    for i in xrange(1000): 
     Obj(i) 

print timeit.Timer('test_slots()', 'from __main__ import test_slots').timeit(10000) 

Se l'eseguo via python2.7 - vorrei avere qualcosa di circa 6 secondi - ok, è molto più veloce (e anche più efficiente in termini di memoria) che senza slot.

Tuttavia, se eseguo il codice in PyPy (utilizzando 2.2.1 - 64 bit per Mac OS/X), inizia a utilizzare il 100% della CPU e "mai" restituisce (atteso per minuti - nessun risultato).

Cosa sta succedendo? Dovrei usare __slots__ sotto PyPy?

Ecco cosa succede se passo diverso numero di timeit():

timeit(10) - 0.067s 
timeit(100) - 0.5s 
timeit(1000) - 19.5s 
timeit(10000) - ? (probably more than a Game of Thrones episode) 

Grazie in anticipo.


Si noti che lo stesso comportamento si osserva se uso namedtuple s:

import collections 
import timeit 

def test_namedtuples(): 
    Obj = collections.namedtuple('Obj', 'i l') 

    for i in xrange(1000): 
     Obj(i, []) 

print timeit.Timer('test_namedtuples()', 'from __main__ import test_namedtuples').timeit(10000) 
+3

Indipendentemente da qualsiasi altra cosa, un programma che esegue un ciclo infinito o che richiede 60x finché sotto CPython è un bug e deve essere generato con i ragazzi di PyPy. – delnan

+0

Mi aspetterei che la versione '__slots__' sia più lenta in quanto tali classi sono ottimizzate per lo spazio e non sono ottimizzate per il recupero degli attributi. – wheaties

+0

@wheaties yup, ma il benchmark mostra che 'slots' e' namedtuples' sono anche più veloci: http://stackoverflow.com/questions/1336791/dictionary-vs-object-which-is-more-efficient-and-why/1336890 # 1336890 – alecxe

risposta

11

In ciascuno dei 10.000 o giù di iterazioni del codice timeit, la classe viene ricreata da zero. Creare le classi probabilmente non è un'operazione ben ottimizzata in PyPy; anche peggio, così facendo probabilmente scartare tutte le ottimizzazioni che il JIT ha appreso sulla precedente incarnazione della classe. PyPy tende ad essere lento fino a quando la JIT non si è riscaldata, quindi fare cose che lo richiedono per riscaldarsi ripetutamente ucciderà le tue prestazioni.

La soluzione qui è, ovviamente, spostare semplicemente la definizione di classe al di fuori del codice oggetto di benchmark.

+0

Questo è interessante e un po 'deludente. La creazione di classi è un'operazione leggera sotto CPython, e ci sono stili di programmazione che sicuramente dipendono da questo. – Marcin

+0

@Marcin Credo che il team di PyPy consideri i frammenti di codice più lenti rispetto a CPython, quindi sentitevi liberi di scriverne uno. Soprattutto se conosci qualche uso della rapida creazione di classi di breve durata nel codice reale. – kwatford

+2

Vale la pena notare che anche in CPython, la creazione di una nuova classe non è così economica. Le classi occupano diverse centinaia di byte, molto più di un oggetto tipico, e sono in (e hanno) diverse cache, quindi l'allocazione e la deallocazione sono molto più lente del tuo oggetto medio. –

8

Per rispondere direttamente alla domanda nel titolo: __slots__ non ha senso (ma non guasta) le prestazioni in PyPy.

+0

Grazie, Armin. Potresti aggiungere o fare riferimento a qualsiasi informazione su come PyPy memorizza le variabili di istanza? – alecxe

+4

@alecxe http://morepypy.blogspot.ca/2010/11/efficiently-implementing-python-objects.html delinea l'approccio. –