INFO 2014 Fichiers et base de données Philippe Fournier-Viger Département d’informatique, U.de Moncton Bureau D216, Hiver
Calendrier – cours 2 2 Aujourd’hui: suite chapitre 8 – traitement de fichiers
L’UTILISATION DE TAMPONS ( BUFFERS ) 3
Introduction Améliorer la performance des logiciels utilisant la mémoire secondaire peut être fait en réduisant le nombre d’accès disque. Plusieurs techniques: ◦ algorithmes adaptés ◦ représentation des données adaptée ◦ utilisation de tampons sujet aujourd’hui taille des tampons, utilisation de plusieurs tampons, type de tampons, etc. ◦ compression des données, etc. 4
Mise en tampon secteur Observation: ◦ Le temps pour lire un octet sur un disque dur peut être la moitié du temps pour lire une piste (en raison du temps de positionnement). Pour cette raison, la plupart des disques dur lisent au moins un secteur complet à la fois et le conservent en mémoire. C’est la mise en tampon secteur. 5
La mise en tampon secteur (suite) 6
En pratique, de nombreux logiciels effectuent souvent des requêtes pour un même secteur dans un court laps de temps. Pour cette raison, l’utilisation d’un tampon améliore généralement la performance. L’utilisation de tampons est l’une des raisons pour laquelle les disques durs modernes sont plus performants. 7
Où est effectué la mise en tampon? Généralement ◦ au niveau du système d’exploitation ◦ ou du contrôleur du disque dur. Elle pourrait aussi être faite par un logiciel d’application ayant des besoins spécialisés. Note: le terme « cache » est aussi parfois utilisé pour désigner le concept de « tampon ». 8
Tampons d’entrée et de sortie Un système d’exploitation va offrir deux types de tampons. Tampon d’entrée: ◦ parce qu’une même valeur peut être lue plusieurs fois, ◦ parce que des valeurs dans l’entourage de la valeur lue peuvent être lues. Tampon de sortie: ◦ parce qu’une valeur écrite peut être réécrite peu de temps après. ◦ parce qu’une valeur écrite peut être lue peu de temps après. Si on utilisais un tampon unique commun pour lecture et écriture, ◦ il faudrait écraser les données de lecture pour écriture et vice-versa. 9
Mémoire tampons multiples En pratique, un microprocesseur (CPU) peut exécuter plusieurs millions/milliards d’opérations par secondes. Un disque dur fonctionne beaucoup plus lentement. Cela affecte la performance! Exemple: Lecture séquentielle d’un fichier ◦ Le disque dur lit un secteur dans un tampon ◦ Pendant ce temps, le CPU attend que le tampon soit rempli ou trouve autre chose à faire. ◦ Le CPU traite le secteur. ◦ Le disque dur lit le secteur suivant… ◦ … 10
Mémoire tampons multiples (suite) Or, le CPU et le disque dur pourraient travailler en parallèle. Comment ? ◦ Une technique est l’utilisation de mémoires tampon multiples (double-buffering, multiple-buffering). Exemple: avec deux tampons: ◦ le disque dur lit le premier secteur dans un tampon, ◦ le CPU traite le secteur. En parallèle, le disque dur lit le prochain secteur dans le second tampon ◦ … 11
Mémoire tampons multiples (suite) Les tampons multiples sont aussi utilisés dans les jeux vidéos lorsque le temps pour dessiner une image est supérieur au temps de rafraichissement de l’écran. Le logiciel écrit l’image à afficher à l’écran dans un tampon pendant que la carte graphique lit le contenu de l’autre tampon. Ensuite, les tampons sont inversé et le processus continue. Tampon 1Tampon 2 12
Les bassins de tampons Une autre façon d’améliorer la performance est d’utiliser un ensemble de tampons afin de mettre plusieurs parties d’un fichier en tampon simultanément. Un ensemble de tampons est appelé un bassin de tampons (buffer pool). Une partie d’un fichier est appelée un bloc. Un bassin de tampon peut-être utilisé par le système d’exploitation ou par des logiciels. Un tampon peut être utilisé pour lecture/écriture. 13
Les bassins de tampons (suite) Fonctionnement: ajout de données quand le bassin est non plein, les nouvelles données sont ajoutées dans un des tampons disponibles, quand le bassin est plein, un tampon est vidé pour faire de la place pour les nouvelles données. Tampon 1 Tampon 2 Tampon n … 0001…0111… 14
Les bassins de tampons (suite) Le bassin est plein! quel tampon vider? Choisir aléatoirement? Idéalement, il faudrait vider celui dont l’information a le moins de chance d’être réutilisée prochainement. Malheureusement, on ne connaît pas le futur! Il faut utiliser des heuristiques. Tampon 1 Tampon 2 Tampon n … 0001…0111… …
Les bassins de tampons (suite) Heuristique 1: utilisation d’une file « premier entré, premier sorti » (FIFO) ◦ lors d’une lecture, le premier tampon est rempli puis mis en fin de file, ◦ lorsqu’il n’y a plus de tampons vides, le tampon vidé est celui pour lequel le plus de temps s’est écoulé depuis la première lecture (celui en début de la file). ◦ note: même si un tampon est lu plusieurs fois, sa position dans la file n’est pas modifiée. ◦ Cette heuristique fonctionne bien pour une lecture approximativement séquentielle. 16
Les bassins de tampons (suite) Heuristique 2: retirer l’information la moins fréquemment utilisé ◦ un compteur est ajouté à chaque tampon, ◦ le compteur est incrémenté à chaque utilisation du tampon (lecture/écriture), ◦ le tampon avec la valeur de compteur la plus faible est vidé en premier. Si égalité, le numéro de tampon peut être utilisé pour trancher. Tampon 1 Tampon 2 Tampon n … 0001…0111… #Utilisations = 1#Utilisations = 15#Utilisations = 23 17
Les bassins de tampons (suite) ◦ Limite : ce qui est pertinent dans le passé peut ne pas l’être dans le futur ◦ Une solution: ajout d’un mécanisme d’expiration. Périodiquement, les compteurs sont décrémentés par une constante (ex.: 1). Tampon 1 Tampon 2 Tampon n … 0001…0111… #Utilisations = 1#Utilisations = 15#Utilisations = 23 18
Les bassins de tampons (suite) Heuristique 3: retirer l’information la plus anciennement utilisée ◦ une variable « dernier accès » est ajoutée à chaque tampon, ◦ lors de chaque lecture/écriture, le temps actuel est écris dans cette variable. ◦ le tampon le plus anciennement utilisée est vidé en premier. ◦ facile à implémenter, une bonne approximation Tampon 1 Tampon 2 Tampon n … 0001…0111… dernier_accès=… 19
Les bassins de tampons (suite) Heuristique 3: retirer l’information la plus anciennement utilisée (suite) ◦ Note: Plutôt que d’utiliser une variable « dernier accès » pour implémenter cette heuristique, on pourrait utiliser une file. Tampon 1 Tampon 2 Tampon n … 0001…0111… 20
Les bassins de tampons (suite) Heuristique 4: retirer l’information la moins probable d’être utilisée selon un modèle d’usage Pour des applications spécialisée demandant une haute performance, l’usage du disque peut être étudié. Un modèle d’usage du disque est créé pour prédire les données les plus probable d’être utilisées dans le futur. Les tampons vidés sont ceux qui ont le moins de chance d’être utilisés. 21
Les bassins de tampons (suite) Pour l’écriture: Observation: ◦ Il n’est pas efficient de sauvegarder les changements à un bloc immédiatement sur le disque dur, car il peut y avoir des changements répétés à ce même bloc. Pour améliorer la performance: ◦ ne sauvegarder un bloc que lorsque le fichier est fermé ou que le tampon le contenant est vidé. ◦ On peut utiliser un bit sale (dirty bit) pour marquer chaque bloc qui doit être sauvegardé (pour indiquer s’il y a eu un changement ou non). 22
Application: la mémoire virtuelle La mémoire virtuelle ◦ une mémoire vive simulée, stockée sur le disque dur. ◦ gérée par le système d’exploitation. ◦ permet de dépasser la limite de mémoire vive d’un ordinateur, ◦ est transparente pour le programmeur. Limite: ◦ performance moindre, car les accès au disque dur sont lents. 23
Sous Windows 7: ◦ paramétrer la mémoire virtuelle: om/fr-FR/windows- vista/Change-the-size-of- virtual-memory om/fr-FR/windows- vista/Change-the-size-of- virtual-memory ◦ Stockée dans: C:/pagefile.sys Application: la mémoire virtuelle 24
Application: la mémoire virtuelle Fonctionnement: ◦ un bassin de tampon est créé pour la mémoire virtuelle, ◦ quand un bloc est utilisé, il est chargé en mémoire vive dans le bassin de tampon, ◦ les blocs les moins fréquemment utilisés font places aux blocs fréquemment utilisés. 25
Application: la mémoire virtuelle Une illustration: mémoire secondaire mémoire vive (RAM) 26
Application: la mémoire virtuelle (suite) Exercice: Soit une mémoire virtuelle de 10 secteurs. Soit un bassin de tampons de 5 blocs. Heuristique 1: retirer le plus ancien selon une file. La série de requêtes suivante est effectuée: 9,0,1,7,6,6,8,1,3,5,1,7,1 Quel est le contenu du bassin de tampon après ces requêtes? 27
IMPLÉMENTATION D’UN BASSIN DE TAMPONS 28
Implémentation d’un bassin de tampons Deux façons de communiquer avec un bassin de tampon: soit par passage de messages, soit par passage de tampons. 29
Communication par passage de messages Le bassin de tampons fourni: Une méthode pour lire un octet à une position logique. Une méthode pour écrire un octet à une position logique. Tous les détails du fonctionnement du bassin sont masqués à l’utilisateur de la classe. 30
Exemple: Fichier stocké sur des secteurs de 1024 octets. Un logiciel fait une requête d’écriture de 40 octets à la position 6000 du fichier, Le tampon 5 du bassin contient les octets 5120 à 6143 du fichier. Résultat: ◦ La classe BufferPool copie les 40 octets à la position du tampon 5. Lorsque le fichier est fermé ou que le tampon est retiré du bassin, la modification est écrite sur le disque. Communication par passage de messages 31
Alternative: ◦ donner accès aux tampons à l’utilisateur. ◦ l’utilisateur doit connaître la taille des blocs stockés dans les tampons. ◦ Trois méthodes: obtenir la taille d’un bloc (une constante) obtenir un pointeur vers un bloc, changer la valeur du « bit sale » pour indiquer une modification du bloc, Communication par passage de tampons 32
Communication par passage de tampons 33
Problème de pointeurs désuets: le contenu d’un tampon peut être remplacé ou effacé de telle sorte qu’un pointeur vers un bloc n’est plus valide. Solution: verrouillage des tampons ◦ ajouter deux méthodes: réserver un bloc acquireBuffer() – lit le tampon et le réserve libérer un bloc releaseBuffer() - libère le tampon ◦ ajouter une variable compteur pour compter le nombre d’utilisateurs du bloc. ◦ Un bloc ne peut être effacé d’un tampon que lorsque le compteur est à 0. Communication par passage de tampons 34
Le verrouillage des tampons peut être problématique si: ◦ l’usager oublie de libérer des blocs, ◦ il y a plus de blocs actifs que de tampons disponibles dans le bassin! Il est difficile d’éviter ces problèmes (pourrait être géré au niveau du système d’exploitation – ex.: quand un programme est fermé, les blocs sont libérés) Communication par passage de tampons 35
Observation: parfois l’utilisateur veut réécrire complètement un bloc et ne souhaite pas le lire. La lecture du bloc est donc inutile et réduit la performance. Optimisation: séparer l’assignement des blocs et la lecture des blocs. ◦ acquireBuffer() ne lit plus les bloc sur le disque. ◦ un méthode readBlock() doit être appelée pour lire le bloc sur le disque lorsque voulu. Communication par passage de tampons 36
Code (1) 37
Code (2) 38
Désavantage du passage par tampons: plusieurs obligations à respecter pour le programmeur: ◦ prendre connaissances de la taille des blocs, ◦ ne pas corrompre l’espace de stockage, ◦ informer le bassin quand un bloc est modifié, ◦ informer le bassin quand un bloc est réservé ou libéré. Avantages: Communication par passage de tampons 39
Avantages: ◦ pas besoin de faire de copie de blocs pour l’utilisateur (néanmoins, ceci n’est pas coûteux comparativement à la lecture du disque si peu de données), ◦ plus de contrôle: peut réserver un bloc pour le forcer à rester en mémoire, pour plusieurs écritures/lectures. Communication par passage de tampons 40
Autres applications Le concept de tampon est utilisé dans plusieurs domaines de l’informatique où le temps d’accès a une ressource peut être long ◦ ex.: accès à une base de données ◦ ex.: accès à une ressource sur un réseau Ou bien pour un stockage temporaire quand deux processus ne travaillent pas à la même vitesse: ◦ ex.: routeur, réseau… ◦ analogie: manège 41
MANIPULATION DES FICHIERS EN C++ (RAPPEL) 42
Les fichiers en C++ Un fichier, un flux d’octets Trois instructions principales: ◦ lire des octets à la position actuelle dans le fichier, ◦ écrire des octets à la position actuelle, ◦ déplacer la position dans le fichier. La mise en tampon est faite au niveau du contrôleur du disque principalement. Lecture: ◦ accès séquentiel ◦ accès aléatoire: plus efficace lorsque les fichiers sont stockés sur des secteurs/groupes/pistes contigus. 43
Les fichiers en C++ (suite) Plusieurs mécanismes pour manipuler les fichiers binaires. Fstream ◦ open(char *name, openmode flags) ◦ read(char *buff, int count) ◦ write(char *buff, int count) ◦ seekg(int pos) // lecture ◦ seekp(int pos) // écriture ◦ close() similaire au passage par message. 44
Fstream – ofstream ofstream: pour écrire dans un fichier Même principe que cout open(« Text.txt », ios::binary) pour ouvrir en mode binaire 45
Fstream – ifstream – exemple 1 ifstream: pour lire un fichier ne doit pas contenir d’espace car >> utilise le caractère espace pour diviser le flux. 46
Fstream – ifstream – exemple 2 ifstream: pour lire un fichier si pas la fin du fichier, erreur de lecture ou autres erreur. getLine permet de lire les espaces contrairement à l’opérateur >> 47
Fichier binaire position: fin du fichier position: début du fichier un type d’entier (peut être converti à integer) 48
À l’interne Les flux de fichiers (file streams) utilisent un objet streambuf qui est un bassin de tampons. L’écriture se fait au fichier quand: ◦ le fichier est fermé, ◦ le bassin est plein, ◦ certains caractères sont insérés dans le flux (endl, flush, …), ◦ sync(). 49
Les flux en C++ permettent de manipuler des données (fichiers, console, écran, mémoire) avec les mêmes instructions (en écriture ou lecture). Les classes pour manipuler les flux font parties de la librairie iostream. include classe « ios » classe « istream »classe « ostream » … cout … cin 50
Les flux en C++ opérateur << pour écrire opérateur >> pour lire Quatre flux prédéfinis en C++: ◦ cin: flux d’entrée standard ◦ cout: flux de sortie standard ◦ cerr: sortie d’erreur standard ◦ clog: écrire un fichier d’historique et utiliser la sortie d’erreur standard 51
Manipulateurs de flux Certains manipulateurs peuvent être utilisés dans les flux: ◦ endl : ajouter un saut de ligne ( \n ) ◦ flush : vider un flux ◦ ends : ajouter un caractère de fin de chaîne ( \0 ) ◦ … ◦ ws: supprimer les espaces ◦ dec : convertir en décimale ◦ hex, oct :… 52
cin, cout - exemple 53
Les flux en C++ Attention: Le type des variables et la taille des variables ne sont pas vérifiés avec l’opérateur >>. Solution 1: Solution 2: string input; cin >> input; Note: Solution 1 peut tronquer l’entrée de l’utilisateur. 54
Surcharge de << Il est possible de surcharger l’opérateur << pour accepter d’autres types. Par exemple: 55
Surcharge de >> Il est aussi possible de surcharger l’opérateur >>. Par exemple: 56
Surcharge de >> On peut aussi redéfinir les opérateurs > pour les pointeurs. Exemple: Note: getLine autorise la saisie de texte avec espaces contrairement à >> … 57