Bonjour,

j'ai développé, en reprenant un programme fait pour tourner sur 2 coeurs, une petite apllication du calcul de pi en essayant de le généraliser à un nombre quelconque de threads (à condition d'avoir le rapport (nombre d'itérations/nombre de thread) entier ).

Le code original (pour 2 coeurs) est le suivant :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
 
/* parallel computation of pi with monte carlo method */ 
#include <stdio.h> 
#include <stdlib.h>
#include <unistd.h> 
#include <fcntl.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <sys/mman.h> 
#include <sys/wait.h> 
#include <sys/time.h> 
#define SEED 35791246 
 
struct sharedMem { double child_p_in; };
 
 
 int main (int argc, char** argv) 
 
 { double parent_p_in; // number of hits inside the circle 
   double niter; // total number of hits 
   int fd; int i; 
   double x,y,z,pi;
   struct sharedMem* smem; 
   struct timeval chrono1, chrono2; 
   int micro, second; pid_t pid; 
 
  // parse the second argument 
  if (argc != 2)
   { printf ("Please, specify interval number\n"); 
     exit (1); } 
     niter = (double) atoi(argv[1]); 
 
  // create the shared memory 
  fd = open ("/tmp/myregion", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
   if (fd == -1) exit(2);
 
    if (ftruncate (fd, sizeof (struct sharedMem)) == -1) exit(3); 
 
    smem = mmap (NULL, sizeof (struct sharedMem),
              PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
     if (smem == MAP_FAILED) exit(1); 
 
 
     srand(SEED); 
 
     // initialize random numbers 
     gettimeofday(&chrono1, NULL);
 
     // monte carlo's method in parallel 
 
     pid = fork(); 
     if (pid < 0) exit(4); 
     else if (pid == 0) { 
      // child process
      // compute the first half of niter hits 
 
      smem->child_p_in = 0; 
 
      for (i=0; i<(niter/2); i++) 
      { x = (double) rand() / RAND_MAX; 
      y = (double) rand() / RAND_MAX; 
      z = x*x + y*y; 
      if (z <= 1.0) 
       smem->child_p_in++; } 
 
       exit(0); }
       else { 
 
       // parent process // compute the second half of niter hits 
       parent_p_in = 0; 
       for (i=(niter/2); i<niter; i++) 
       { x = (double) rand() / RAND_MAX;
        y = (double) rand() / RAND_MAX; 
	z = x*x + y*y; 
	if (z <= 1.0) parent_p_in++; } 
 
 
	wait(NULL); 
	// wait for child 
 
	}
 
	gettimeofday(&chrono2, NULL); 
//	munmap(smem, sizeof(struct sharedMem)); 
	close(fd); 
	// compute ellapsed time 
	micro = chrono2.tv_usec - chrono1.tv_usec; 
	if (micro < 0) 
	{ micro += 1000000; 
	second = chrono2.tv_sec - chrono1.tv_sec - 1; } 
	else 
	second = chrono2.tv_sec - chrono1.tv_sec;
 
	pi = (parent_p_in + smem->child_p_in)/niter; 
        pi *= 4; 
 
	// special format fot limted niter parameter < 10e9
 
	printf("# of trials= %d , estimate of pi is %f \n",(int) niter,pi);
	printf("%d second %d micro ellapsed\n", second, micro);
 
  return 0;
 
   }
On se sert du fork() pour que chaque thread génère (n_iterations/2) nombres aléatoires.
A l'exécution, ce programme tourne effectivement sur 2 coeurs.

Pour le généraliser, j'ai codé une fonction récursive qui par exemple, pour un nombre de thread "nb_thread" égale à 4, va utiliser 2 fork(). Je récupère le nombre de thread et le nombre d'itérations en paramètres de l'exécutable.

Voici la procédure récursive :


Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
 
/* parallel computation of pi with monte carlo method */ 
#include <stdio.h> 
#include <stdlib.h>
#include <unistd.h> 
#include <fcntl.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <sys/mman.h> 
#include <sys/wait.h> 
#include <sys/time.h> 
#define SEED 35791246 
 
struct sharedMem { double j[100]; };
 
 
struct sharedMem* Parallel( struct sharedMem* smem, pid_t* pid, int nbt, long int niter, int statut, int options, int nb_current )
{
  double x,y,z;
  long int i;
     if (nbt==1)  
               { smem->j[nb_current]=0;
                 for (i=0; i<(niter/nbt); i++) 
                      { x = (double) rand() / RAND_MAX; 
                        y = (double) rand() / RAND_MAX; 
                        z = x*x + y*y;
                        if (z <= 1.0) 
                 {smem->j[nb_current]++;}}
		 return smem;
		 }
 else if (nb_current==(nbt-1)) return smem;
     else {  pid[nb_current]=fork();          
     	     if (pid[nb_current] < 0) exit(4); 
                else if (pid[nb_current]==0) 
		    {// child process
                      // compute the niter/nbt part of niter hits 
                      smem->j[nb_current]=0;
                    for (i=0; i<(niter/nbt); i++) 
                      { x = (double) rand() / RAND_MAX; 
                        y = (double) rand() / RAND_MAX; 
                        z = x*x + y*y;
                        if (z <= 1.0) 
                         {smem->j[nb_current]++;}}
			 exit(0); 
		      }
		    else {
		      // parent process 
		      // compute the niter/nbt part of niter hits 
                      smem->j[nb_current+1] = 0;		       		      
                      for (i=0; i<(niter/nbt); i++) 
                      { x = (double) rand() / RAND_MAX;
                        y = (double) rand() / RAND_MAX; 
	                z = x*x + y*y; 
	                if (z <= 1.0) smem->j[nb_current+1]++; }
                        waitpid(pid[nb_current],&statut,options);			
         	        return Parallel(smem,pid,nbt,niter,statut,options,nb_current+1);   	 
                      }
		   exit(0);   
 
	  }
 
 }  
 
.....
Pour créer la mémoire partagée, j'utilise comme pour l'algo original :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
 
 // create the shared memory 
  fd = open ("/tmp/myregion", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
   if (fd == -1) exit(2);
 
    if (ftruncate (fd, sizeof (struct sharedMem)) == -1) exit(3); 
 
    smem = mmap (NULL, sizeof (struct sharedMem),
              PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
     if (smem == MAP_FAILED) exit(1);
et avec l'appel de la fonction récursive :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
 
 // recursive function 
 
     smem=Parallel(smem,pid,nb_thread,niter,statut,options,nb_current);

Mais voilà, le problème est qu'à l'exécution, pour un nombre de processus supérieur à 2 et inférieur ou égal à 8 (j'ai un processeur 8 cores), le programme ne tourne pas sur le nombre de cores correspondant ( vérifié grâce à la commande "htop" ou "ps aux | grep a.out" ) :

Pour nb_thread=4, j'aimerais qu'il tourne sur 4, pour 6, sur 6 ...

Il donne bien le résultat attendu, à savoir une estimation de pi, d'autant plus précise que le nombre d'itérations est grand,

Mais pourquoi ne tourne t-il pas sur le nombre de coeurs attendu comme j'aimerais (du moins pour nb_thread compris entre 4 et 8) ?

Est-ce que ceci est du au fait que je fait le fork() dans une fonction et non dans le "main" ?

Si quelqu'un pouvait m'aider à voir plus clair,

Merci d'avance.