J'ai une certaine expérience de l'écriture de petits outils dans Haskell et je le trouve très intuitif à utiliser, en particulier pour écrire des filtres (utilisant interact
) qui traitent leur entrée standard et la dirigent vers la sortie standard.
Récemment, j'ai essayé d'utiliser un tel filtre sur un fichier qui était environ 10 fois plus gros que d'habitude et j'ai eu une Stack space overflow
erreur.
Après avoir fait quelques lectures (par exemple ici et ici ), j'ai identifié deux lignes directrices pour économiser de l'espace de pile (Haskellers expérimentés, veuillez me corriger si j'écris quelque chose qui n'est pas correct):
- Évitez les appels de fonction récursifs qui ne sont pas récursifs (c'est valable pour tous les langages fonctionnels qui prennent en charge l'optimisation des appels de queue).
- Introduisez
seq
pour forcer une évaluation précoce des sous-expressions afin que les expressions ne deviennent pas trop grandes avant d'être réduites (ceci est spécifique à Haskell, ou au moins aux langues utilisant l'évaluation paresseuse).
Après avoir introduit cinq ou six seq
appels dans mon code, mon outil fonctionne à nouveau sans problème (également sur les données plus volumineuses). Cependant, je trouve que le code d'origine était un peu plus lisible.
Comme je ne suis pas un programmeur Haskell expérimenté, je voulais demander si l'introduction seq
de cette manière est une pratique courante et à quelle fréquence on verra normalement seq
dans le code de production Haskell. Ou existe-t-il des techniques qui permettent d'éviter d'utiliser seq
trop souvent tout en utilisant peu d'espace de pile?
Réponses:
Malheureusement, il y a des cas où l'on doit utiliser
seq
pour obtenir un programme efficace / bien fonctionner pour de grandes données. Donc, dans de nombreux cas, vous ne pouvez pas vous en passer dans le code de production. Vous pouvez trouver plus d'informations dans Real World Haskell, Chapitre 25. Profilage et optimisation .Cependant, il existe des possibilités pour éviter d'utiliser
seq
directement. Cela peut rendre le code plus propre et plus robuste. Quelques idées:interact
. IO paresseux est connu pour avoir des problèmes avec la gestion des ressources (pas seulement la mémoire) et les itérés sont conçus exactement pour surmonter cela. (Je suggérerais d'éviter les E / S paresseux, quelle que soit la taille de vos données - voir Le problème des E / S paresseuses .)seq
directement (ou de concevoir vos propres) combinateurs tels que foldl ' ou foldr' ou des versions strictes de bibliothèques (telles que Data.Map.Strict ou Control.Monad.State.Strict ) qui sont conçues pour des calculs stricts.seq
par une correspondance stricte des motifs. La déclaration de champs constructeurs stricts pourrait également être utile dans certains cas.rseq
) ou NF complet (rdeepseq
). Il existe de nombreuses méthodes utilitaires pour travailler avec des collections, combiner des stratégies, etc.la source
ByteString
.