J'ai un script qui manipule de très gros dictionnaires, lus dans un
fichier et reconstruits à l'aide de eval(). Il y a là-dedans plein de
chaînes de caractères, avec beaucoup de répétition.
Malheureusement, la mémoire utilisée s'envole (plus de 6 giga avec 200
ou 300 mega de texte original). Y a-t-il une possibilité que les chaînes
soient automatiquement passées à intern() ? Ou bien dois-je le faire à
la main ?
Merci d'avance.
-- Alain.
Pourquoi le eval() ?
Je ferais plutôt :
- Lire dans le fichier cle + valeur puis
- dico [cle] = valeur
D'où vient le fichier texte ?
S'il est géré par un appli Python (ou autre), tu gagnerais peut être à
utiliser sqllite : moins de données en mémoire, les clés gérées par
l'index, etc...
Il faudrait un peu plus d'infos pour répondre au problème.
>> J'ai un script qui manipule de très gros dictionnaires, lus dans un
>> fichier et reconstruits à l'aide de eval(). Il y a là-dedans plein de
>> chaînes de caractères, avec beaucoup de répétition.
> Pourquoi le eval() ?
>
> Je ferais plutôt :
> - Lire dans le fichier cle + valeur puis
> - dico [cle] = valeur
En fait, mes structures sont un peu plus compliquées qu'un seul niveau
de dictionnaire. Il y a une grosse structure de premier niveau, dans
laquelle plein de détails sont lus sous la forme de litéraux python
(typiquement, à chaque fois, un dictionnaire dont les valeurs sont des
listes dont les éléments sont des arbres dont les feuilles sont des
chaines). J'ai un parser pour la "macro" structure, et il y a plein
d'appel à eval() (en général, plusieurs milliers).
> D'où vient le fichier texte ?
Généré par une appli à moi (qui n'est pas écrite en python). Ca me sert
à debugger et parcourir des structures assez complexes, pour faire des
tas de tests annexes, des affichages, etc. L'appli principale dump ses
structures, et tout le test est reporté dans des scripts python.
> S'il est géré par un appli Python (ou autre), tu gagnerais peut être à
> utiliser sqllite : moins de données en mémoire, les clés gérées par
> l'index, etc...
En fait, ça me poserait plus de problèmes de passer tout dans une base
relationnelle. L'avantage de la manip, c'est justement d'éviter des
transformations de structures de données.
J'ai fini par traverser le résultat de chaque eval() pour recontruire
une structure identique mais dont les chaines sont internées. J'ai gagné
un tiers en volume. Ca devrait aller pour l'instant.
-- Alain.
>> D'où vient le fichier texte ?
>
> Généré par une appli à moi (qui n'est pas écrite en python). Ca me sert
> à debugger et parcourir des structures assez complexes, pour faire des
> tas de tests annexes, des affichages, etc. L'appli principale dump ses
> structures, et tout le test est reporté dans des scripts python.
>
Si le fichier était généré en python, tu aurais pu utiliser pickle pour
dumper le dictionnaire.
Dans ton cas, tu peux peut-être regarder du coté de json ou yaml, si ces
formats sont disponibles pour ton appli qui génère le fichier,
mais je ne suis pas sur que tu y gagneras en performances.
Bonne continuation.
Pour un truc un peu similaire, j'utilise cPickle (dump pour enregistrer, load pour charger).
@+
--
Michel Claveau
> Effectivement, c'est un peu plus complexe qu'indiqué dans le message
> initial.
[...]
Oui, je ne voulais pas effrayer tout le monde avec mes usines à gaz.
> Si le fichier était généré en python, tu aurais pu utiliser pickle
> pour dumper le dictionnaire.
[...]
J'avais essayé pickle (en fait, cPickle) il y a quelques temps et
j'étais assez déçu : ça n'allait pas plus vite qu'un parser écrit à la
main (ça c'est normal), et les fichiers étaient à peu près de la même
taille (mais par défaut c'est en ASCII). Cela dit, je n'avais pas poussé
bien loin. Je réessaierai un de ces jours et je vous tiendrai au
courant.
Pour en revenir à ma question originale (l'utilisation plus ou moins
automatique de intern()), je suis tout de même surpris qu'il ne soit pas
plus facile d'utiliser ce mécanisme : puisque les chaînes sont
immuables, autant en profiter.
Bref. Merci pour les idées.
-- Alain.
> Pour un truc un peu similaire, j'utilise cPickle (dump pour
> enregistrer, load pour charger).
Avec le protocole version 2, alors ? J'avais fait une expérience peu
concluante avec le protocole 0 (voir mon autre message de ce jour). Cela
dit, dans mon cas, je n'écris pas, je ne fais que lire. Et, au passage,
pickle ne parle pas de intern(), ce qui est décevant.
-- Alain.
Vu sa taille, j'imagine que ton fichier est auto-généré.
Dans ce cas, tu peux explicitement appeler intern() dans ton
dictionnaire, par exemple en écrivant i('toto') au lieu de 'toto'.
Par exemple :
>>> s = """ {i('a a'): i('b b'), i('b b'): i('c c')} """
>>> d = eval(s, dict(i=intern))
>>> [(id(k), id(v)) for k, v in d.items()]
[(28633200, 28633152), (28633152, 28632912)]
>>> d = eval(s, dict(i=intern))
>>> [(id(k), id(v)) for k, v in d.items()]
[(28633200, 28633152), (28633152, 28632912)]
Les ids sont les mêmes... Alors qu'en n'appelant pas intern() :
>>> d = eval(s, dict(i=lambda x: x))
>>> [(id(k), id(v)) for k, v in d.items()]
[(28632912, 28633152), (28633152, 28633200)]
>>> d = eval(s, dict(i=lambda x: x))
>>> [(id(k), id(v)) for k, v in d.items()]
[(28633056, 28633200), (28633200, 28632912)]
De nouvelles chaînes ont été créées et détruites.
a+
Antoine.