Tableau Python efficace avec 100 millions de zéros?

Quel est un moyen efficace d'initialiser et d'accéder aux éléments d'un grand tableau en Python?

Je veux créer un tableau en Python avec 100 millions d'entrées, entiers non signés de 4 octets, initialisés à zéro. Je veux un accès rapide au tableau, de préférence avec une mémoire contiguë.

Étrangement, NumPy les tableaux semblent fonctionner très lentement. Y a-t-il des alternatives que je peux essayer?

Il y a le tableau .array module, mais je ne vois pas de méthode pour allouer efficacement un bloc de 100 millions d'entrées.

Réponses aux commentaires:

  • Je ne peux pas utiliser un tableau clairsemé. Ce sera trop lent pour cet algorithme car le tableau devient dense très rapidement.
  • Je sais que Python est interprété, mais il y a sûrement un moyen de faire des opérations de tableau rapides?
  • j'ai fait du profilage, et j'obtiens environ 160K accès au tableau (recherche ou mise à jour d'un élément par index) par seconde avec NumPy. Cela semble très lent.
22
demandé sur Peter Mortensen 2010-02-06 23:46:40

10 réponses

J'ai fait du profilage, et les résultats sont complètement contre-intuitifs. Pour les opérations d'accès au tableau simples, numpy et array.les tableaux sont 10 fois plus lents que les tableaux Python natifs .

Notez que pour l'accès au tableau, je fais des opérations de la forme:

a[i] += 1

Profils:

  • [0] * 20000000

    • Accès: 2,3 M / s
    • initialisation: 0.8 s
  • Numpy.zéros (forme = (20000000,), dtype = numpy. int32)

    • Accès: 160K/s
    • initialisation: 0.2 s
  • Tableau.tableau ('L', [0] * 20000000)

    • Accès: 175 K/s
    • initialisation: 2.0 s
  • Tableau.tableau ('L', (0 pour i dans la plage (20000000)))

    • Accès: 175K / sec, probablement, basé sur le profil de l'autre tableau.tableau
    • initialisation: 6.7 s
29
répondu Joseph Turian 2010-02-06 21:17:34

Juste un rappel comment fonctionnent les entiers de Python: Si vous allouez une liste en disant

a = [0] * K

Vous avez besoin de la mémoire pour la liste (sizeof(PyListObject) + K * sizeof(PyObject*)) et de la mémoire pour l'objet entier unique 0. Tant que les nombres dans la liste restent en dessous du nombre magique V que Python utilise pour la mise en cache, vous allez bien parce que ceux-ci sont partagés, c'est-à-dire tout nom qui pointe vers un nombre n < V pointe vers le même objet. Vous pouvez trouver cette valeur en utilisant l'extrait suivant:

>>> i = 0
>>> j = 0
>>> while i is j:
...    i += 1
...    j += 1
>>> i # on my system!
257 

Ce signifie que dès que les comptes vont au-dessus de ce nombre, la mémoire dont vous avez besoin est sizeof(PyListObject) + K * sizeof(PyObject*) + d * sizeof(PyIntObject), Où d < K est le nombre d'entiers au-dessus de V (== 256). Sur un système 64 bits, sizeof(PyIntObject) == 24 et sizeof(PyObject*) == 8, c'est-à-dire que la consommation de mémoire la plus défavorable est de 3 200 000 000 octets.

Avec numpy.ndarray ou array.array, la consommation de mémoire est constante après l'initialisation, mais vous payez pour les objets wrapper créés de manière transparente, comme L'a dit Thomas Wouters. Probablement, vous devriez penser à convertir le code de mise à jour (qui accède et augmente les positions dans le tableau) en code C, soit à l'aide de Cython ou scipy.weave.

13
répondu Torsten Marek 2010-02-06 22:26:07

Essayez ceci:

x = [0] * 100000000

Il ne faut que quelques secondes pour s'exécuter sur ma machine, et l'accès est proche de l'instant.

7
répondu Mark Byers 2010-02-06 20:52:39

Si vous n'êtes pas capable de vectoriser vos calculs, Python / Numpy sera lent. Numpy est rapide car les calculs vectorisés se produisent à un niveau inférieur à celui de Python. Les fonctions numpy de base sont toutes écrites en C ou en Fortran. Par conséquent, sum(a) n'est pas une boucle python avec de nombreux accès, c'est un seul appel C de bas niveau.

Numpy les Performances de Python page de démonstration a un bon exemple avec différentes options. Vous pouvez facilement obtenir une augmentation de 100 fois en utilisant un langage compilé de niveau inférieur, Cython, ou en utilisant des fonctions vectorisées si possible. Cet article de blog {[5] } qui montre une augmentation de 43 fois en utilisant Cython pour un cas d'utilisation numpy.

5
répondu Tristan 2010-02-06 22:41:25

Il est peu probable que vous trouviez quelque chose de plus rapide que numpy's array. L'implémentation du tableau lui-même est aussi efficace que dans, disons, C (et fondamentalement la même chose que array.array, juste avec plus d'utilité.)

Si vous voulez accélérer votre code, vous devrez le faire par faire exactement cela. Même si le tableau est implémenté efficacement, l'accès à partir du code Python a une certaine surcharge; par exemple, l'indexation du tableau produit des objets entiers, qui doivent être créés à la volée. numpy propose un certain nombre d'opérations implémentées efficacement en C, mais sans voir le code réel qui ne fonctionne pas aussi bien que vous le souhaitez, il est difficile de faire des suggestions spécifiques.

3
répondu Thomas Wouters 2010-02-06 20:53:51

Pour une création rapide, utilisez le module array.

L'Utilisation du module array est ~5 fois plus rapide pour la création, mais environ deux fois plus lente pour accéder aux éléments par rapport à une liste normale:

# Create array
python -m timeit -s "from array import array" "a = array('I', '\x00'
 * 100000000)"
10 loops, best of 3: 204 msec per loop

# Access array
python -m timeit -s "from array import array; a = array('I', '\x00'
* 100000000)" "a[4975563]"
10000000 loops, best of 3: 0.0902 usec per loop

