2009-02-17 12 views
11

J'ai besoin de charger (dé-sérialiser) une liste d'entiers précalculés à partir d'un fichier dans un script Python (dans une liste Python). La liste est grande (jusqu'à des millions d'objets), et je peux choisir le format dans lequel je stocke, tant que le chargement est le plus rapide.Sérialisation de la liste Python - méthode la plus rapide

Quelle est la méthode la plus rapide, et pourquoi?

  1. En utilisant import sur un fichier .py qui contient simplement la liste affectée à une variable
  2. En utilisant load
  3. de » cPickle
  4. autre méthode (peut-être numpy?)

En outre, comment peut-on comparer ces choses de manière fiable?

Addendum: mesure cela est fiable difficile, car import est mis en cache de sorte qu'il ne peut pas être exécuté plusieurs fois dans un test. Le chargement avec le pickle devient également plus rapide après la première fois, probablement parce que le système d'exploitation précâblait les pages. Le chargement de 1 million de numéros avec cPickle prend 1,1 seconde la première fois et 0,2 seconde lors des exécutions suivantes du script. Intuitivement, je pense que cPickle devrait être plus rapide, mais j'apprécierais les chiffres (c'est assez difficile à mesurer, je pense).

Et oui, il est important pour moi que cela fonctionne rapidement.

Merci

+0

Est-ce vraiment la partie lente de votre code? À quelle fréquence allez-vous charger le fichier? –

+0

Avez-vous essayé un de ces produits? Quelles mesures avez-vous en ce moment? –

+0

Pour ce que cela vaut, vous pouvez éviter les problèmes d'importation en utilisant "execfile()" ... – gahooa

Répondre

7

Je suppose cPickle sera plus rapide si vous avez vraiment besoin la chose dans une liste.

Si vous pouvez utiliser un array, qui est un type de séquence intégré, je l'ai chronométré cela à un quart de seconde pour 1 million d'entiers:

from array import array 
from datetime import datetime 

def WriteInts(theArray,filename): 
    f = file(filename,"wb") 
    theArray.tofile(f) 
    f.close() 

def ReadInts(filename): 
    d = datetime.utcnow() 
    theArray = array('i') 
    f = file(filename,"rb") 
    try: 
     theArray.fromfile(f,1000000000) 
    except EOFError: 
     pass 
    print "Read %d ints in %s" % (len(theArray),datetime.utcnow() - d) 
    return theArray 

if __name__ == "__main__": 
    a = array('i') 
    a.extend(range(0,1000000)) 
    filename = "a_million_ints.dat" 
    WriteInts(a,filename) 
    r = ReadInts(filename) 
    print "The 5th element is %d" % (r[4]) 
+0

'Read 1000000 ints in 0: 00: 03.500000', et cela a pris 1/4 de seconde pour vous? –

+0

Cependant, vous avez raison, array.fromfile est beaucoup plus rapide que cpickle !! –

+0

@eliben - vous pourriez vouloir choisir ceci comme la meilleure réponse. Les leçons sur l'utilisation du module timeit sont populaires, mais elles ne répondent pas directement à votre question! –

2

"comment peut-on référence fiable de telles choses?"

Je ne comprends pas la question.

Vous écrivez un tas de petites fonctions pour créer et enregistrer votre liste sous diverses formes.

Vous écrivez un tas de petites fonctions pour charger vos listes dans leurs différentes formes. Vous écrivez une petite fonction de minuterie pour obtenir l'heure de début, exécutez la procédure de chargement quelques dizaines de fois (pour obtenir une moyenne solide suffisamment longue pour que le bruit d'ordonnancement du système d'exploitation ne domine pas vos mesures).

Vous résumez vos données dans un petit rapport.

Ce qui n'est pas fiable à ce sujet?

Voici quelques questions non liées qui montrent comment mesurer et comparer les performances.

Convert list of ints to one number?

String concatenation vs. string substitution in Python

+0

Je suis d'accord. C'est ce que je fais. –

+0

Comment puis-je exécuter "importer " plusieurs fois dans une boucle si l'importation est mise en cache? –

+1

Si votre ensemble de données est assez grand, une mesure peut être tout ce dont vous avez besoin. Sinon, vous pouvez exécuter à partir de la ligne de commande dans une boucle shell et l'heure à la place. Regardez aussi imp.load_module. –

3

Pour l'étalonnage, voir le module timeit dans la bibliothèque standard de Python. Pour voir ce qui est le moyen le plus rapide, implémentez toutes les façons de penser et mesurez-les avec le temps. Pensée aléatoire: en fonction de ce que vous faites exactement, vous pouvez trouver le plus rapide pour stocker des "ensembles d'entiers" dans le style utilisé dans .newsrc fichiers:

1, 3-1024, 11000-1200000 

Si vous devez vérifier si quelque chose est dans ce jeu, puis le chargement et correspondant à une telle représentation devrait être parmi les moyens les plus rapides. Cela suppose que vos ensembles d'entiers sont raisonnablement denses, avec de longues suites consécutives de valeurs adjacentes.

+0

http://docs.python.org/library/timeit.html – Owen

2

Pour vous aider à temps, la bibliothèque Python fournit le module timeit:

Ce module fournit un moyen simple de temps des petits morceaux de code Python. Il a à la fois une ligne de commande et des interfaces appelables. Il évite un certain nombre de pièges communs pour mesurer les temps d'exécution.

Un exemple (du manuel) qui compare le coût d'utilisation par rapport à hasattr()try/except pour tester manquantes et présente les attributs d'objet:

% timeit.py 'try:' ' str.__nonzero__' 'except AttributeError:' ' pass' 
100000 loops, best of 3: 15.7 usec per loop 
% timeit.py 'if hasattr(str, "__nonzero__"): pass' 
100000 loops, best of 3: 4.26 usec per loop 
% timeit.py 'try:' ' int.__nonzero__' 'except AttributeError:' ' pass' 
1000000 loops, best of 3: 1.43 usec per loop 
% timeit.py 'if hasattr(int, "__nonzero__"): pass' 
100000 loops, best of 3: 2.23 usec per loop 
1

cPickle sera le plus rapide puisqu'il est enregistré en binaire et aucun vrai code python doit être analysé.

D'autres avantages sont qu'il est plus sécurisé (puisqu'il n'exécute pas de commandes) et vous n'avez aucun problème avec le réglage $PYTHONPATH correctement.

2

Avez-vous besoin de toujours charger le fichier entier? Sinon, upack_from() pourrait être la meilleure solution. Supposons que vous ayez des entiers 1000000, mais que vous souhaitiez charger seulement ceux de 50000 à 50099, vous feriez:

import struct 
intSize = struct.calcsize('i') #this value would be constant for a given arch 
intFile = open('/your/file.of.integers') 
intTuple5K100 = struct.unpack_from('i'*100,intFile,50000*intSize) 
Questions connexes