Gestion de la mémoire

publicité
Programmation Fonctionnelle
Stefano Guerrini
[email protected]
http://www.lipn.univ-paris13.fr/~guerrini
Gestion de la mémoire
LIPN - Institut Galilée, Université Paris Nord 13
Licence Info 3
février 2013
S. Guerrini (LIPN - Paris 13)
Programmation Fonctionnelle
Gestion de la mémoire
février 2013
1 / 131
S. Guerrini (LIPN - Paris 13)
Mémoire de programme
Programmation Fonctionnelle
Gestion de la mémoire
Des zones di↵érents de mémoire
février 2013
68 / 131
Mémoire de programme
Garbage collection
Mémoire statique
I
I
I
allouée au moment du chargement du programme (e.g., au démarrage de la
boucle interactive).
ne change pas de dimension, mais sont contenu peut se modifier au long de
l’exécution.
contient des données (ou du code) nécessaires tout au long de l’exécution du
programme (e.g., le code et les données de la boucle interactive).
Mémoire dynamique : le tas (en anglais, heap)
I
Des structures (e.g., des listes) sont ajoutées ou supprimées dynamiquement.
Pile d’exécution (en anglais : stack)
I
I
I
I
Comme dans le tas, des données sont ajoutées et supprimées dynamiquement.
Mais, dans la pile, les données sont supprimées en respectant l’ordre inverse
d’allocation (DEPS)
La pile est organisée en cadres de piles (en anglais, stack frames) qui
contiennent les données des appels de fonctions.
La pile contient aussi des valeurs intermédiaire nécessaires seulement pendant
l’évaluation di une expression de la fonction.
S. Guerrini (LIPN - Paris 13)
Programmation Fonctionnelle
février 2013
69 / 131
En OCaml on n’a pas des commandes explicites d’allocation ou de-allocation
de la mémoire.
Le structures sont alloué au moyen des constructeur du type de données
(e.g., pour les liste le cons ::).
Quand une structure (par exemple une liste) n’est plus accessible alors son
espace mémoire peut être réutilisé.
C’est la tâche du Garbage Collector (GC, parfois Ramassage de miettes), qui
est lancé quand le système OCaml a besoin de mémoire.
Comme en Java.
S. Guerrini (LIPN - Paris 13)
Programmation Fonctionnelle
février 2013
70 / 131
Gestion de la mémoire
Mémoire de programme
Gestion de la mémoire
La pile
Appel terminal
A chaque appel de fonction, OCaml alloue un cadre sur la pile.
Chaque cadre de pile stockes (parmi d’autres)
I
I
Une fonction f appèle une fonction g et le résultat de l’évaluation de g est
envoyé tout de suite par f.
les valeurs locales et les paramètres d’un appel de fonction
les informations nécessaires pour revenir au point de l’appel de la fonction
I
Quand cet appel de fonction est terminé, sa mémoire locale est balayée du
sommet de la pile.
Dans l’évaluation d’une fonction récursive on a un cadre de pile pour chaque
appel récursif.
I
I
Mémoire de programme
Pourtant, si on fait une récursion trop profonde on risque d’épuiser l’espace
disponible pour la pile.
Mais attention, le risque est réel seulement si ont fait au moins des dizaines ou
centaines de milliers d’appels récursives.
S. Guerrini (LIPN - Paris 13)
Programmation Fonctionnelle
Gestion de la mémoire
février 2013
71 / 131
exemple d’appel terminal :
let f x = if x = 0 then 0 else g x
I
exemple d’appel non-terminal :
let f x = x + g x
Dans le cas d’appel terminal, on n’a plus besoin des valeurs locales de la
fonction f quand on évalue g.
Le cadre de f sur la pile peut être libéré avant d’évaluer g.
S. Guerrini (LIPN - Paris 13)
Mémoire de programme
Programmation Fonctionnelle
Gestion de la mémoire
Récursion terminale
février 2013
72 / 131
Mémoire de programme
Réécrire une fonction en récursion terminale
Il est parfois possible de réécrire une fonction récursive en une fonction
récursive terminale équivalente.
En anglais, tail recursion.
let rec somme = function
| [] -> 0
| hd :: tl -> hd + somme tl;;
C’est une fonction récursive, avec tous les appels récursifs à des positions
terminales.
let somme ls =
let rec somme_term acc = function
| [] -> acc
| hd :: tl -> somme_term (hd + acc) tl
in somme_term 0 ls;;
Normalement, il faut ajouter des paramètres à la fonction.
let rec fold_left f b = function
| [] -> b
| h::r -> fold_left f (f b h) r;;
I
I
Peut importe la profondeur de récurrence, l’utilisation d’espace reste
constante :
Une fonction récursive terminale correspond à une version itérative de la
fonction.
Les paramètres additionnels sont les données (l’état) modifiées à chaque
itération.
let fibonacci n =
let rec fib n =
if (n = 0) then (1,0)
else let (a,b) = fib (n-1)
in (a+b, a)
in fst (fib n);;
La fonction récursive est exécutée comme une boucle d’itération !
let fibonacci n =
let rec fib (a,b) n =
if (n = 0) then (a,b)
else fib (a+b, a) (n-1)
in fst (fib (1, 0) n);;
La récursion terminale permet d’éviter des dépassements de pile (en anglais,
stack overflow).
S. Guerrini (LIPN - Paris 13)
Programmation Fonctionnelle
février 2013
73 / 131
S. Guerrini (LIPN - Paris 13)
Programmation Fonctionnelle
février 2013
74 / 131
Gestion de la mémoire
Mémoire de programme
Récursion terminale et fonctions fold
La fonction fold left est récursive terminale :
let rec fold_left f b = function
| [] -> b
| h::r -> fold_left f (f b h) r;;
La fonction fold right n’est pas récursive terminale :
let rec fold_right f l b = match l with
| [] -> b
| h::r -> f h (fold_right f r b);;
S. Guerrini (LIPN - Paris 13)
Programmation Fonctionnelle
février 2013
75 / 131
Téléchargement