Nantes Université

Skip to content
Extraits de code Groupes Projets
Valider d40d73b8 rédigé par Sebastien FAUCOU's avatar Sebastien FAUCOU
Parcourir les fichiers

ajout sujet tp5 (mémoire virtuelle).

parent 567a180b
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
# Introduction
## Appels systèmes et fonctions utiles
Deux appels systèmes permettent de manipuler directement la table des
pages d'un processus :
- `mmap` : ajoute une ou plusieurs pages à l'espace d'adressage
virtuelle d'un processus. Les pages ajoutées peuvent être
initialisées à partir d'un fichier (p.ex. pour lire depuis/écrire
vers ce fichier), ou être anonyme (pages « vides », mises à 0). Ces
pages peuvent être marquées privées (*copy on write*), ou partagées.
Une page partagée reste commune au processus parent et ses éventuels
enfants **même en cas de modification de son contenu**. Lors de
l'appel à `mmap`, la taille de la zone doit être connue et ne peut
ensuite plus être modifiée.
- `munmap` : efface une ou plusieurs entrées de la table des pages
d'un processus.
# Travail à réaliser
## *Memory-mapped-ios*
En partant du fichier suivant, comparer les performances des
entrées-sorties utilisant des appels systèmes (ici `read` et `write`),
et des entrées-sorties exploitant directement le système de mémoire
virtuelle.
Pour cela, vous devez dans un premier temps compléter le fichier source
ci-dessous en donnant le code de la fonction `copy_with_mmap`. Cette
fonction doit copier le contenu du fichier `in.bin` vers le fichier
`out.bin`. Pour cela, elle doit créer des pages associées à ces fichiers
dans son espace de mémoire virtuel, puis utiliser la fonction standard
`memcpy` pour effectuer la copie. Enfin, les pages doivent être
supprimées de l'espace virtuel et les fichiers sources et destinations
doivent être fermées. Pour connaître la taille du fichier à copier, et
ainsi déterminer le nombre de page à créer, il est possible d'utiliser
l'appel système `fstat`.
Une fois que la fonction est codée, vous pourrez comparez les
performances des deux approches pour différentes tailles de fichier
source. Pour allouer rapidement un fichier d'une taille donnée, vous
pouvez utiliser la commande `fallocate` comme ci-dessous (dans
l'exemple, un fichier de 16MiB est alloué). À noter qu'il faut d'abord
effacer le fichier pour que la taille demandée soit effectivement prise
en considération.
``` bash
rm in.txt
fallocate -l $((16 * 1024 * 1024)) in.txt
```
Si la commande `fallocate` n'est pas disponible, vous pouvez utiliser la
commande `dd` qui est un peu plus lente :
``` bash
dd if=/dev/urandom of=in.txt bs=1024 count=$$((16 * 1024))
```
Enfin, un dernier point important : pour que vos tests soient
pertinents, sur les machines du parc pédagogique de l'IUT, assurez vous
que les fichiers sources et destinations se situent sur un dossier local
de la machine. Dans le cas contraire, les performances seront dominées
par les latences du réseau et ne permettront pas d'observer la
différence entre les deux techniques d'entrées-sorties.
``` c
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/**
* copy the file using systems io
*/
void copy_with_read()
{
int src = open("in.bin", O_RDONLY, S_IRUSR|S_IWUSR);
int dst = open("out.bin", O_WRONLY|O_TRUNC|O_CREAT, S_IRUSR|S_IWUSR);
char buff[4096];
ssize_t nread;
while((nread = read(src, buff, 4096)) > 0)
{
write(dst, buff, nread);
}
close(src);
close(dst);
}
/**
* copy the file without transfer to user space
*/
void copy_with_mmap()
{
// TODO
return;
}
int main(void)
{
clock_t begin, end;
const int LOOPS = 100;
begin = clock();
for(int i=0; i<LOOPS; i++)
{
copy_with_read();
}
end = clock();
fprintf(stdout, "with std io: %e\n", (double)(end-begin) / CLOCKS_PER_SEC);
begin = clock();
for(int i=0; i<LOOPS; i++)
{
copy_with_mmap();
}
end = clock();
fprintf(stdout, "with mapped io: %e\n", (double)(end-begin) / CLOCKS_PER_SEC);
fprintf(stdout, "done.\n");
exit(0);
}
```
## Calcul parallèle et mémoire partagée
L'ensemble de Mandelbrot est le sous-ensembe des points *c* du plan
complexe pour lesquels la suite *z* converge, où *z(i)* est défini par :
- *z(0) = 0 + i0*
- *z(n+1) = z(n)² + c*
C'est un ensemble fractal, c'est-à-dire que sa structure est invariante
par changement d'échelle. Sa définition a été formulée par Adrien
Douadi, qui l'a nommé ainsi en hommage à Benoît Mandelbrot, l'un des
pionniers de la théorie des fractals.
Un exercice classique en algorithmique consiste à calculer et visualiser
une approximation discrète de cet ensemble. Étant donnée une matrice
dont les cellules sont associées à des pixels, il s'agit de calculer si
le pixel appartient ou non à l'ensemble. Pour cela, on associe à chaque
pixel les coordonnées d'un des points du plan qu'il représente, et on
applique un algorithme d'approximation pour déterminer si ce point
appartient ou non à l'ensemble. Cet algorithme est paramétré par une
précision *p*. Il consiste, étant donné un point *c*, à calculer la
suite des valeurs *z(n)* jusqu'à *n=p*, ou jusqu'à ce que *|z(n)| \> 2*
(car on sait qu'alors la suite *z* diverge). Le pixel est marqué comme
appartenant à l'ensemble si *z(p)* est inférieur ou égal à 2. Pour les
points n'appartenant pas à l'ensemble, il est possible de choisir une
couleur en fonction de l'itération *k* à laquelle *z(k)* devient
supérieur à 2.
Cet algorithme est implanté dans le fichier source ci-dessous. Votre
travail consiste à paralléliser ce calcul, c'est-à-dire à répartir le
travail entre plusieurs processus. Sur une machine disposant de
plusieurs processeurs, cela permet d'effectuer le calcul plus
rapidement.
Pour cela, suivre les étapes suivantes :
1. Étudier le code existant.
2. Faire en sorte que la variable `set` soit accessible depuis
plusieurs processus.
3. Créer plusieurs processus et leur répartir le travail. Pour
l'exercice, répartir le travail entre quatre processus enfants, le
processus parent ne s'occupant que de l'affichage.
4. Faire en sorte que l'affichage de l'image ne se fasse qu'une fois
qu'on est certain que le calcul est terminé.
Le programme affiche les ressources consommées. Selon la valeur de *p*
et le nombre de cœur de calcul de la machine, la version parallèle est
plus rapide ou plus lente que la version séquentielle. Expliquer
pourquoi.
``` c
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
const int WIDTH = 5400; /* x resolution in pixels */
const int HEIGHT = 3600; /* y resolution in pixels, y = (2/3)x */
const unsigned short PRECISION = 1000;
/* distance between two pixels */
double deltax;
double deltay;
/* viewport */
const double xmin = -2.0;
const double xmax = 1.0;
const double ymin = -1.0 ;
const double ymax = 1.0;
unsigned short * set;
time_t stamp;
void compute_lines(int start, int end)
{
/* loop counters */
int i, j, iteration;
for (i=start; i<end; i++)
{
double yc = ymin + i*deltay;
for (j=0; j<WIDTH; j++)
{
double xc = xmin + j*deltax;
double x = 0.0;
double y = 0.0;
iteration = 0;
while((iteration < PRECISION) && ((x*x + y*y) <= 4.0))
{
iteration++;
double newx = x*x - y*y + xc;
double newy = 2.0*x*y + yc;
x = newx;
y = newy;
}
set[i*WIDTH+j] = iteration;
}
}
}
void save_to_file (void)
{
FILE *image;
unsigned short buf[3];
const unsigned short COLOR_COEFFICIENT = USHRT_MAX / PRECISION;
unsigned short color;
unsigned short iteration;
image = fopen("output.ppm", "wb");
if (image == NULL)
{
fprintf(stderr, "Cannot open output file: %s\n", strerror(errno));
exit(3);
}
fprintf(image, "P6\n"); // binary
fprintf(image, "%d %d\n", WIDTH, HEIGHT); // image dimension
fprintf(image, "%u\n",USHRT_MAX); // color depth: 32 bits
for(int i=0; i<HEIGHT; i++)
{
for(int j=0; j<WIDTH; j++)
{
iteration = set[i*WIDTH+j];
if (iteration == PRECISION) color = USHRT_MAX;
else color = iteration * COLOR_COEFFICIENT;
buf[0] = color;
buf[1] = color;
buf[2] = color;
fwrite(buf, sizeof(short), 3, image) ;
}
}
fclose(image);
}
int main ()
{
struct rusage stats;
deltax = (xmax - xmin)/(double)WIDTH;
deltay = (ymax - ymin)/(double)HEIGHT;
set = (unsigned short*)malloc(WIDTH*HEIGHT*sizeof(short));
compute_lines(0, HEIGHT);
getrusage(RUSAGE_SELF, &stats);
printf("user time: %ld:%.6ld\n", stats.ru_utime.tv_sec, stats.ru_utime.tv_usec);
printf("system time: %ld:%.6ld\n", stats.ru_stime.tv_sec, stats.ru_stime.tv_usec);
save_to_file();
return (0);
}
```
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter