diff --git a/fifo/Makefile b/fifo/Makefile new file mode 100644 index 0000000..820c401 --- /dev/null +++ b/fifo/Makefile @@ -0,0 +1,13 @@ +# Nom de l'exécutable +TARGET = cache_simulator + +# Fichier source +SRC = cache_fifo.c + +# Règle par défaut : compilation et lien +all: + gcc $(SRC) -o $(TARGET) -lm -Wall + +# Nettoyage des fichiers générés +clean: + rm -f $(TARGET) diff --git a/fifo/README.md b/fifo/README.md new file mode 100644 index 0000000..a65ef11 --- /dev/null +++ b/fifo/README.md @@ -0,0 +1,66 @@ +# Simulateur de Cache - Version FIFO + +## 📖 Description + +Le programme `cache_fifo.c` simule le comportement d'un cache de processeur en utilisant la politique FIFO. Il permet de suivre les hits et les misses ainsi que d'analyser les performances du cache en fonction des références mémoire fournies. + + +## 📦 Compilation +Pour compiler le programme, utilisez la commande suivante : +```bash +gcc -o cache_simulator cache_fifo.c -lm +``` + +ou + +```bash +make +``` + +## ⚙️ Exécution +Pour exécuter le programme, utilisez la ligne de commande suivante : +```c +./cache_simulator -s <cache_size(KB)> -l <line_size> -a <associativity> [-c] [-W] < <trace_file> +``` + +## 📝 Arguments +* `-s <cache_size(KB)>` : Taille du cache en kilooctets. (Obligatoire) +Exemple : -s 64 -> taille du cache de 64 KB + +* `-l <line_size>` : Taille de la ligne de cache en octets. (Obligatoire) +Exemple : -l 16 -> taille de ligne de 16 octets + +* `-a <associativity>` : Associativité du cache (nombre de lignes par ensemble). (Obligatoire) +Exemple : -a 4 -> associativité de 4 lignes + +* `-c` : Activer l'écriture en mode Write Back. (Optionnel) +Exemple : -c -> active le mode Write Back + +* `-W` : Activer la stratégie d'allocation d'écriture (Write Allocate). (Optionnel) +Exemple : -W -> active l'allocation d'écriture + +## 📚 Fonctionnement de FIFO (First-In, First-Out) + +La politique de remplacement **First-In, First-Out (FIFO)** est une méthode utilisée pour gérer les lignes de cache lorsque le cache est plein et qu'il est nécessaire de remplacer une ligne. FIFO remplace la ligne de cache qui a été chargée en premier, en supposant que les lignes les plus anciennes sont les moins susceptibles d'être utilisées à nouveau. + +### Principe de Fonctionnement + +1. **Gestion de l'Ordre**: Chaque ligne de cache est gérée dans l'ordre dans lequel elle a été ajoutée au cache. La première ligne à être ajoutée sera la première à être remplacée lorsque le cache sera plein. + +2. **Ajout de Lignes**: Lorsqu'une nouvelle ligne de cache doit être ajoutée, elle est insérée à la fin de la liste des lignes en cache. + +3. **Remplacement**: Lorsque le cache atteint sa capacité maximale et qu'un nouvel accès nécessite l'allocation d'une ligne de cache, FIFO remplace la ligne qui a été la plus longtemps présente dans le cache. + +### Avantages et Inconvénients + +- **Avantages**: + - FIFO est simple à mettre en œuvre, car il nécessite peu de gestion de l'état par rapport à d'autres algorithmes comme LRU. + - Le mécanisme de remplacement est "constant" en temps, ce qui peut être bénéfique dans certaines situations. + +- **Inconvénients**: + - FIFO peut parfois être moins efficace que LRU, car il ne prend pas en compte la fréquence d'accès des lignes. Il est possible de remplacer une ligne encore fréquemment utilisée simplement parce qu'elle a été ajoutée au cache plus tôt. + - Le phénomène de "Belady's Anomaly" peut survenir, où augmenter la taille du cache entraîne en fait un plus grand nombre de misses. + + +## 🛠️ Auteur +- Moncef STITI diff --git a/fifo/cache_fifo.c b/fifo/cache_fifo.c new file mode 100644 index 0000000..1a7a44b --- /dev/null +++ b/fifo/cache_fifo.c @@ -0,0 +1,726 @@ +/* + * Fichier : cache_fifo.c + * Auteur : Moncef STITI + * Date : 06/11/2024 + * Version : 1.0 + * Description : Ce programme permet de simuler une mémoire cache avec comme algorithme d'éviction d'une ligne "FIFO" (First In First Out). + * Compilation : Pour compiler le programme, on utilise la commande suivante : gcc cache_fifo.c -o cache_simulator -lm -Wall ou make +*/ +#include<stdio.h> +#include<sys/types.h> +#include<unistd.h> +#include<stdlib.h> +#include <ctype.h> +#include <math.h> +#include <stdbool.h> +#include <time.h> + + +#define TAILLE_INITIALE 1000 // Taille initiale du tableau d'adresses +#define INCREMENT_TAILLE 1000 // Incrément pour realloc + +// Différentes couleurs pour l'affichage des résultats +#define RESET "\x1b[0m" // Couleur de base du terminal +#define RED "\033[31m" // Rouge +#define GREEN "\033[32m" // Vert +#define YELLOW "\033[33m" // Jaune +#define MAGENTA "\033[35m" // Violet/Magenta +#define CYAN "\033[36m" // Cyan +#define WHITE "\033[37m" // Blanc +#define COULEUR_BORDURE CYAN // Couleur des bordures + +// Structure représentant une ligne de cache. +struct line +{ + unsigned int valid; // Indicateur de validité : 1 si la ligne est valide, 0 sinon. + unsigned int dirty; // Indicateur de "saleté" (dirty) : 1 si la ligne a été modifiée, 0 sinon. + unsigned int tag; // Tag de la ligne de cache utilisé pour identifier les données stockées. + unsigned int fifo_position; // Position dans la file FIFO +}; + +// Structure représentant un ensemble de lignes de cache. +struct set +{ + struct line * lines; // Pointeur vers un tableau de lignes de cache dans cet ensemble. + unsigned int fifo_head; // Tête de la file (indique la ligne à remplacer en premier). + unsigned int fifo_tail; // Queue de la file (indique la prochaine position à remplir). +}; + +// Structure contenant les tailles des différents composants d'une adresse mémoire. +struct adresse_info +{ + unsigned int taille_adresse; // Taille totale de l'adresse mémoire (en bits). + unsigned int taille_offset; // Taille de l'offset (en bits). + unsigned int taille_index; // Taille de l'index (en bits). + unsigned int taille_tag; // Taille du tag (en bits). +}; + +// Structure contenant les comptes des actions de cache (hits et misses). +struct cache_action +{ + unsigned int instruction_reference_count; // Compte des références d'instructions (total). + unsigned int instruction_hit_count; // Compte des hits pour les instructions. + unsigned int instruction_miss_count; // Compte des misses pour les instructions. + unsigned int data_read_reference_count; // Compte des références de lecture de données (total). + unsigned int data_read_hit_count; // Compte des hits pour les lectures de données. + unsigned int data_read_miss_count; // Compte des misses pour les lectures de données. + unsigned int data_write_reference_count; // Compte des références d'écriture de données (total). + unsigned int data_write_hit_count; // Compte des hits pour les écritures de données. + unsigned int data_write_miss_count; // Compte des misses pour les écritures de données. + unsigned int memory_accesses; // Compte total des accès à la mémoire (tous types). +}; + + +// Structure principale représentant une cache. +struct cache +{ + unsigned int taille_cache; // Taille totale de la mémoire cache (en octets). + unsigned int taille_ligne; // Taille d'une ligne de cache (en octets). + unsigned int associativite; // Niveau d'associativité de la mémoire cache (directement mappée, associée par ensembles, etc.). + unsigned int nombre_sets; // Nombre total d'ensembles dans la cache. + unsigned int write_policy; // Politique d'écriture : 0 pour Write Through, 1 pour Write Back. + unsigned int write_allocate; // Politique d'allocation lors des écritures : 0 pour pas d'allocation, 1 pour Write Allocate. + + struct adresse_info addr_info; // Informations sur les tailles d'adresse dans la cache. + + struct set * sets; // Pointeur vers un tableau d'ensembles de lignes de cache. + struct cache_action action; // Comptes des actions réalisées sur la cache (hits/misses). + + double total_access_time; // Temps total d'accès au cache + double cache_hit_time; // Temps pour un hit + double cache_miss_time; // Temps pour un miss +}; + + +// Structure représentant une référence mémoire, incluant son adresse (en hexadécimal) et son type d'accès (0, 1 ou 2). +struct reference_memoire +{ + unsigned int adresse; // Adresse mémoire en hexadécimal. + unsigned int adresse_type; // Type d'accès à l'adresse. +}; + +/* + * Fonction : calculer_tailles_adresse + * Description : Cette fonction calcule la taille de l'offset, de l'index et du tag en nombre de bits + * à partir de la taille de ligne de cache et du nombre de sets. Elle met à jour + * les informations d'adresse dans la structure de cache. + * + * Paramètre : + * - struct cache* c : Un pointeur vers la structure de cache dont les informations d'adresse doivent être calculées. + * + * Détails des calculs : + * - La taille totale de l'adresse mémoire est fixée à 32 bits. + * - La taille de l'offset est calculée comme le logarithme en base 2 de la taille de ligne, + * ce qui détermine combien de bits sont nécessaires pour adresser chaque octet dans une ligne. + * - La taille de l'index est calculée de la même manière, en utilisant le nombre de sets, + * pour déterminer combien de bits sont nécessaires pour identifier un set particulier dans la cache. + * - La taille du tag est ensuite déterminée en soustrayant les tailles d'offset et d'index + * de la taille totale de l'adresse. + * + * Erreurs : + * - Si la taille de l'offset ou de l'index est égale à -1, cela signifie que la taille de ligne ou + * le nombre de sets ne sont pas des puissances de 2, ce qui est requis pour le bon fonctionnement + * de la cache. Dans ce cas, un message d'erreur est affiché. + */ +void calculer_tailles_adresse (struct cache* c) +{ + // Taille totale de l'adresse (fixée à 32 bits) + unsigned int taille_adresse = 32; + + // Calcul de la taille de l'offset en bits. + // On utilise log2 pour déterminer combien de bits sont nécessaires pour adresser chaque octet dans une ligne. + unsigned int taille_offset = (int) ceil(log2(c->taille_ligne)); + + unsigned int taille_index = (int) ceil(log2(c->nombre_sets)); + + unsigned int taille_tag = taille_adresse - taille_offset - taille_index; + + + // Vérification des puissances de 2 + if (taille_offset == -1) + { + printf("Erreur : Taille de ligne de cache invalide (%d). La valeur doit être une puissance de 2.\n", c->taille_ligne); + } else c->addr_info.taille_offset = taille_offset; + if (taille_index == -1) + { + printf("Erreur : Nombre de sets invalide (%d). La valeur doit être une puissance de 2.\n", c->nombre_sets); + } else c->addr_info.taille_index = taille_index; + + + if (taille_offset != -1 && taille_index != -1) + { + c->addr_info.taille_adresse = taille_adresse; + c->addr_info.taille_tag = taille_tag; + } +} + +/* + * Fonction : creer_struct_cache + * Description : Cette fonction crée une structure de type cache en allouant dynamiquement + * la mémoire nécessaire pour les ensembles et les lignes en fonction des + * paramètres fournis, notamment la taille de la cache, la taille des lignes, + * la politique d'écriture, et l'associativité. + * + * Paramètre : + * - unsigned int taille_cache : Taille de la cache en Ko. + * - unsigned int taille_ligne : Taille de chaque ligne de cache en octets. + * - unsigned int write_policy : Politique d'écriture (0 pour Write Through, + * 1 pour Write Back). + * - unsigned int write_allocate : Stratégie d'allocation d'écriture + * (0 pour pas d'allocation, 1 pour + * allocation d'écriture). + * - unsigned int associativite : Niveau d'associativité de la mémoire cache. + * + * Retour : + * - struct cache* : Pointeur vers la structure de cache créée. + */ +struct cache * creer_struct_cache(unsigned int taille_cache,unsigned int taille_ligne,unsigned int write_policy,unsigned int write_allocate,unsigned int associativite) +{ + unsigned int nbr_lignes = (taille_cache * 1024)/taille_ligne; + unsigned int nombre_sets = nbr_lignes/associativite; + struct cache* val_ret = malloc(sizeof(struct cache)); + val_ret->sets = (struct set*)malloc(nombre_sets*sizeof(struct set)); + unsigned int i = 0,j; + for(i=0;i<nombre_sets;i++) + { + val_ret->sets[i].lines = (struct line *)malloc(associativite*sizeof(struct line)); + for(j=0;j<associativite;j++) + { + val_ret->sets[i].lines[j].valid=0; + val_ret->sets[i].lines[j].dirty=0; + val_ret->sets[i].lines[j].tag=0; + } + val_ret->sets[i].fifo_head = 0; // Initialisation de la tête FIFO + val_ret->sets[i].fifo_tail = 0; // Initialisation de la queue FIFO + + } + + val_ret->taille_cache = taille_cache; + val_ret->associativite = associativite; + val_ret->taille_ligne = taille_ligne; + val_ret->nombre_sets = nombre_sets; + val_ret->write_policy = write_policy; + val_ret->write_allocate = write_allocate; + val_ret->total_access_time = 0.0; + val_ret->cache_hit_time = 0.1; // Exemple : 0.1 ms par hit + val_ret->cache_miss_time = 0.5; // Exemple : 0.5 ms par miss + + + // Initialiser tous les comptes + calculer_tailles_adresse(val_ret); + val_ret->action.instruction_reference_count = 0; + val_ret->action.instruction_hit_count = 0; + val_ret->action.instruction_miss_count = 0; + val_ret->action.data_read_reference_count= 0; + val_ret->action.data_read_hit_count= 0; + val_ret->action.data_read_miss_count= 0; + val_ret->action.data_write_reference_count = 0; + val_ret->action.data_write_hit_count = 0; + val_ret->action.data_write_miss_count= 0; + val_ret->action.memory_accesses = 0; + + return val_ret; +} + +/* + * Fonction : recuperer_index + * Description : Cette fonction calcule l'index d'un ensemble dans la cache à partir d'une + * adresse mémoire donnée. L'index est déterminé en extrayant des bits + * spécifiques de l'adresse mémoire selon la structure de la cache. + * + * Paramètre : + * - unsigned int adresse : Adresse mémoire + * - struct cache* c : Pointeur vers la structure de la mémoire cache qui contient + * les informations nécessaires pour le calcul de l'index, + * notamment la taille de l'index et la taille de l'offset. + * + * Retour : + * - unsigned int : La valeur entière représentant l'index de l'ensemble + * dans la cache correspondant à l'adresse mémoire fournie. + */ +unsigned int recuperer_index(unsigned int adresse, struct cache* c) +{ + unsigned int mask = (1 << c->addr_info.taille_index) - 1; + return (adresse >> c->addr_info.taille_offset) & mask; +} + +/* + * Fonction : recuperer_tag + * Description : Cette fonction extrait le tag d'une adresse mémoire donnée en + * décalant l'adresse à droite d'un certain nombre de bits, qui est la + * somme de la taille de l'offset et de la taille de l'index. + * Le tag est utilisé pour identifier de manière unique une ligne de + * cache dans un ensemble donné. + * + * Paramètre : + * - unsigned int adresse : Adresse mémoire à partir de laquelle le tag + * doit être extrait. + * - struct cache* c : Pointeur vers la structure de la mémoire cache qui contient + * les informations nécessaires pour le calcul du tag, + * notamment la taille de l'offset et la taille de l'index. + * + * Retour : + * - unsigned int : La valeur entière représentant le tag extrait de + * l'adresse mémoire fournie. + */ +unsigned int recuperer_tag( unsigned int adresse, struct cache* c) +{ + unsigned int bits_deplacer; + bits_deplacer = c->addr_info.taille_offset + c->addr_info.taille_index; + + return adresse>> bits_deplacer; +} + + +/* + * Fonction : maj_info_fifo + * Description : Cette fonction gère la politique de remplacement FIFO. + * + * Paramètre : + * - unsigned int tag : Tag de l'adresse mémoire pour identifier la ligne + * dans la cache. + * - unsigned int index : Index de l'ensemble dans la mémoire cache où se + * trouve la ligne à mettre à jour. + * - struct cache* c : Pointeur vers la structure de la mémoire cache qui contient + * les informations sur les lignes et les valeurs FIFO + * de chaque ensemble. + * + * Retour : + * - La fonction ne retourne aucune valeur. Elle modifie les + * informations FIFO directement dans la structure de cache. + */ +void maj_info_fifo(unsigned int tag, unsigned int index, struct cache* c) +{ + bool hit = false; + + // Vérifie si une ligne correspond au tag pour mettre à jour la FIFO + for (unsigned int i = 0; i < c->associativite; i++) + { + if (c->sets[index].lines[i].valid && c->sets[index].lines[i].tag == tag) + { + // Hit trouvé, pas de remplacement nécessaire, on met juste à jour les infos + c->sets[index].lines[i].fifo_position = c->sets[index].fifo_head; // Met à jour la position FIFO + hit = true; + break; + } + } + + // Si c'est un miss, on remplace la ligne avec la plus ancienne (FIFO) + if (!hit) + { + // Remplacement en FIFO : on remplace la ligne à la position "tail" + c->sets[index].lines[c->sets[index].fifo_tail].valid = 1; // Marque la ligne comme valide + c->sets[index].lines[c->sets[index].fifo_tail].tag = tag; // Met à jour le tag + c->sets[index].lines[c->sets[index].fifo_tail].dirty = 0; // Réinitialise le bit dirty + c->sets[index].lines[c->sets[index].fifo_tail].fifo_position = c->sets[index].fifo_head; // Met à jour la position FIFO + + // Avancement de la queue FIFO pour la prochaine insertion/remplacement + c->sets[index].fifo_tail = (c->sets[index].fifo_tail + 1) % c->associativite; // Le fifo_tail se déplace en circulaire + } + + // Incrémenter "fifo_head" à chaque nouvelle insertion pour suivre l'ordre FIFO + c->sets[index].fifo_head = (c->sets[index].fifo_head + 1) % c->associativite; // Incrément circulaire également +} + + +/* + * Fonction : calcul_hits_miss + * Description : Cette fonction calcule les compteurs de hits et de misses pour une + * référence mémoire donnée. Elle vérifie si l'adresse est présente + * dans la cache, met à jour les compteurs en conséquence, et enregistre + * les informations nécessaires dans la structure de cache. + * + * Paramètre : + * - struct reference_memoire m : Structure contenant l'adresse mémoire + * et le type d'accès (lecture/écriture). + * - struct cache* c : Pointeur vers la structure de la mémoire cache. + * + * Retour : + * - void : La fonction ne retourne aucune valeur, mais met à jour les + * compteurs et les informations de la mémoire cache directement. + */ +void calcul_hits_miss( struct reference_memoire m, struct cache* c) +{ + // Récupère le tag et l'index de l'adresse mémoire + unsigned int tag = recuperer_tag(m.adresse,c); + unsigned int index = recuperer_index(m.adresse,c); + unsigned int i = 0; + + // Mesurer les temps d'accés + clock_t start_time = clock(); // Démarrer le chronomètre + + // Vérifier si la référence entraîne un miss + bool miss = true; // Initialement, on suppose qu'il y a un miss + for (i=0;i<c->associativite;i++) + { + struct line temp = c->sets[index].lines[i]; + + // Vérifie si la ligne est valide et si le tag correspond + if (temp.tag == tag && temp.valid) + { + miss = false; // Hit trouvé + break; + } + } + + + // Charger la valeur dans la cache si c'est un miss + if ( miss ) + { + c->action.memory_accesses++; // Compte l'accès mémoire + + // Gestion de la politique d'écriture pour Write Back + if (c->write_policy==1 && c->sets[index].lines[0].dirty == 1) // Write Back et set dirty + { + c->action.memory_accesses++; // Incrémente l'accès mémoire + c->sets[index].lines[0].dirty = 0; // Réinitialise le bit dirty + } + // Met à jour les lignes du set + for(i=0;i<c->associativite;i++) + { + c->sets[index].lines[i].valid = 1; // Marque la ligne comme valide + if (m.adresse_type == 1) c->sets[index].lines[i].dirty = 1; // Si c'est une écriture, marque dirty à 1 + c->sets[index].lines[i].tag = tag; // Met à jour le tag + } + + // Incrémente le compteur de miss selon le type d'adresse + switch (m.adresse_type) + { + case 0: + c->action.data_read_miss_count++; + break; + case 1: + c->action.data_write_miss_count++; + break; + case 2: + c->action.instruction_miss_count++; + break; + default: + break; + } + } + else // C'est un hit et non pas un miss + { + // Incrémente le compteur de hit selon le type d'adresse + switch (m.adresse_type) + { + case 0: + c->action.data_read_hit_count++; + break; + case 1: + c->action.data_write_hit_count++; + break; + case 2: + c->action.instruction_hit_count++; + break; + default: + break; + } + } + + // Met à jour les informations FIFO pour le tag et l'index + maj_info_fifo(tag,index,c); + + // Fin de la mesure de temps + clock_t end_time = clock(); // Arrêter le chronomètre + double elapsed_time = ((double)(end_time - start_time)) / CLOCKS_PER_SEC; // Temps écoulé en secondes + c->total_access_time += elapsed_time; // Ajouter au temps total d'accès + + // Incrémente le compteur de références selon le type d'adresse + switch (m.adresse_type) + { + case 0: + c->action.data_read_reference_count++; + break; + case 1: + c->action.data_write_reference_count++; + break; + case 2: + c->action.instruction_reference_count++; + break; + default: + break; + } + +} + +/* + * Fonction : get_hits_color + * Description : Cette fonction détermine la couleur d'affichage basée sur le pourcentage + * de hits pour une performance de cache donnée. Elle renvoie une constante + * représentant une couleur en fonction du niveau de performance. + * + * Paramètre : + * - float percentage : Pourcentage de hits, exprimé en tant que valeur + * flottante. Ce pourcentage est utilisé pour évaluer + * la performance de la mémoire cache. + * + * Retour : + * - const char* : Retourne une constante de chaîne de caractères représentant + * la couleur correspondant à la performance de la mémoire cache : + * - GREEN pour une bonne performance (≥ 80 %) + * - YELLOW pour une performance moyenne (≥ 50 % et < 80 %) + * - RED pour une mauvaise performance (< 50 %) + * + * Potentiel erreur : + * - La fonction ne gère pas les valeurs de pourcentage en dehors de la + * plage normale (0 à 100). Les entrées négatives ou supérieures à 100 + * pourraient donner des résultats inattendus. + */ +const char* get_hits_color(float percentage) +{ + if (percentage >= 80.0) { + return GREEN; // Bonne performance + } else if (percentage >= 50.0) { + return YELLOW; // Moyenne performance + } else { + return RED; // Mauvais performance + } +} + +/* + * Fonction : get_misses_color + * Description : Cette fonction détermine la couleur d'affichage basée sur le pourcentage + * de misses pour une performance de cache donnée. Elle renvoie une constante + * représentant une couleur en fonction du niveau de performance. + * + * Paramètre : + * - float percentage : Pourcentage de misses, exprimé en tant que valeur + * flottante. Ce pourcentage est utilisé pour évaluer + * la performance de la mémoire cache. + * + * Retour : + * - const char* : Retourne une constante de chaîne de caractères représentant + * la couleur correspondant à la performance de la mémoire cache : + * - GREEN pour une bonne performance (< 8 % de misses) + * - YELLOW pour une performance acceptable (≥ 8 % et < 16 %) + * - RED pour une mauvaise performance (≥ 16 % de misses) + * + * Potentiel erreur : + * - La fonction ne gère pas les valeurs de pourcentage en dehors de la + * plage normale (0 à 100). Les entrées négatives ou supérieures à 100 + * pourraient donner des résultats inattendus. + */ +const char* get_misses_color(float percentage) +{ + if (percentage < 8.0) { + return GREEN; // Bonne performance + } else if (percentage < 16.0) { + return YELLOW; // Acceptable + } else { + return RED; // Mauvais performance + } +} + +/* + * Fonction : affiche_hits_miss + * Description : Cette fonction affiche les statistiques de performance de la mémoire cache, + * y compris le nombre total d'accès, les hits et les misses, ainsi que + * les pourcentages correspondants pour les lectures et les écritures. + * Les résultats sont formatés et colorés pour une meilleure lisibilité. + * + * Paramètre : + * - struct cache * c : Pointeur vers une structure de cache contenant + * les compteurs d'accès et les statistiques + * de hits et misses. + * + * Calculs : + * - Total des accès : Somme des accès en lecture, écriture et + * instructions. + * - Accès en lecture et en écriture : Calcul des accès respectifs + * et de leurs pourcentages par rapport au total. + * - Hits et misses : Somme des hits et misses en lecture, écriture + * et instructions, avec calcul des pourcentages. + * - Conversion des accès mémoire en nombre de mots (en divisant + * par 4). + * + * Affichage : + * - Les statistiques sont affichées dans un format tabulaire + * avec des couleurs indiquant la performance : + * - Hits en vert, jaune ou rouge selon le pourcentage. + * - Misses en vert, jaune ou rouge selon le pourcentage. + * + * Potentiel erreur : + * - La fonction ne gère pas les cas où le nombre total d'accès + * est égal à zéro, ce qui pourrait entraîner une division par zéro + * lors du calcul des pourcentages. + */ +void affiche_hits_miss(struct cache * c) +{ + int accesses = c->action.data_read_reference_count + c->action.data_write_reference_count + c->action.instruction_reference_count; + int access_reads = c->action.data_read_reference_count + c->action.instruction_reference_count; + float pourcent_acc_reads = ((float)access_reads / (float)accesses) * 100; + int access_writes = c->action.data_write_reference_count; + float pourcent_acc_writes = ((float)access_writes / (float)accesses) * 100; + + int hits = c->action.data_read_hit_count + c->action.data_write_hit_count + c->action.instruction_hit_count; + float pourcent_hits = ((float)hits / (float)accesses) * 100; + int hits_reads = c->action.data_read_hit_count + c->action.instruction_hit_count; + int hits_writes = c->action.data_write_hit_count; + float pourcent_hits_reads = ((float)hits_reads / (float)accesses) * 100; + float pourcent_hits_writes = ((float)hits_writes / (float)accesses) * 100; + + int miss = c->action.data_read_miss_count + c->action.data_write_miss_count + c->action.instruction_miss_count; + float pourcent_miss = ((float)miss / (float)accesses) * 100; + int miss_reads = c->action.data_read_miss_count + c->action.instruction_miss_count; + int miss_writes = c->action.data_write_miss_count; + float pourcent_miss_reads = ((float)miss_reads / (float)accesses) * 100; + float pourcent_miss_writes = ((float)miss_writes / (float)accesses) * 100; + + c->action.memory_accesses = c->action.memory_accesses * (c->taille_ligne / 4); // Convertit en nombre de mots + + // Afficher les hits et misses de manière formatée avec couleurs + printf("%s----------------------------------------------------\n", COULEUR_BORDURE); + printf("| %sStatistiques de Cache%s |\n", WHITE, COULEUR_BORDURE); + printf("----------------------------------------------------\n"); + printf("| %sTotal Accès%s | %s%6d %s|\n", MAGENTA,COULEUR_BORDURE,WHITE, accesses, COULEUR_BORDURE); + printf("----------------------------------------------------\n"); + printf("| %sAccès en Lecture%s | %s%6d (%.2f%%)%s |\n", WHITE, COULEUR_BORDURE,WHITE, access_reads, pourcent_acc_reads, COULEUR_BORDURE); + printf("| %sAccès en Écriture%s | %s%6d (%.2f%%)%s |\n", WHITE, COULEUR_BORDURE,WHITE, access_writes, pourcent_acc_writes, COULEUR_BORDURE); + printf("----------------------------------------------------\n"); + printf("| %sTotal Hits :%s | %s%6d (%.2f%%) %s|\n", MAGENTA,COULEUR_BORDURE, get_hits_color(pourcent_hits),hits, pourcent_hits, COULEUR_BORDURE); + printf("|%s - Hits en Lecture %s | %s%6d (%.2f%%)%s |\n", WHITE,COULEUR_BORDURE,get_hits_color(pourcent_hits), hits_reads, pourcent_hits_reads, COULEUR_BORDURE); + printf("|%s - Hits en Écriture %s | %s%6d (%.2f%%)%s |\n", WHITE,COULEUR_BORDURE,get_hits_color(pourcent_hits), hits_writes, pourcent_hits_writes, COULEUR_BORDURE); + printf("----------------------------------------------------\n"); + printf("| %sTotal Misses :%s | %s%6d (%.2f%%) %s|\n", MAGENTA,COULEUR_BORDURE,get_misses_color(pourcent_miss), miss, pourcent_miss, COULEUR_BORDURE); + printf("|%s - Misses en Lecture%s | %s%6d (%.2f%%) %s|\n", WHITE,COULEUR_BORDURE, get_misses_color(pourcent_miss), miss_reads, pourcent_miss_reads, COULEUR_BORDURE); + printf("|%s - Misses en Écriture%s | %s%6d (%.2f%%) %s|\n", WHITE,COULEUR_BORDURE, get_misses_color(pourcent_miss), miss_writes, pourcent_miss_writes, COULEUR_BORDURE); + printf("----------------------------------------------------\n"); + printf("|%s Accès Mémoire%s | %s%6d %s|\n", WHITE, COULEUR_BORDURE, WHITE, c->action.memory_accesses, COULEUR_BORDURE); + printf("----------------------------------------------------\n"); + printf("| %sTemps total d'accès au cache : %.6f secondes%s |\n", MAGENTA, c->total_access_time, COULEUR_BORDURE); + printf("----------------------------------------------------%s\n", RESET); +} + +/* + * Fonction : afficher_aide + * Description : Cette fonction affiche un message d'aide détaillant comment utiliser le programme + * de simulation de cache. Elle fournit des informations sur les options disponibles + * et la syntaxe correcte pour exécuter le programme. + * + * Fonctionnalités : + * - Indique l'utilisation générale du programme. + * - Liste les options possibles avec des descriptions pour chacune, y compris + * les paramètres de taille de cache, de taille de ligne, d'associativité, + * et les options de politique d'écriture. + * - Fournit un exemple de ligne de commande illustrant l'utilisation des options. + * + * Comportement : + * - Appelle `printf` pour afficher chaque ligne d'information à l'utilisateur. + * - Utilise `exit(EXIT_SUCCESS)` pour quitter le programme après l'affichage de l'aide, + * garantissant que le programme ne continue pas à s'exécuter après avoir fourni + * les informations nécessaires. + */ +void afficher_aide() +{ + printf("Usage: ./cache [options] < fichier\n"); + printf("Options :\n"); + printf(" -h : Affiche cette aide et quitte le programme\n"); + printf(" -s <taille_cache> : Spécifie la taille du cache en Ko\n"); + printf(" -l <taille_ligne> : Spécifie la taille d'une ligne de cache en octets\n"); + printf(" -a <associativite>: Spécifie l'associativité de la mémoire cache (ex: 1, 2, 4, 8...)\n"); + printf(" -c : Active le Write Back (par défaut, Write Through est activé)\n"); + printf(" -W : Active le Write Allocate (par défaut, No Write Allocate est activé)\n"); + printf("\nExemple :\n"); + printf(" ./cache -s 1 -l 8 -W -c -a 1 < sample\n"); + printf("\n"); + exit(EXIT_SUCCESS); +} + +//Fonction pricipale qui utilise getopt pour les options et lit les informations du fichier trace +int main(int argc, char * argv[]) +{ + + if (argc < 7 || argc > 9) + { + afficher_aide(); + } + + char c; + unsigned int i = 0, j = 0; + unsigned int taille_actuelle = TAILLE_INITIALE; // Taille actuelle du tableau d'adresses + + struct reference_memoire* adresses = malloc(taille_actuelle * sizeof(struct reference_memoire)); // Allocation dynamique + if (adresses == NULL) { + fprintf(stderr, "Erreur : Allocation de mémoire échouée.\n"); + exit(EXIT_FAILURE); + } + + int taille_c = 0, taille_l = 0, write_policy = 0, write_allocate = 0, asso = 0; + + // Récupération des options via getopt + while ((c = getopt(argc, argv, "hs:l:Wca:")) != -1) { + switch (c) { + case 's': + taille_c = atoi(optarg); + break; + case 'h': + afficher_aide(); + break; + case 'l': + taille_l = atoi(optarg); + break; + case 'W': + write_allocate = 1; + break; + case 'c': + write_policy = 1; + break; + case 'a': + asso = atoi(optarg); + break; + default: + fprintf(stderr, "Usage: [-s valeur] [-l valeur] [-W] [-c] [-a valeur] < fichier\n"); + free(adresses); // Libérer la mémoire allouée avant de quitter + exit(EXIT_FAILURE); + } + } + + // Vérifier s'il n'y a pas de fichier donné en entrée standard + if (optind >= argc && isatty(fileno(stdin))) { + fprintf(stderr, "Erreur : Aucun fichier donné en entrée.\n"); + free(adresses); // Libérer la mémoire allouée + exit(EXIT_FAILURE); + } + + // Lecture des adresses depuis l'entrée standard + while (scanf("%d %x", &adresses[i].adresse_type, &adresses[i].adresse) == 2) { + i++; + + // Si on atteint la taille limite actuelle du tableau, on la double avec realloc + if (i >= taille_actuelle) { + taille_actuelle += INCREMENT_TAILLE; + adresses = realloc(adresses, taille_actuelle * sizeof(struct reference_memoire)); + + if (adresses == NULL) { + fprintf(stderr, "Erreur : Reallocation de mémoire échouée.\n"); + exit(EXIT_FAILURE); + } + } + } + + // Vérifier si aucune adresse n'a été lue (fichier vide) + if (i == 0) { + fprintf(stderr, "Erreur : Fichier vide ou données d'entrée incorrectes.\n"); + free(adresses); // Libérer la mémoire allouée + exit(EXIT_FAILURE); + } + + // Créer la structure du cache et exécuter le calcul des hits/miss + struct cache* ch; + ch = creer_struct_cache(taille_c, taille_l, write_policy, write_allocate, asso); + + for (j = 0; j < i; j++) { + calcul_hits_miss(adresses[j], ch); + } + + // Afficher les statistiques de hits et miss + affiche_hits_miss(ch); + + // Libération de la mémoire allouée dynamiquement + free(adresses); + free(ch->sets); // Libérer la mémoire allouée pour les sets et lignes + free(ch); + + return 0; +} \ No newline at end of file diff --git a/lru/Makefile b/lru/Makefile new file mode 100644 index 0000000..3c474bb --- /dev/null +++ b/lru/Makefile @@ -0,0 +1,13 @@ +# Nom de l'exécutable +TARGET = cache_simulator + +# Fichier source +SRC = cache_lru.c + +# Règle par défaut : compilation et lien +all: + gcc $(SRC) -o $(TARGET) -lm -Wall + +# Nettoyage des fichiers générés +clean: + rm -f $(TARGET) diff --git a/lru/README.md b/lru/README.md index c4e8a18..b15a19a 100644 --- a/lru/README.md +++ b/lru/README.md @@ -2,12 +2,18 @@ ## 📖 Description -Le programme `cache.c` simule le comportement d'un cache de processeur. Il permet de suivre les hits et les misses ainsi que d'analyser les performances du cache en fonction des références mémoire fournies. +Le programme `cache_lru.c` simule le comportement d'un cache de processeur. Il permet de suivre les hits et les misses ainsi que d'analyser les performances du cache en fonction des références mémoire fournies. ## 📦 Compilation Pour compiler le programme, utilisez la commande suivante : ```bash -gcc -o cache_simulator cache.c -lm +gcc -o cache_simulator cache_lru.c -lm +``` + +ou + +```bash +make ``` ## ⚙️ Exécution diff --git a/lru/cache.c b/lru/cache_lru.c similarity index 99% rename from lru/cache.c rename to lru/cache_lru.c index a922919..3d6383a 100644 --- a/lru/cache.c +++ b/lru/cache_lru.c @@ -1,10 +1,10 @@ /* - * Fichier : cache.c + * Fichier : cache_lru.c * Auteur : Moncef STITI * Date : 10/11/2024 * Version : 1.0 * Description : Ce programme permet de simuler une mémoire cache avec comme algorithme d'éviction d'une ligne "LRU" (Least Recently Used). - * Compilation : Pour compiler le programme, on utilise la commande suivante : gcc cache.c -o simulateur_cache -lm -Wall + * Compilation : Pour compiler le programme, on utilise la commande suivante : gcc cache_lru.c -o cache_simulator -lm -Wall ou simplement make */ #include<stdio.h> #include<sys/types.h> diff --git a/random/Makefile b/random/Makefile new file mode 100644 index 0000000..da3406f --- /dev/null +++ b/random/Makefile @@ -0,0 +1,13 @@ +# Nom de l'exécutable +TARGET = cache_simulator + +# Fichier source +SRC = cache_random.c + +# Règle par défaut : compilation et lien +all: + gcc $(SRC) -o $(TARGET) -lm -Wall + +# Nettoyage des fichiers générés +clean: + rm -f $(TARGET) diff --git a/random/README.md b/random/README.md index 9d3fbaf..fc3b686 100644 --- a/random/README.md +++ b/random/README.md @@ -2,12 +2,18 @@ ## 📖 Description -Le programme `cache.c` simule le comportement d'un cache de processeur. Il permet de suivre les hits et les misses ainsi que d'analyser les performances du cache en fonction des références mémoire fournies. +Le programme `cache_random.c` simule le comportement d'un cache de processeur. Il permet de suivre les hits et les misses ainsi que d'analyser les performances du cache en fonction des références mémoire fournies. ## 📦 Compilation Pour compiler le programme, utilisez la commande suivante : ```bash -gcc -o cache_simulator cache.c -lm +gcc -o cache_simulator cache_random.c -lm +``` + +ou + +```bash +make ``` ## ⚙️ Exécution diff --git a/random/cache.c b/random/cache_random.c similarity index 99% rename from random/cache.c rename to random/cache_random.c index 03c4762..30c78c1 100644 --- a/random/cache.c +++ b/random/cache_random.c @@ -1,10 +1,10 @@ /* - * Fichier : cache.c + * Fichier : cache_random.c * Auteur : Moncef STITI * Date : 02/11/2024 * Version : 1.0 * Description : Ce programme permet de simuler une mémoire cache avec comme algorithme d'éviction d'une ligne "Random" (Aléatoire). - * Compilation : Pour compiler le programme, on utilise la commande suivante : gcc cache.c -o simulateur_cache -lm -Wall + * Compilation : Pour compiler le programme, on utilise la commande suivante : gcc cache_random.c -o cache_simulator -lm -Wall ou make */ #include <stdio.h> #include <stdlib.h> diff --git a/traces/README.md b/traces/README.md index b0d5382..0b71e92 100644 --- a/traces/README.md +++ b/traces/README.md @@ -8,4 +8,4 @@ Ce répertoire contient des exemples de fichiers trace utilisés pour le simulat ## 🛠️ Auteur des fichiers traces -- Denis MONNERAT (Professeur à l'IUT de Fontainebleau) \ No newline at end of file +- Denis MONNERAT (Professeur à l'IUT de Fontainebleau) \ No newline at end of file diff --git a/traces/sample b/traces/sample index 0ae714b..4b1f055 100644 --- a/traces/sample +++ b/traces/sample @@ -13,7 +13,7 @@ 2 21f 2 223 2 220 -1 7fffcca4 +1 7fffcca4 0 54 1 7fffcca0 1 7fffcc9c @@ -998,4 +998,3 @@ 1 7fffcbbc 2 56dfb 2 56dff - diff --git a/traces/sample1 b/traces/sample1 index 815af8a..1d09398 100644 --- a/traces/sample1 +++ b/traces/sample1 @@ -13,7 +13,7 @@ 0 21f 0 223 0 220 -1 7fffcca4 +1 7fffcca4 0 54 1 7fffcca0 1 7fffcc9c @@ -46,3 +46,4 @@ 0 531d5 0 531d9 0 7fffcc84 + diff --git a/traces/sample2 b/traces/sample2 index 5320237..7ed4522 100644 --- a/traces/sample2 +++ b/traces/sample2 @@ -832475,3 +832475,4 @@ 2 432724 2 432728 2 4328 + diff --git a/traces_generator/README.md b/traces_generator/README.md index 8072550..c8044b4 100644 --- a/traces_generator/README.md +++ b/traces_generator/README.md @@ -36,3 +36,4 @@ Exemple : `-o trace_file` -> crée le fichier `trace_file` ## 🛠️ Auteur - Moncef STITI + \ No newline at end of file diff --git a/traces_generator/trace_generator.c b/traces_generator/trace_generator.c index 998e3aa..dbe8dd2 100644 --- a/traces_generator/trace_generator.c +++ b/traces_generator/trace_generator.c @@ -140,4 +140,4 @@ int main(int argc, char *argv[]) { printf("Le fichier trace '%s' avec %d lignes a été généré avec succès.\n", output_file, total_lines); return EXIT_SUCCESS; -} \ No newline at end of file +}