718 lines
32 KiB
C
718 lines
32 KiB
C
/*
|
|
* 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)
|
|
{
|
|
struct set *current_set = &c->sets[index];
|
|
unsigned int pos_remplacement = current_set->fifo_head; // La position de remplacement est celle de la tête FIFO
|
|
|
|
// Remplacer l'ancienne ligne par la nouvelle
|
|
current_set->lines[pos_remplacement].tag = tag;
|
|
current_set->lines[pos_remplacement].valid = 1; // Ligne maintenant valide
|
|
current_set->lines[pos_remplacement].dirty = 0; // Par défaut, pas encore "salie"
|
|
|
|
// Mise à jour de la position FIFO (circular buffer)
|
|
current_set->fifo_head = (current_set->fifo_head + 1) % c->associativite; // Avancer la tête
|
|
}
|
|
|
|
/*
|
|
* 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) {
|
|
unsigned int index = recuperer_index(m.adresse, c);
|
|
unsigned int tag = recuperer_tag(m.adresse, c);
|
|
struct set* current_set = &c->sets[index];
|
|
|
|
clock_t start_time = clock(); // Démarrer le chronomètre
|
|
|
|
// Logique pour les accès en lecture
|
|
if (m.adresse_type == 0) { // 0 pour lecture
|
|
c->action.data_read_reference_count++;
|
|
|
|
// Vérification des hits
|
|
for (unsigned int i = 0; i < c->associativite; i++) {
|
|
if (current_set->lines[i].valid && current_set->lines[i].tag == tag) {
|
|
// Hit
|
|
c->action.data_read_hit_count++;
|
|
// 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
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Miss
|
|
c->action.data_read_miss_count++;
|
|
// Incrémenter le compteur total d'accès
|
|
c->action.memory_accesses++;
|
|
|
|
// Gérer l'insertion dans la cache (FIFO)
|
|
if (current_set->fifo_tail < c->associativite) {
|
|
// Insérer dans une ligne vide
|
|
current_set->lines[current_set->fifo_tail].valid = 1;
|
|
current_set->lines[current_set->fifo_tail].tag = tag;
|
|
current_set->fifo_tail++;
|
|
} else {
|
|
// Remplacer selon FIFO
|
|
maj_info_fifo(tag, index, c); // Appel à la fonction de mise à jour
|
|
}
|
|
}
|
|
// Logique pour les écritures
|
|
else if (m.adresse_type == 1) { // 1 pour écriture
|
|
c->action.data_write_reference_count++;
|
|
|
|
// Vérification des hits
|
|
for (unsigned int i = 0; i < c->associativite; i++) {
|
|
if (current_set->lines[i].valid && current_set->lines[i].tag == tag) {
|
|
// Hit
|
|
c->action.data_write_hit_count++;
|
|
current_set->lines[i].dirty = 1; // Marquer comme "dirty"
|
|
// 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
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Miss
|
|
c->action.data_write_miss_count++;
|
|
// Incrémenter le compteur total d'accès
|
|
c->action.memory_accesses++;
|
|
|
|
// Gérer l'insertion dans la cache (FIFO)
|
|
if (current_set->fifo_tail < c->associativite) {
|
|
// Insérer dans une ligne vide
|
|
current_set->lines[current_set->fifo_tail].valid = 1;
|
|
current_set->lines[current_set->fifo_tail].tag = tag;
|
|
current_set->lines[current_set->fifo_tail].dirty = 1; // Marquer comme "dirty"
|
|
current_set->fifo_tail++;
|
|
} else {
|
|
// Remplacer selon FIFO
|
|
maj_info_fifo(tag, index, c); // Appel à la fonction de mise à jour
|
|
}
|
|
}
|
|
// Logique pour les instructions
|
|
else if (m.adresse_type == 2) { // 2 pour instruction
|
|
c->action.instruction_reference_count++;
|
|
|
|
// Vérification des hits
|
|
for (unsigned int i = 0; i < c->associativite; i++) {
|
|
if (current_set->lines[i].valid && current_set->lines[i].tag == tag) {
|
|
// Hit
|
|
c->action.instruction_hit_count++;
|
|
// 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
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Miss
|
|
c->action.instruction_miss_count++;
|
|
// Incrémenter le compteur total d'accès
|
|
c->action.memory_accesses++;
|
|
|
|
// Gérer l'insertion dans la cache (FIFO)
|
|
if (current_set->fifo_tail < c->associativite) {
|
|
// Insérer dans une ligne vide
|
|
current_set->lines[current_set->fifo_tail].valid = 1;
|
|
current_set->lines[current_set->fifo_tail].tag = tag;
|
|
current_set->fifo_tail++;
|
|
} else {
|
|
// Remplacer selon FIFO
|
|
maj_info_fifo(tag, index, c); // Appel à la fonction de mise à jour
|
|
}
|
|
}
|
|
|
|
// Fin de la mesure de temps pour les cas de misses
|
|
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
|
|
}
|
|
|
|
|
|
/*
|
|
* 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;
|
|
} |