414 lines
17 KiB
C
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;
|
|
}
|