# Create list
python -m timeit "a = [0] * 100000000"
10 loops, best of 3: 949 msec per loop

# Access list
python -m timeit  -s "a = [0] * 100000000" "a[4975563]"
10000000 loops, best of 3: 0.0417 usec per loop
3
répondu truppo 2010-02-06 22:09:04

En plus des autres excellentes solutions, une autre façon est d'utiliser un dict au lieu d'un tableau (les éléments qui existent sont non nuls, sinon ils sont nuls). Recherche en temps est O(1).

Vous pouvez également vérifier si votre application réside dans la RAM, plutôt que d'échanger. Ce N'est que 381 Mo, mais le système ne vous donne peut-être pas tout pour une raison quelconque.

Cependant, il y a aussi des matrices clairsemées très rapides ( SciPy et ndsparse). Ils sont fait dans Bas Niveau C, et pourrait aussi être bon.

1
répondu user262976 2010-02-06 22:48:02

Je voudrais simplement créer votre propre type de données qui n'initialise aucune valeur.

Si vous voulez lire une position d'index qui N'a pas été initialisée, vous renvoyez des zéros. Pourtant, ne pas initialiser tout stockage.

Si vous voulez lire une position d'index qui a été initialisée, renvoyez simplement la valeur.

Si vous voulez écrire dans une position d'index qui N'a pas été initialisée, initialisez-la et stockez l'entrée.

0
répondu Dolph 2010-02-06 22:32:24

NumPy est l'outil approprié pour un grand tableau homogène de taille fixe. L'accès à des éléments individuels de N'importe quoi en Python ne sera pas si rapide, bien que les opérations de tableau entier puissent souvent être effectuées à des vitesses similaires à C ou Fortran. Si vous avez besoin d'effectuer des opérations sur des millions et des millions d'éléments individuellement rapidement, il ya seulement tellement que vous pouvez sortir de Python.

Quel type d'algorithme implémentez-vous? Comment savez-vous que l'utilisation de matrices creuses est trop lent si vous n'avez pas essayé? Qu'entendez-vous par "efficace"? Vous voulez une initialisation rapide? C'est le goulot d'étranglement de votre code?

0
répondu Mike Graham 2010-02-06 22:43:07

Si

  • vitesse d'accès du tableau.tableau est acceptable pour votre application
  • le stockage compact est le plus important
  • vous voulez utiliser des modules standard (pas de dépendance NumPy)
  • Vous êtes sur des plates-formes qui ont / dev / zero

Ce qui suit peut vous intéresser. Il initialise le tableau.array environ 27 fois plus rapide que array.tableau ('L', [0] * Taille):

myarray = array.array('L')
f = open('/dev/zero', 'rb')
myarray.fromfile(f, size)
f.close()

Sur Comment initialiser un tableau d'entiers.objet tableau avec des zéros en Python je suis la recherche d'une meilleure façon de le faire.

0
répondu Joachim Wagner 2017-05-23 12:18:13