146 votes

Dictionnaire vs objet - ce qui est plus efficace et pourquoi ?

Ce qui est plus efficace en Python en termes de l'utilisation de la mémoire et de la consommation CPU - Dictionnaire ou d'un Objet?

Arrière-plan: Je charge énorme quantité de données en Python. J'ai créé un objet qui est juste un champ contenant. La création de 4M instances et de les mettre dans un dictionnaire a pris environ 10 minutes et ~6 go de mémoire. Après le dictionnaire est prêt, y accéder est un clin d'œil.

Exemple: Afin de vérifier la performance, j'ai écrit deux programmes simples qui font la même - l'un est à l'aide d'objets, d'autres dictionnaire:

Objet (temps d'exécution ~18sec):

class Obj(object):
  def __init__(self, i):
    self.i = i
    self.l = []
all = {}
for i in range(1000000):
  all[i] = Obj(i)

Dictionnaire (temps d'exécution ~12 s):

all = {}
for i in range(1000000):
  o = {}
  o['i'] = i
  o['l'] = []
  all[i] = o

Question: Suis-je en train de faire quelque chose de mal ou de dictionnaire est juste plus rapide de l'objet? Si, en effet, le dictionnaire fonctionne mieux, quelqu'un peut-il expliquer pourquoi?

178voto

codeape Points 38576

Avez-vous essayé d'utiliser __slots__?

À partir de la documentation http://docs.python.org/reference/datamodel.html#slots:

"Par défaut, les instances de l'ancien et du nouveau style de classes ont un dictionnaire pour le stockage des attributs. Cela gaspille de l'espace pour les objets ayant très peu de variables d'instance. La consommation de l'espace peut devenir aiguë lors de la création d'un grand nombre de cas.

La valeur par défaut peut être remplacée par la définition d' __slots__ dans un nouveau style de la définition de la classe. L' __slots__ déclaration prend une séquence de variables d'instance et les réserves de juste assez d'espace dans chaque cas pour contenir une valeur pour chaque variable. L'espace est sauvé parce qu' __dict__ n'est pas créé pour chaque instance."

Donc, cela permet d'économiser du temps, de la mémoire?

En comparant les trois approches sur mon ordinateur:

test_slots.py:

class Obj(object):
  __slots__ = ('i', 'l')
  def __init__(self, i):
    self.i = i
    self.l = []
all = {}
for i in range(1000000):
  all[i] = Obj(i)

test_obj.py:

class Obj(object):
  def __init__(self, i):
    self.i = i
    self.l = []
all = {}
for i in range(1000000):
  all[i] = Obj(i)

test_dict.py:

all = {}
for i in range(1000000):
  o = {}
  o['i'] = i
  o['l'] = []
  all[i] = o

test_namedtuple.py (pris en charge dans 2.6):

import collections

Obj = collections.namedtuple('Obj', 'i l')

all = {}
for i in range(1000000):
  all[i] = Obj(i, [])

Référence (à l'aide de Disponible 2.5):

$ lshw | grep product | head -n 1
          product: Intel(R) Pentium(R) M processor 1.60GHz
$ python --version
Python 2.5
$ time python test_obj.py && time python test_dict.py && time python test_slots.py 

real    0m27.398s (using 'normal' object)
real    0m16.747s (using __dict__)
real    0m11.777s (using __slots__)

L'aide Disponible 2.6.2, y compris le nom de n-uplet de test:

$ python --version
Python 2.6.2
$ time python test_obj.py && time python test_dict.py && time python test_slots.py && time python test_namedtuple.py 

real    0m27.197s (using 'normal' object)
real    0m17.657s (using __dict__)
real    0m12.249s (using __slots__)
real    0m12.262s (using namedtuple)

Donc, oui (pas vraiment une surprise), à l'aide de __slots__ est une optimisation des performances. À l'aide d'un nommé n-uplet a des performances similaires à l' __slots__.

17voto

Vinay Sajip Points 41286

L’accès à un objet attribut utilise l’accès dictionnaire dans les coulisses - si vous ajoutez à l’aide d’accès de l’attribut supplémentaire de frais généraux. De plus dans le cas de l’objet, vous encourent une charge supplémentaire à cause des allocations de mémoire par exemple supplémentaire et l’exécution de code (par exemple de la `` méthode).

Dans votre code, si est un cas, est équivalent à avec une petite quantité d’une charge supplémentaire.

11voto

John Fouhy Points 14700

Avez-vous envisagé d'utiliser un nom nommé ? ( lien pour python 2.4 / 2.5 )

C'est la nouvelle manière standard de représenter des données structurées qui vous donne les performances d'un tuple et la commodité d'une classe.

Son seul inconvénient est que cela ne vous donne pas la possibilité de modifier les attributs après la création (comme les n-uplets).

4voto

hughdbrown Points 15770
 from datetime import datetime

ITER_COUNT = 1000 * 1000

def timeit(method):
    def timed(*args, **kw):
        s = datetime.now()
        result = method(*args, **kw)
        e = datetime.now()

        print method.__name__, '(%r, %r)' % (args, kw), e - s
        return result
    return timed

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

class SlotObj(object):
    __slots__ = ('i', 'l')
    def __init__(self, i):
       self.i = i
       self.l = []

@timeit
def profile_dict_of_dict():
    return dict((i, {'i': i, 'l': []}) for i in xrange(ITER_COUNT))

@timeit
def profile_list_of_dict():
    return [{'i': i, 'l': []} for i in xrange(ITER_COUNT)]

@timeit
def profile_dict_of_obj():
    return dict((i, Obj(i)) for i in xrange(ITER_COUNT))

@timeit
def profile_list_of_obj():
    return [Obj(i) for i in xrange(ITER_COUNT)]

@timeit
def profile_dict_of_slotobj():
    return dict((i, SlotObj(i)) for i in xrange(ITER_COUNT))

@timeit
def profile_list_of_slotobj():
    return [SlotObj(i) for i in xrange(ITER_COUNT)]

if __name__ == '__main__':
    profile_dict_of_dict()
    profile_list_of_dict()
    profile_dict_of_obj()
    profile_list_of_obj()
    profile_dict_of_slotobj()
    profile_list_of_slotobj()
 

Résultats:

 hbrown@hbrown-lpt:~$ python ~/Dropbox/src/StackOverflow/1336791.py 
profile_dict_of_dict ((), {}) 0:00:08.228094
profile_list_of_dict ((), {}) 0:00:06.040870
profile_dict_of_obj ((), {}) 0:00:11.481681
profile_list_of_obj ((), {}) 0:00:10.893125
profile_dict_of_slotobj ((), {}) 0:00:06.381897
profile_list_of_slotobj ((), {}) 0:00:05.860749
 

3voto

Roberto Liffredo Points 15265

Il n'est pas question.
Vous avez des données, n'ayant pas d'autres attributs (pas de méthodes, rien). Donc vous avez un conteneur de données (dans ce cas, un dictionnaire).

D'habitude, je préfère penser en termes de modélisation de données. Si il y a un énorme problème de performances, alors que je peux donner quelque chose dans l'abstraction, mais seulement avec de très bonnes raisons.
La programmation est tout au sujet de la gestion de la complexité, et le maintien de la corriger l'abstraction est très souvent l'un de la façon la plus utile pour parvenir à un tel résultat.

Sur les raisons d'un objet est plus lent, je pense que votre mesure n'est pas correcte.
Vous effectuez trop petits travaux à l'intérieur de la boucle, et donc ce que vous voyez là est la différence de temps nécessaire pour instancier une dict (intrinsèque de l'objet) et "personnalisé" de l'objet. Bien que du point de vue de la langue, ils sont les mêmes, ils sont fort différents de mise en œuvre.
Après cela, la cession, le temps devrait être presque la même pour les deux, comme dans la fin les membres sont maintenus à l'intérieur d'un dictionnaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X