2024-10-10 20:10:01 +02:00

414 lines
17 KiB
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_random.c -o cache_simulator -lm -Wall ou make
*/
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h> // Pour les options en argument
#include <time.h> // Pour les mesures de temps
// Différentes couleurs pour l'affichage des résultats
#define RESET "\x1b[0m" // Couleur de base du terminal
#define GREEN "\x1b[32m" // Vert
#define YELLOW "\x1b[33m" // Jaune
#define RED "\x1b[31m" // Rouge
#define CYAN "\033[36m" // Cyan
#define MAGENTA "\x1b[35m" // Violet/Magenta
#define WHITE "\x1b[37m" // Blanc
#define COULEUR_BORDURE CYAN // Cyan
// Structure représentant une ligne de cache.
struct line
{
unsigned int valid; // Indicateur de validité
unsigned int dirty; // Indicateur de "saleté" (dirty)
unsigned int tag; // Tag de la ligne de cache
}; typedef struct line line;
// Structure représentant un ensemble de lignes de cache.
struct Set
{
struct line *lines;
}; typedef struct Set Set;
// Structure représentant le cache lui-même.
struct Cache
{
struct Set *sets;
}; typedef struct Cache Cache;
/*
* Fonction : ecrire_ligne_en_memoire
* Description : Cette fonction simule l'écriture d'une ligne de cache évincée
* dans la mémoire principale. Dans le contexte de la simulation,
* la fonction n'effectue pas d'écriture réelle, mais elle peut
* être utilisée pour afficher des informations de débogage concernant
* l'éviction d'une ligne "dirty" (modifiée).
*
* Paramètre :
* - line *evictedLine : Pointeur vers la ligne de cache qui a été évincée.
*
* Retour :
* - void : Cette fonction ne retourne rien, car elle est conçue pour
* effectuer une action (écriture dans la mémoire) plutôt que de
* fournir une valeur de retour.
*/
void ecrire_ligne_en_memoire(line *evictedLine)
{
// Comme il s'agit d'une simulation, nous ne faisons pas d'écriture réelle ici.
// Mais voici comment afficher la ligne :
// printf("Ecriture de la ligne %d dans la mémoire centrale\n", evictedLine->tag);
}
/*
* Fonction : creer_struct_cache
* Description : Cette fonction initialise une structure de cache en allouant
* dynamiquement la mémoire pour les ensembles et les lignes de cache.
* Chaque ligne est initialisée avec des valeurs par défaut pour
* les indicateurs de validité, de saleté (dirty) et le tag.
*
* Paramètres :
* - Cache *cache : Pointeur vers la structure de cache à initialiser.
* - int numSets : Nombre d'ensembles dans la cache. C'est le nombre total
* de groupes de lignes de cache.
* - int associativite : Le degré d'associativité de la mémoire cache.
*
* Retour :
* - void : Cette fonction ne retourne rien, car elle modifie directement
* la structure de cache passée en paramètre.
*/
void creer_struct_cache(Cache *cache, int numSets, int associativite)
{
cache->sets = (Set *)malloc(numSets * sizeof(Set));
for (int i = 0; i < numSets; i++) {
cache->sets[i].lines = (line *)malloc(associativite * sizeof(line));
for (int j = 0; j < associativite; j++) {
cache->sets[i].lines[j].valid = 0;
cache->sets[i].lines[j].dirty = 0;
cache->sets[i].lines[j].tag = -1;
}
}
}
/*
* Fonction : acceder_cache
* Description : Cette fonction simule l'accès à un cache à partir d'une
* adresse mémoire donnée. Elle détermine si l'accès est un hit ou
* un miss, met à jour les statistiques en conséquence, et gère
* l'éviction de lignes de cache si nécessaire.
*
* Paramètres :
* - Cache *cache : Pointeur vers la structure de cache dans laquelle l'accès
* est effectué.
* - int adresse : Adresse mémoire à laquelle on souhaite accéder.
* - int associativite : Degré d'associativité de la mémoire cache.
* - int writeBack : Indicateur de la politique d'écriture (Write Back ou Write Through).
* - int writeAllocate : Indicateur de la stratégie d'allocation lors des écritures
* (Write Allocate ou No Write Allocate).
* - int numSets : Nombre d'ensembles dans la mémoire cache.
* - int accessType : Type d'accès (0 pour lecture, 1 pour écriture).
* - int* memoryAccesses : Pointeur vers un entier qui suit le nombre total
* d'accès mémoire effectués.
* - int* hitsLecture : Pointeur vers un entier qui suit le nombre de hits en lecture.
* - int* missesLecture : Pointeur vers un entier qui suit le nombre de misses en lecture.
* - int* hitsEcriture : Pointeur vers un entier qui suit le nombre de hits en écriture.
* - int* missesEcriture : Pointeur vers un entier qui suit le nombre de misses en écriture.
*
* Retour :
* - void : Cette fonction ne retourne rien, mais met à jour les statistiques de cache
* en fonction de l'accès effectué.
*/
void acceder_cache(Cache *cache, int adresse, int associativite, int writeBack, int writeAllocate, int numSets, int accessType, int* memoryAccesses, int* hitsLecture, int* missesLecture, int* hitsEcriture, int* missesEcriture)
{
int setIndex = adresse % numSets;
// Recherche de l'adresse dans le set correspondant
int found = 0;
for (int i = 0; i < associativite; i++) {
line *currentLine = &cache->sets[setIndex].lines[i];
if (currentLine->valid && currentLine->tag == (adresse / numSets)) {
found = 1;
if (accessType == 0 || accessType == 2) {
// C'est un hit en lecture
(*hitsLecture)++;
} else {
// C'est un hit en écriture
(*hitsEcriture)++;
if (writeBack) {
currentLine->dirty = 1;
}
}
// Marquer la ligne comme valide
currentLine->valid = 1;
}
}
if (!found) {
// C'est un miss, mettre à jour les statistiques
if (accessType == 0 || accessType == 2) {
// C'est un miss en lecture
(*missesLecture)++;
} else {
// C'est un miss en écriture
(*missesEcriture)++;
}
// Choisir aléatoirement une ligne pour l'éviction
int evictionIndex = rand() % associativite;
line *evictedLine = &cache->sets[setIndex].lines[evictionIndex];
// Si la ligne évincée est "dirty" et si la stratégie = write-back, alors écrire dans la mémoire centrale
if (writeBack && evictedLine->dirty) {
ecrire_ligne_en_memoire(evictedLine);
}
// Mettez à jour la ligne évincée avec la nouvelle adresse
evictedLine->tag = adresse / numSets;
evictedLine->valid = 1;
if (accessType == 1) {
evictedLine->dirty = 1;
}
(*memoryAccesses)++;
}
}
/*
* 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
}
}
void afficher_hits_miss(int nbTotalAcces, int nbAccesLecture, int nbAccesEcriture, int memoryAccesses, int hitsLecture, int hitsEcriture, int missesLecture, int missesEcriture, double cpu_time_used) {
// Calculs des pourcentages
float pourcentAccLecture = (double) nbAccesLecture / nbTotalAcces * 100;
float pourcentAccEcriture = (double) nbAccesEcriture / nbTotalAcces * 100;
int totalHits = hitsLecture + hitsEcriture;
float pourcentHits = (double) totalHits / nbTotalAcces * 100;
int totalMisses = missesLecture + missesEcriture;
float pourcentMisses = (double) totalMisses / nbTotalAcces * 100;
// Affichage avec bordures et 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, nbTotalAcces, COULEUR_BORDURE);
printf("----------------------------------------------------\n");
printf("| %sAccès en Lecture%s | %s%6d (%.2f%%)%s |\n", WHITE, COULEUR_BORDURE, WHITE, nbAccesLecture, pourcentAccLecture, COULEUR_BORDURE);
printf("| %sAccès en Écriture%s | %s%6d (%.2f%%)%s |\n", WHITE, COULEUR_BORDURE, WHITE, nbAccesEcriture, pourcentAccEcriture, COULEUR_BORDURE);
printf("----------------------------------------------------\n");
printf("| %sTotal Hits :%s | %s%6d (%.2f%%) %s|\n", MAGENTA, COULEUR_BORDURE, get_hits_color(pourcentHits), totalHits, pourcentHits, COULEUR_BORDURE);
printf("|%s - Hits en Lecture %s | %s%6d %s |\n", WHITE, COULEUR_BORDURE, get_hits_color(pourcentHits), hitsLecture, COULEUR_BORDURE);
printf("|%s - Hits en Écriture %s | %s%6d %s |\n", WHITE, COULEUR_BORDURE, get_hits_color(pourcentHits), hitsEcriture, COULEUR_BORDURE);
printf("----------------------------------------------------\n");
printf("| %sTotal Misses :%s | %s%6d (%.2f%%) %s|\n", MAGENTA, COULEUR_BORDURE, get_misses_color(pourcentMisses), totalMisses, pourcentMisses, COULEUR_BORDURE);
printf("|%s - Misses en Lecture%s | %s%6d %s|\n", WHITE, COULEUR_BORDURE, get_misses_color(pourcentMisses), missesLecture, COULEUR_BORDURE);
printf("|%s - Misses en Écriture%s | %s%6d %s|\n", WHITE, COULEUR_BORDURE, get_misses_color(pourcentMisses), missesEcriture, COULEUR_BORDURE);
printf("----------------------------------------------------\n");
printf("|%s Accès Mémoire%s | %s%6d %s|\n", WHITE, COULEUR_BORDURE, WHITE, memoryAccesses, COULEUR_BORDURE);
printf("----------------------------------------------------\n");
printf("| %sTemps total d'accès au cache : %.6f secondes%s |\n", MAGENTA, cpu_time_used, 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);
}
int main(int argc, char *argv[])
{
if (argc < 7 || argc > 9)
{
afficher_aide();
}
Cache cache;
// def Arguments
int tailleCache;
int tailleLigne;
int associativite;
int writeBack = 0;
int writeAllocate = 0;
// Suivi des statistiques
int nbTotalAcces = 0;
int nbAccesLecture = 0;
int nbAccesEcriture = 0;
int memoryAccesses = 0;
int hitsLecture = 0;
int hitsEcriture = 0;
int missesLecture = 0;
int missesEcriture = 0;
int numSets;
int accessType;
int adresse;
// Variables pour le temps
clock_t start_time, end_time;
double cpu_time_used;
// Arguments
int opt;
while ((opt = getopt(argc, argv, "s:l:a:Wch:")) != -1) {
switch (opt) {
case 's':
tailleCache = atoi(optarg);
break;
case 'l':
tailleLigne = atoi(optarg);
break;
case 'a':
associativite = atoi(optarg);
break;
case 'W':
writeAllocate = 1;
break;
case 'c':
writeBack = 1;
break;
case 'h':
afficher_aide();
break;
default:
printf("Usage: %s -s <cache_size> -l <line_size> -a <associativite> -[W] -[c] < fichier_trace\n", argv[0]);
exit(1);
}
}
numSets = tailleCache * 1024 / (tailleLigne * associativite);
// Initialiser le générateur aléatoire une seule fois
srand(time(NULL));
// Initialiser le cache
creer_struct_cache(&cache, numSets, associativite);
// Démarrer le chrono
start_time = clock();
// Boucle de simulation d'accès au cache
while (scanf("%d %x", &accessType, &adresse) != EOF) {
nbTotalAcces++;
if (accessType == 0 || accessType == 2) {
nbAccesLecture++;
} else {
nbAccesEcriture++;
}
acceder_cache(&cache, adresse, associativite, writeBack, writeAllocate, numSets, accessType, &memoryAccesses, &hitsLecture, &hitsEcriture, &missesLecture, &missesEcriture);
}
// Arrêter le chronomètre
end_time = clock();
cpu_time_used = ((double) (end_time - start_time)) / CLOCKS_PER_SEC; // Calcule le temps écoulé
// Afficher les statistiques du cache
afficher_hits_miss(nbTotalAcces, nbAccesLecture, nbAccesEcriture, memoryAccesses, hitsLecture, hitsEcriture, missesLecture, missesEcriture, cpu_time_used);
// Libérer la mémoire allouée
for (int i = 0; i < numSets; i++) {
free(cache.sets[i].lines);
}
free(cache.sets);
return 0;
}